Post Game jam commit

This commit is contained in:
Daniel Kauss Serna 2026-02-03 21:06:49 +01:00
commit 6db2131520
164 changed files with 172524 additions and 0 deletions

4
.editorconfig Normal file
View file

@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

18
.gitignore vendored Normal file
View file

@ -0,0 +1,18 @@
# Godot 4+ specific ignores
.godot/
.nomedia
# Godot-specific ignores
.import/
export.cfg
export_credentials.cfg
*.tmp
# Imported translations (automatically generated from CSV files)
*.translation
# Mono-specific ignores
.mono/
data_*/
mono_crash.*.json

BIN
assets/fonts/ByteBounce.ttf Normal file

Binary file not shown.

View file

@ -0,0 +1,36 @@
[remap]
importer="font_data_dynamic"
type="FontFile"
uid="uid://ctv72j20ly3an"
path="res://.godot/imported/ByteBounce.ttf-a21898d48b43b4e74178b034355eb9b6.fontdata"
[deps]
source_file="res://assets/fonts/ByteBounce.ttf"
dest_files=["res://.godot/imported/ByteBounce.ttf-a21898d48b43b4e74178b034355eb9b6.fontdata"]
[params]
Rendering=null
antialiasing=1
generate_mipmaps=false
disable_embedded_bitmaps=true
multichannel_signed_distance_field=false
msdf_pixel_range=8
msdf_size=48
allow_system_fallback=true
force_autohinter=false
modulate_color_glyphs=false
hinting=1
subpixel_positioning=4
keep_rounding_remainders=true
oversampling=0.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
preload=[]
language_support={}
script_support={}
opentype_features={}

BIN
assets/music/title.wav Normal file

Binary file not shown.

View file

@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://cgbyt2t17021h"
path="res://.godot/imported/title.wav-01bfab1a13469a53d2678f216bb06949.sample"
[deps]
source_file="res://assets/music/title.wav"
dest_files=["res://.godot/imported/title.wav-01bfab1a13469a53d2678f216bb06949.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=2
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

Binary file not shown.

BIN
assets/scithe.aseprite Normal file

Binary file not shown.

BIN
assets/sfx/click.mp3 Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://bol5fdgkgkatg"
path="res://.godot/imported/click.mp3-0625a102c47e756510f31025be055e4b.mp3str"
[deps]
source_file="res://assets/sfx/click.mp3"
dest_files=["res://.godot/imported/click.mp3-0625a102c47e756510f31025be055e4b.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/sfx/death.mp3 Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://dksf3fo4qgnq0"
path="res://.godot/imported/death.mp3-44291ed827785208d0566bebb7169166.mp3str"
[deps]
source_file="res://assets/sfx/death.mp3"
dest_files=["res://.godot/imported/death.mp3-44291ed827785208d0566bebb7169166.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/sfx/heart.mp3 Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://daoupl7nh7wwj"
path="res://.godot/imported/heart.mp3-843267af0a49b39c4120b2b105d6d170.mp3str"
[deps]
source_file="res://assets/sfx/heart.mp3"
dest_files=["res://.godot/imported/heart.mp3-843267af0a49b39c4120b2b105d6d170.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/sfx/swing.mp3 Normal file

Binary file not shown.

View file

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://diwr7vjwsqgl2"
path="res://.godot/imported/swing.mp3-693cef8e724588bb56b5aaa958129a04.mp3str"
[deps]
source_file="res://assets/sfx/swing.mp3"
dest_files=["res://.godot/imported/swing.mp3-693cef8e724588bb56b5aaa958129a04.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
assets/tilemap.aseprite Normal file

Binary file not shown.

Binary file not shown.

17
click.gd Normal file
View file

@ -0,0 +1,17 @@
extends BaseButton
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
button_down.connect(down)
print("button")
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func down() -> void:
print("clikced!!")
SoundManager.play_sfx("click")

1
click.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://llka3aa044jo

15
credits.gd Normal file
View file

@ -0,0 +1,15 @@
extends Node2D
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel"):
get_tree().quit()

1
credits.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://b178407fwlyvs

147
credits.tscn Normal file
View file

@ -0,0 +1,147 @@
[gd_scene load_steps=9 format=3 uid="uid://b8f6fhfp1pb58"]
[ext_resource type="Script" uid="uid://b178407fwlyvs" path="res://credits.gd" id="1_5em78"]
[ext_resource type="Texture2D" uid="uid://d3abeekjumqyb" path="res://assets/maske1.png" id="1_nm7lk"]
[ext_resource type="Theme" uid="uid://d3iyu7ukwsn1p" path="res://ui/default_theme.tres" id="2_c1t8d"]
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="2_g4wvt"]
[ext_resource type="Script" uid="uid://bhgj68nop1igc" path="res://floattt.gd" id="2_pjc1l"]
[ext_resource type="Texture2D" uid="uid://dgd8u7jrpy1v5" path="res://assets/maske2.png" id="3_4av4j"]
[ext_resource type="Texture2D" uid="uid://d3h6wl5cnf8ba" path="res://assets/maske3.png" id="4_5em78"]
[sub_resource type="SpriteFrames" id="SpriteFrames_h30m1"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("1_nm7lk")
}, {
"duration": 1.0,
"texture": ExtResource("3_4av4j")
}, {
"duration": 1.0,
"texture": ExtResource("4_5em78")
}],
"loop": true,
"name": &"default",
"speed": 5.0
}]
[node name="Node2D" type="Node2D"]
script = ExtResource("1_5em78")
[node name="ColorRect" type="ColorRect" parent="."]
z_index = -10
offset_left = -685.0
offset_top = -704.0
offset_right = -645.0
offset_bottom = -664.0
scale = Vector2(36.570084, 36.570084)
color = Color(0.08235294, 0.09803922, 0.89411765, 1)
[node name="Shadow" type="Node2D" parent="."]
z_index = -1
position = Vector2(1, 99)
script = ExtResource("2_g4wvt")
metadata/_custom_type_script = "uid://synocbtvgrf4"
[node name="Maske1" type="AnimatedSprite2D" parent="."]
position = Vector2(0, 65)
sprite_frames = SubResource("SpriteFrames_h30m1")
autoplay = "default"
script = ExtResource("2_pjc1l")
float_offset = 65.0
[node name="Camera2D" type="Camera2D" parent="."]
zoom = Vector2(2, 2)
[node name="Label" type="Label" parent="."]
offset_left = -54.0
offset_top = 114.0
offset_right = 61.0
offset_bottom = 139.0
theme = ExtResource("2_c1t8d")
text = "Daniel Kauss
Claudia Francisco"
[node name="Label2" type="Label" parent="."]
offset_left = -277.0
offset_top = -151.0
offset_right = -122.0
offset_bottom = 70.0
theme = ExtResource("2_c1t8d")
text = "ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR"
[node name="Label3" type="Label" parent="."]
offset_left = -115.0
offset_top = -151.0
offset_right = 40.0
offset_bottom = 70.0
theme = ExtResource("2_c1t8d")
text = "ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR"
[node name="Label4" type="Label" parent="."]
offset_left = 43.0
offset_top = -151.0
offset_right = 198.0
offset_bottom = 70.0
theme = ExtResource("2_c1t8d")
text = "ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR"
[node name="Label5" type="Label" parent="."]
offset_left = 204.0
offset_top = -151.0
offset_right = 359.0
offset_bottom = 70.0
theme = ExtResource("2_c1t8d")
text = "ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR
ERROR ERROR ERROR ERROR"

20
default_bus_layout.tres Normal file
View file

@ -0,0 +1,20 @@
[gd_resource type="AudioBusLayout" load_steps=2 format=3 uid="uid://u82hlijupp4q"]
[sub_resource type="AudioEffectCompressor" id="AudioEffectCompressor_j3pel"]
resource_name = "Compressor"
[resource]
bus/1/name = &"Music"
bus/1/solo = false
bus/1/mute = false
bus/1/bypass_fx = false
bus/1/volume_db = 0.0
bus/1/send = &"Master"
bus/2/name = &"SFX"
bus/2/solo = false
bus/2/mute = false
bus/2/bypass_fx = false
bus/2/volume_db = -17.657278
bus/2/send = &"Master"
bus/2/effect/0/effect = SubResource("AudioEffectCompressor_j3pel")
bus/2/effect/0/enabled = true

142
enemy.gd Normal file
View file

@ -0,0 +1,142 @@
class_name Enemy extends CharacterBody2D
@export_group("Combat Scenes")
@export var mask_drop : PackedScene
@export var death_explosion : PackedScene
@export var heart_attack : PackedScene
@export var scithe_attack : PackedScene
@export var spit_attack : PackedScene
var charge : float = 0.0
@export var charge_time : float = 0.7
@export var flee_range = 20
@export var aproach_range = 100
@export var health := 3
@export var current_mask : Types.mask_types
@export var movement_speed: float = 30.0
@onready var navigation_agent: NavigationAgent2D = $NavigationAgent2D
@onready var movement_target_position: Vector2 = global_position
@onready var sprite : AnimatedSprite2D = $Anim
@onready var player = get_tree().get_first_node_in_group("player")
func _ready():
navigation_agent.path_desired_distance = 4.0
navigation_agent.target_desired_distance = 4.0
navigation_agent.velocity_computed.connect(_on_velocity_computed)
call_deferred("_actor_setup")
func _actor_setup():
await get_tree().physics_frame
set_movement_target(movement_target_position)
func set_movement_target(movement_target: Vector2):
navigation_agent.target_position = movement_target
func _drop_mask():
var drop : MaskDrop = mask_drop.instantiate()
drop.mask_type = current_mask
get_parent().add_child(drop)
drop.global_position = global_position
func die():
EventBus.screenshake.emit(10)
var tween = create_tween()
var explo = death_explosion.instantiate()
get_parent().add_child(explo)
explo.global_position = global_position
explo.emitting = true
tween.tween_property(self, "modulate:a", 0, 0.2)
tween.tween_callback(_drop_mask)
tween.tween_callback(queue_free)
func launch_atk():
var atk = null
var target = navigation_agent.target_position
if (current_mask == Types.mask_types.Melee):
atk = scithe_attack.instantiate()
add_child(atk)
atk.look_at(target)
atk.modulate = Color.BLACK
elif (current_mask == Types.mask_types.Ranged):
atk = heart_attack.instantiate()
atk.global_position = global_position
atk.look_at(target)
atk.modulate = Color.BLACK
get_parent().add_child(atk)
elif (current_mask == Types.mask_types.Spit):
atk = spit_attack.instantiate()
atk.global_position = global_position
atk.start_position = global_position
get_parent().add_child(atk)
atk.target_position = global_position + global_position.direction_to(target) * 150
atk.set_from_player(false)
atk.global_position = atk.global_position.move_toward(target, 10 )
func _process(_delta: float) -> void:
queue_redraw()
if player:
set_movement_target(player.global_position)
if not current_mask == 2:
scale = Vector2.ONE * (1 + charge)
if (charge > charge_time):
launch_atk()
charge = 0
func hit():
health -= 1
SoundManager.play_sfx("death")
#charge = 0
if health == 0:
die()
EventBus.screenshake.emit(0.2)
var tween = create_tween()
tween.tween_property(self, "modulate", Color.CRIMSON, 0.1)
tween.tween_property(self, "modulate", Color.WHITE, 0.3)
func knockback():
EventBus.debug_print.emit("knocking back called!")
velocity += global_position.direction_to(navigation_agent.target_position).normalized() * -2000
func _dir_to_target():
if navigation_agent.is_navigation_finished():
return Vector2.ZERO
var next_path_position: Vector2 = navigation_agent.get_next_path_position()
return next_path_position
func _physics_process(delta : float):
var target_dist = navigation_agent.target_position.distance_to(global_position)
#if (target_dist > 300):
#charge = 0
if (target_dist > aproach_range):
velocity += global_position.direction_to(_dir_to_target()) * movement_speed
sprite.flip_h = velocity.x > 0
charge = 0
elif (target_dist < flee_range):
velocity -= global_position.direction_to(_dir_to_target()) * movement_speed
sprite.flip_h = velocity.x > 0
charge = 0
else:
charge += delta
if player:
sprite.flip_h = player.global_position.x > global_position.x
velocity *= 0.6
navigation_agent.set_velocity(velocity)
move_and_slide()
func _on_velocity_computed(safe_velocity: Vector2):
velocity = safe_velocity
move_and_slide()

1
enemy.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://b1t0k6dfubsmk

View file

@ -0,0 +1,66 @@
/**************************************************************************/
/* godot.audio.position.worklet.js */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
class GodotPositionReportingProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [
{
name: 'reset',
defaultValue: 0,
minValue: 0,
maxValue: 1,
automationRate: 'k-rate',
},
];
}
constructor(...args) {
super(...args);
this.position = 0;
}
process(inputs, _outputs, parameters) {
if (parameters['reset'][0] > 0) {
this.position = 0;
}
if (inputs.length > 0) {
const input = inputs[0];
if (input.length > 0) {
this.position += input[0].length;
this.port.postMessage({ type: 'position', data: this.position });
}
}
return true;
}
}
registerProcessor('godot-position-reporting-processor', GodotPositionReportingProcessor);

View file

@ -0,0 +1,213 @@
/**************************************************************************/
/* audio.worklet.js */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
class RingBuffer {
constructor(p_buffer, p_state, p_threads) {
this.buffer = p_buffer;
this.avail = p_state;
this.threads = p_threads;
this.rpos = 0;
this.wpos = 0;
}
data_left() {
return this.threads ? Atomics.load(this.avail, 0) : this.avail;
}
space_left() {
return this.buffer.length - this.data_left();
}
read(output) {
const size = this.buffer.length;
let from = 0;
let to_write = output.length;
if (this.rpos + to_write > size) {
const high = size - this.rpos;
output.set(this.buffer.subarray(this.rpos, size));
from = high;
to_write -= high;
this.rpos = 0;
}
if (to_write) {
output.set(this.buffer.subarray(this.rpos, this.rpos + to_write), from);
}
this.rpos += to_write;
if (this.threads) {
Atomics.add(this.avail, 0, -output.length);
Atomics.notify(this.avail, 0);
} else {
this.avail -= output.length;
}
}
write(p_buffer) {
const to_write = p_buffer.length;
const mw = this.buffer.length - this.wpos;
if (mw >= to_write) {
this.buffer.set(p_buffer, this.wpos);
this.wpos += to_write;
if (mw === to_write) {
this.wpos = 0;
}
} else {
const high = p_buffer.subarray(0, mw);
const low = p_buffer.subarray(mw);
this.buffer.set(high, this.wpos);
this.buffer.set(low);
this.wpos = low.length;
}
if (this.threads) {
Atomics.add(this.avail, 0, to_write);
Atomics.notify(this.avail, 0);
} else {
this.avail += to_write;
}
}
}
class GodotProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.threads = false;
this.running = true;
this.lock = null;
this.notifier = null;
this.output = null;
this.output_buffer = new Float32Array();
this.input = null;
this.input_buffer = new Float32Array();
this.port.onmessage = (event) => {
const cmd = event.data['cmd'];
const data = event.data['data'];
this.parse_message(cmd, data);
};
}
process_notify() {
if (this.notifier) {
Atomics.add(this.notifier, 0, 1);
Atomics.notify(this.notifier, 0);
}
}
parse_message(p_cmd, p_data) {
if (p_cmd === 'start' && p_data) {
const state = p_data[0];
let idx = 0;
this.threads = true;
this.lock = state.subarray(idx, ++idx);
this.notifier = state.subarray(idx, ++idx);
const avail_in = state.subarray(idx, ++idx);
const avail_out = state.subarray(idx, ++idx);
this.input = new RingBuffer(p_data[1], avail_in, true);
this.output = new RingBuffer(p_data[2], avail_out, true);
} else if (p_cmd === 'stop') {
this.running = false;
this.output = null;
this.input = null;
this.lock = null;
this.notifier = null;
} else if (p_cmd === 'start_nothreads') {
this.output = new RingBuffer(p_data[0], p_data[0].length, false);
} else if (p_cmd === 'chunk') {
this.output.write(p_data);
}
}
static array_has_data(arr) {
return arr.length && arr[0].length && arr[0][0].length;
}
process(inputs, outputs, parameters) {
if (!this.running) {
return false; // Stop processing.
}
if (this.output === null) {
return true; // Not ready yet, keep processing.
}
const process_input = GodotProcessor.array_has_data(inputs);
if (process_input) {
const input = inputs[0];
const chunk = input[0].length * input.length;
if (this.input_buffer.length !== chunk) {
this.input_buffer = new Float32Array(chunk);
}
if (!this.threads) {
GodotProcessor.write_input(this.input_buffer, input);
this.port.postMessage({ 'cmd': 'input', 'data': this.input_buffer });
} else if (this.input.space_left() >= chunk) {
GodotProcessor.write_input(this.input_buffer, input);
this.input.write(this.input_buffer);
} else {
// this.port.postMessage('Input buffer is full! Skipping input frame.'); // Uncomment this line to debug input buffer.
}
}
const process_output = GodotProcessor.array_has_data(outputs);
if (process_output) {
const output = outputs[0];
const chunk = output[0].length * output.length;
if (this.output_buffer.length !== chunk) {
this.output_buffer = new Float32Array(chunk);
}
if (this.output.data_left() >= chunk) {
this.output.read(this.output_buffer);
GodotProcessor.write_output(output, this.output_buffer);
if (!this.threads) {
this.port.postMessage({ 'cmd': 'read', 'data': chunk });
}
} else {
// this.port.postMessage('Output buffer has not enough frames! Skipping output frame.'); // Uncomment this line to debug output buffer.
}
}
this.process_notify();
return true;
}
static write_output(dest, source) {
const channels = dest.length;
for (let ch = 0; ch < channels; ch++) {
for (let sample = 0; sample < dest[ch].length; sample++) {
dest[ch][sample] = source[sample * channels + ch];
}
}
}
static write_input(dest, source) {
const channels = source.length;
for (let ch = 0; ch < channels; ch++) {
for (let sample = 0; sample < source[ch].length; sample++) {
dest[sample * channels + ch] = source[ch][sample];
}
}
}
}
registerProcessor('godot-processor', GodotProcessor);

220
export/index.html Normal file
View file

@ -0,0 +1,220 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0">
<title>Netmasked</title>
<style>
html, body, #canvas {
margin: 0;
padding: 0;
border: 0;
}
body {
color: white;
background-color: black;
overflow: hidden;
touch-action: none;
}
#canvas {
display: block;
}
#canvas:focus {
outline: none;
}
#status, #status-splash, #status-progress {
position: absolute;
left: 0;
right: 0;
}
#status, #status-splash {
top: 0;
bottom: 0;
}
#status {
background-color: #242424;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
visibility: hidden;
}
#status-splash {
max-height: 100%;
max-width: 100%;
margin: auto;
}
#status-splash.show-image--false {
display: none;
}
#status-splash.fullsize--true {
height: 100%;
width: 100%;
object-fit: contain;
}
#status-splash.use-filter--false {
image-rendering: pixelated;
}
#status-progress, #status-notice {
display: none;
}
#status-progress {
bottom: 10%;
width: 50%;
margin: 0 auto;
}
#status-notice {
background-color: #5b3943;
border-radius: 0.5rem;
border: 1px solid #9b3943;
color: #e0e0e0;
font-family: 'Noto Sans', 'Droid Sans', Arial, sans-serif;
line-height: 1.3;
margin: 0 2rem;
overflow: hidden;
padding: 1rem;
text-align: center;
z-index: 1;
}
</style>
<link id="-gd-engine-icon" rel="icon" type="image/png" href="index.icon.png" />
<link rel="apple-touch-icon" href="index.apple-touch-icon.png"/>
</head>
<body>
<canvas id="canvas">
Your browser does not support the canvas tag.
</canvas>
<noscript>
Your browser does not support JavaScript.
</noscript>
<div id="status">
<img id="status-splash" class="show-image--true fullsize--true use-filter--true" src="index.png" alt="">
<progress id="status-progress"></progress>
<div id="status-notice"></div>
</div>
<script src="index.js"></script>
<script>
const GODOT_CONFIG = {"args":[],"canvasResizePolicy":2,"emscriptenPoolSize":8,"ensureCrossOriginIsolationHeaders":true,"executable":"index","experimentalVK":false,"fileSizes":{"index.pck":5768316,"index.wasm":38034280},"focusCanvas":true,"gdextensionLibs":[],"godotPoolSize":4};
const GODOT_THREADS_ENABLED = false;
const engine = new Engine(GODOT_CONFIG);
(function () {
const statusOverlay = document.getElementById('status');
const statusProgress = document.getElementById('status-progress');
const statusNotice = document.getElementById('status-notice');
let initializing = true;
let statusMode = '';
function setStatusMode(mode) {
if (statusMode === mode || !initializing) {
return;
}
if (mode === 'hidden') {
statusOverlay.remove();
initializing = false;
return;
}
statusOverlay.style.visibility = 'visible';
statusProgress.style.display = mode === 'progress' ? 'block' : 'none';
statusNotice.style.display = mode === 'notice' ? 'block' : 'none';
statusMode = mode;
}
function setStatusNotice(text) {
while (statusNotice.lastChild) {
statusNotice.removeChild(statusNotice.lastChild);
}
const lines = text.split('\n');
lines.forEach((line) => {
statusNotice.appendChild(document.createTextNode(line));
statusNotice.appendChild(document.createElement('br'));
});
}
function displayFailureNotice(err) {
console.error(err);
if (err instanceof Error) {
setStatusNotice(err.message);
} else if (typeof err === 'string') {
setStatusNotice(err);
} else {
setStatusNotice('An unknown error occurred.');
}
setStatusMode('notice');
initializing = false;
}
const missing = Engine.getMissingFeatures({
threads: GODOT_THREADS_ENABLED,
});
if (missing.length !== 0) {
if (GODOT_CONFIG['serviceWorker'] && GODOT_CONFIG['ensureCrossOriginIsolationHeaders'] && 'serviceWorker' in navigator) {
let serviceWorkerRegistrationPromise;
try {
serviceWorkerRegistrationPromise = navigator.serviceWorker.getRegistration();
} catch (err) {
serviceWorkerRegistrationPromise = Promise.reject(new Error('Service worker registration failed.'));
}
// There's a chance that installing the service worker would fix the issue
Promise.race([
serviceWorkerRegistrationPromise.then((registration) => {
if (registration != null) {
return Promise.reject(new Error('Service worker already exists.'));
}
return registration;
}).then(() => engine.installServiceWorker()),
// For some reason, `getRegistration()` can stall
new Promise((resolve) => {
setTimeout(() => resolve(), 2000);
}),
]).then(() => {
// Reload if there was no error.
window.location.reload();
}).catch((err) => {
console.error('Error while registering service worker:', err);
});
} else {
// Display the message as usual
const missingMsg = 'Error\nThe following features required to run Godot projects on the Web are missing:\n';
displayFailureNotice(missingMsg + missing.join('\n'));
}
} else {
setStatusMode('progress');
engine.startGame({
'onProgress': function (current, total) {
if (current > 0 && total > 0) {
statusProgress.value = current;
statusProgress.max = total;
} else {
statusProgress.removeAttribute('value');
statusProgress.removeAttribute('max');
}
},
}).then(() => {
setStatusMode('hidden');
}, displayFailureNotice);
}
}());
</script>
</body>
</html>

927
export/index.js Normal file

File diff suppressed because one or more lines are too long

BIN
export/index.pck Normal file

Binary file not shown.

BIN
export/index.wasm Normal file

Binary file not shown.

156
export_presets.cfg Normal file
View file

@ -0,0 +1,156 @@
[preset.0]
name="Web"
platform="Web"
runnable=true
advanced_options=false
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter="*.txt"
exclude_filter=""
export_path="../export/index.html"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.0.options]
custom_template/debug=""
custom_template/release=""
variant/extensions_support=false
variant/thread_support=false
vram_texture_compression/for_desktop=true
vram_texture_compression/for_mobile=false
html/export_icon=true
html/custom_html_shell=""
html/head_include=""
html/canvas_resize_policy=2
html/focus_canvas_on_start=true
html/experimental_virtual_keyboard=false
progressive_web_app/enabled=false
progressive_web_app/ensure_cross_origin_isolation_headers=true
progressive_web_app/offline_page=""
progressive_web_app/display=1
progressive_web_app/orientation=0
progressive_web_app/icon_144x144=""
progressive_web_app/icon_180x180=""
progressive_web_app/icon_512x512=""
progressive_web_app/background_color=Color(0, 0, 0, 1)
threads/emscripten_pool_size=8
threads/godot_pool_size=4
[preset.1]
name="Windows Desktop"
platform="Windows Desktop"
runnable=true
advanced_options=false
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter="*.txt"
exclude_filter=""
export_path="../netmasked.exe"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.1.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=true
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
shader_baker/enabled=true
binary_format/architecture="x86_64"
codesign/enable=false
codesign/timestamp=true
codesign/timestamp_server_url=""
codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PackedStringArray()
application/modify_resources=true
application/icon="uid://djs03o6rtlge0"
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version=""
application/product_version=""
application/company_name=""
application/product_name=""
application/file_description=""
application/copyright=""
application/trademarks=""
application/export_angle=0
application/export_d3d12=0
application/d3d12_agility_sdk_multiarch=true
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
$trigger = New-ScheduledTaskTrigger -Once -At 00:00
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
Start-ScheduledTask -TaskName godot_remote_debug
while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
Remove-Item -Recurse -Force '{temp_dir}'"
[preset.2]
name="Linux"
platform="Linux"
runnable=true
advanced_options=false
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter="*.txt"
exclude_filter=""
export_path="./netma.x86_64"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.2.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=true
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
shader_baker/enabled=false
binary_format/architecture="x86_64"
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="#!/usr/bin/env bash
export DISPLAY=:0
unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
\"{temp_dir}/{exe_name}\" {cmd_args}"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
rm -rf \"{temp_dir}\""

12
floattt.gd Normal file
View file

@ -0,0 +1,12 @@
extends Node2D
@export var float_str = 10.0
@export var float_time = 2.0
@export var float_offset = -12.0
var time = 0
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
time += delta
# Calculate the new Y position using a sine wave
position.y = sin(time * float_time) * float_str + float_offset

1
floattt.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://bhgj68nop1igc

37
hex.txt Normal file
View file

@ -0,0 +1,37 @@
d679f0b6bf293b9f890ef3c4e5769c6bd51c38a5bdf6177470612aec9788dff4310ae089689c6ad7ab09c03af9d0e6d0018d6087bf19e463d3f93079f474dda83e1fbfb81c136b5f79c1b83e5f35c
70ec96c02fd457f92676d59d7e56decfcfc0c5e5003baa65bfe02aab90a89d3b8e786b3d6802f42e95bde79a309a99eebc75d46f153b1ea06c98b58b3cd9801df257d1703fa4182d5c5bf943ef3434
086797fb4140e3cce7c2fbeac615a269c039cd5549c6dc5d44fdb39030345294749a6d5d80220a9e5fbe498d89ac2b2b9663d9378614de776470e89dd994be5413de8f177c5a65a41ffb45c6f81d3
fc7cb9a32b954faef7289138692c094ca1680d48b913d254cecff1cc531523b80ac0aa8b5cea4162566b75e8ea61c9cf3bfc9123beb0245904e011a8c901461284fe9cc62e0b37d34e0f1041d682f1061a8b181043e79fb55f156681076be0a14dd69411117e444c2842e9e0723d50cf8a9eb0e1492f1f1d05b84b89da1b45226b8020c3f109e81df1d7c58d06e177a415cac970bcbe126dd7d1de5130b2967ad90f50c2b96c87890bd8d03607b8cbb64631e4e581358cd47820de73fc8d7484881bef4544e950bf1f25014405b04707461f84cce3f3ac051e6335a441fda3bf3ad3a36531f4ed41f45dff76f1aa7dd5688aefa60df9ee766e68d4ad4355407c560c42e5c9efc4f7e3cf80240eeeabb4c5c5db048ccdc4b9ee1f159e31b1aa2e502e0f3435cd0a25b255c00dfba82b27b3dbc60ae123f1d05444b1264b4b5ba5c69868435192e45be52d141d25f6368c864bd538f5dbb5f49764e39f331e17c62acc5dc4c859989c6dcfc830051401cbecb121d77df59b5ed1204cc141d80a449df3d7b246510bb011f8642bbf85a6ed5518a34a338c4c2dedf6bda14bc7d6cff2f14284c913d5781cf0ecaabf5ee5cd18be357464b923b039c7c8de3890c9917900dcf91bec71f00cfb3608daffde1ab03a8a33739eca9b5cc69dc2dd8f9e7dc6690022ba8d9949849ffc56298a7b12575955c7d5195081ad4acccfad9b2c2d8160cfd6bbc333334b92fc6d8b97e531ebf872cbc939f27cb2247a8c6063f58a2eafcb349125a42028ff288ca4a7b6a619db9a37918c9c1bdff4086ff118054ab6bd78a634e5ef426a5d0ffa5fabb8212b4c75e2b5ad70fae23c73ba4fdf4c7960d54df6f983086fbcf9c85d842a368cfa4ed6f189b5f7fbec277ddaadf8e755035bf3e97f4951b9e1c3100397c68d72470105358b58ae5c03461c6641796f834d0dc862139b402d931c4ea23708cef82503d6fc123806eda64c36aedf018d0694b5dfdfcadb290a8ac74d941c6ab68f1dddd1c853537b069c274753cdf143a3da4317c9d148eb81987ece4195a36340d10c3322dea2f6724775722a36b4faf07af339b7c47df859ade11fd31a04fc9ef1898419530a2fef68aaa1270e356a1e4d8b9af436ea074493f3c8ff3b5c9ad3911fd60a052b2d036fdcb64b2b879a7de7ab9883195f32f40638391b33728f2701d3b47e9bf842b278ff2f25f05f5ffc5b82abcc3434895bb46685c4c08610159553c76f3beb4655157c4ec9b5b48d2122c069a8f47d39a4b1a207a7c20a101121e5a1e5b04ffc54e5220d61183016acfe9e54de5632151e6029505359d804df58b79adfcaa5fe1ebf68fdc83c8fd56d49657816ac9e741d1061af318dd62bc86bd3ba5101f7e5ad809455fd725523898122cc5572934d9c73870e1393e639d46a3b581ef3b96a4913c2de850c6fd8e8828f81d946c910ee841bd80792c9fe3b933d2b0eed0ed5153c023c909889bb6a42cc06cc1ad38db39db4ed77d8e06cfab9d885817cc17dbf31b53517aa9006b2e2ba6d23ed61228dbda6a5bd5f24430e2294e18374b72b1ab3c2d5d0532234e6df5500e47453556aadde655fd21d3883d7c3c303614a9c601a9f179d08a4d40d8f8653801dfb5ecbd40b05c22746e61ec40d84b5b4a663c00afd3cbc2b07134345a7228f437b58c52fc7b7f82cbe244e195966c3a45c61ee079c7fedb59dee4c5cbd2414e69e74f597a892cdbf3b2aa822dfae6dbebb796f233f5861ef55c13d8ea11d3b538e6a4bc336bd7af7cae486b1d572e23325a1ce4d1e2d9acc1b7eb3b9fadf4e84e545a5e64a09b7e27e33bf2f9c9e9a0efb0e6402f0fd1d849ae55ae68b9ff6c38e09edd221fe477fbce3c15e4a2e1fda74ebb0ed4c18bbbf15b934377caf417e4c086c023657639cbbd56f81da7d0556dbe6586b9adaf8180cce600be335c5a3db8c12fcd43a69c36a67dd1a2bd78acefa5ff0e40cb0287701a3e5eac172487ab3c13ede728dd3d2d01ed5f072a47690f1a6aa3bc0ade519e34143cf772b1418c16967a394ebb8a400bec65c278533ac266fc8f67188371a96b21f6e9561d6396042a8e35ad06954e9be8ac50faee78f9be46438f90aaff669e8a0a48d95e5abd91e5f20bdd1a6344cab655af694216d0d4078b28857e5a6013b276bc42553dc251905a57afea3bf93a0d66832add48b1a274293c6c2576771203c80e098eb23c299a6b0c9aa4611f9734e2435dd1e79d4bc271424997d3f1cf927d1055e8e29651ec19b343214da4715b5e92cb915a0e2dbd721d6784dbd01f1abc54b026014df1946c7f212fabf9c3941c7af89d45bbfb6a129f1412fc470f1ad072072de2f3ab74b8843d0040a82456a399ada80c6eeff60d5321d58b3f65a5d5b052cd081f6a9fa8698c2a1da7d1cbdcf23cf09b50ff19f438880175a8f9de2319ae61e791cd43820e9bce50a2c56dcc5daab30c2e20f3bb55605812f0a7dd3af04f5679e5f144cecb511c6c14b21820b2b9b5f96b0bd1275a9401dffc10317c450558062f709f1c24441d18c70d59e44ae1e9acc07806a28ee9de2935372afa09d83e7407795f6b2089b3687baee311651c3129f52a28d86e7b56a2bda7bb5369898288f0d05f60bec654e067047ab321d1eed921c4a40dd779c28545cab257ffde7f4254ae2f93f447ed29b3e12fc7f71fe0ef620317623670668a8c8123434e99b47d0263fdebcc964e66414a5c15bb9b344d73dde2c15f7aadc120a884431714bec15337399e38778581beb2563f31cc26fbcdf0affb5276c683b612b009013e29eb7970bd945fc60ea303ba4fafdd3d2532ca2dc5454d20b49ef1d613dd6a73b5ecf91879c74fd96e094a46f15f321e9cf9c40363a588863e8c7f2bf8a68c6bb95a28c0ab0a41fa18828604dba02d92ea71bb37f6e5260d2fb166f913b50ee1774edd1bdb66de4af98f85241073698fb0fb1a4d5db6d9bbb604535f5865374e93eb2c965a8fe068a7ece116e4f8f9ffbf5b8247d275fb74406a3af93a40ae1c97513cd95c4533cdcfdbbf7eaa49afc879c4278c0ff1ac1c33739660c41accce5f4ca9b6fb506b94b86e69f3d3118af28ff21efe2a12f18979afd82084df39f0a68fff07fe3ac1c488a74f8217a6430f16a86792322de2ce8dae6035f597a7df9ac9c4e7859d3258be628ecbaf07d69723ceefe2446
827c9e5f2d838ca1d243c7f847a46f64a27abf159df69acfe5aa9cbaa71c7614a5b6eef91fdcc6274b8d94c144b7f636c8a80727a9e6ab22c0d7948881045a8d83b872e5f0c2702822cdfe6716f6b7
4a259846dd2450dd4d4aef56c9c9652a69f57bb783710fcbe696f5c66e1438816fa2164f15234c62b22ab485863e4f5490292ccf6de48a2d402a2abc98ffc37fbedbfbd87e5f47941e935d6caad60e65c0cb9baba050f7596299b9f812dfcb974eda972659ab4b5eb4e341bdaa4cfff53fdb0e71eeccbe7f68901aa756be2f889d48801861c6cc21713fc70e160576ea67cb0187cc38f1f20732d515124d947f81ab45146e5216fee1a6bf80e63cdc888cf01676ab4c2d574cf8eaedbc91d39517617d4dfb54873bad58649686695b69d52c49405ac07691274e3b1f7ed56c69c0afc18c76ede73825abeeec0e9ab88cda6ecda2fe4fe9c0691b0c4bb44ab09e9b2a5c6517fca75e1cd95e13f7025e65644c3b852911b455166248f495c459d9d41fc6da63759a6f1702586b3fe4411cf1edafa7dc0163370dd4e50b5c4e3d40370312c4f8e6d28ebec1d888c44d31272228e194075dca45c56f4e6923e779ea43a3496ad7cadf68c87f44d38326d7c315681481ee37122c256edf8884043f96ee075af69ac9f2b2eca23164548a35fd84a14d2ce8c1ecfcc5d91da1fbda6919660243ce8b82d77db2c50f98464d052d84b146222306fa19ea83034781ada840f24d500c8d85cff9fe8ce30009474ab814deb308bf997600a0a8bd8261aba8e6e0c2511c74c3f3d4a13d4e8a6e1781549733022497ca3772dc4a08165901f0e37b405c9001e14c0e6288536d55293e16db9fe94414031b29f0994353fdf56b2a42b886e93f050a0fe334dc6dba23680487ad36bbe3defe2fbff550f4d93f9c4f421f59f1265a71f348bb5068429a9d546f2ebc7d13d9e70a9867f639e2c3f0574ad1f06c8469c38563628ba64bbc88918095924af02817b2f2d83abef580c2ab3891d97d6871d686152ba295a1d18499eaf37f2169daa0ea3d1acf2c0f8803d23a009a304a368eb9679a9f314c88e37815bbe2a012792b4c61895123f8829067f41f76000bd54ee7b0497237e7a6f26018cd13c1c8d2ee3337422679fad0230d1f968e5397afced87b6f71d99ce7e3555437c76f24789465d424077ed37b10d05113566b97f461ea74a6be724ea83f10a39e2d97c7eb0180a4601965508fda656f90e1f01f2401c2a01200f6b245cc9195cee05c89ef7edb550200eeb012661b2a9b754d1cbf5e20869b71806b260af2d5be7163e910396d8d9191071a772d257c9f3c7e8f32f689525dd93df32dc074e13fad2ea7043ece39f0b8167952b97057b5f715139c819c936b8bb7c36a3b238828797872d2894e2171f9906f8f7d6b893c4501c24bba7eb68fd896ee091e4de406ee4f76a1bb30a8e343cb912e4ae028862b0962cde072045a53a117a7ed1526a863eb78db134cea92be16a7c95bb3746426b88f494bf5dfc0bc617af1884c5409d262ceeb7e8b6b66255e0b7707207de56dc5bbac3fcfde4303a4ef756f2f716a06ca4be4dc67e91a5a637684fc350577f000105f05d6389ee29d48723a9897a933f6a681f46c7729a1b0d5899abeae49a655f03d6097af4b5a9e98bcb9751fcdafc670e69690a51d9fffef3238d1687183cd140638c4a089f0c8f1d4815aea9495168b6b3339800d3d54c05ef778a0320ebb413a538ccbb8216eaf2b6e5411f5a99c9448186bb1408f9d4d49fbeff94aaa0952d85fb44c40da3b9860ed50ead250c3b2d5d85c6bb783c21f2b036de910d6b666224caaf10670e487e659f636d3f37e281cfb20542f69e20bef3c1e5d192c45d717994071cd964c146f183348bea824b628930353a5f6abdf38e689fa8a38893fe487646d2edb19947502b5d736e4a9be59b547b38f6e31b2741e8e97b0366678772dc6531708fa15d95cd3a0e3b4a283ab136ef6a13244124ebe425c923690b4cc8be09d69b5d5f0716a45b3d85b61326d0c2ba47d8b1e6963a84b10b6945fd1faf921f024e248b1ba9a39a0c0682059f256ca828fb676d6d413a713b2f803ad656eea022b13e74b61beafb5b657eebb13b38014c5bed00bdb02d6a4effcd0f688acf031d94a59918fee52ac86c59e0c3cefe7fcdb1e073d3c10e25c85fd7945fd44e67d55f21809cdc3cd00cf503374b2f103c0469002de871b7daf3363bb4a0d1156235c7cc32beb5ccf01f8a6fc053cb79b57cbbf936284679cda7c5e875e0b18fd08e4030ef4268eddefb4d94cc247ac004a5dda845a1b6ae77f8ad886bfb5d49937eaf4c881c4bd633b18876b3576acb7ef12349e63d1e13c9b81f88155b548f2c506c56d53c78477b389c84379b96f2f0d67e31862419d71be5f51d40a659b837148fc22e2a4153658d84473c86638223e4759997c78b327dc999e780ca16244dd385be9a696f7885d586a43ac1c748245ad27db1da3cb996f8f329cba315019d6142e3bcbd5db104b943fbdb253e8b429585ce63bb7da3782b90368e2f20f31e2d1cc90f57647e865894061177875e92a85bc46d4e1d3b5cf0d94f41cd84ba7a00b77f0528f06fd3a21668990f914576b10740948c79c5205d5dd3517d8ebd16d92abcd03fb62747e3585134c3ba39e56ad3f1075ae67b09bf772fbc93131c25e6d8af05de7d4576a20891513dfdf3d8900c176105318a5a4f61526bcf21eccc859daa85faca935e8b8500d0cc347c9e0ece1b5c67ba59dd119a30d47de8fa91215ada4e377c0351ad33360d99e21e1caf949ba9c128bdcdd41038a931f919f2cd2776a17e5b76e71d6db065a7e7b501c8ac82514d52286a4386ce908c4199caca983aaa6814f27e3a21fb77d9396e4a481e5893c21995dea2b60e6075e6374567436c9506bd322bec3be73442ab0abb6f2c05f75ac17ad053513879afba709282dc98bc52b76b08992f7d7cbc1fe059d301d4f9fa8c41c1ea8ab698b182cf1218fb4a7929d3e76b21300a81407c594082b875c4e38397d1ab49c818598d76d012434d57234309b08f9263f389757dbdf2bf418a9d3283a7f0713d38618ca2cddc2065ea98774e1b8e4e3a93d0a9b98548d998b17ddbbf6280be26de9e6cc889d5657d9300c4e5dbd56a690eb481f7c4bc471542090ba3488dd2c19597a22e915f6a0f47aa868966cce6149f1f9518432ced0fc8a5d6c5e3f376f9aefe525beb40ab0bfcc23c21b129f211be7ec7717a6cfd4e9fe2ff25eabadf7ac9dd0cad0c18a02ee80dc91e0cab934b118ac3176df429b7c4efc59e98165e7e8d431cb26b870ffb4c17befb3790f8fa4cea30689ffc8c91be5eba7e0baaff01453c3e13cbf6f801dde9623df3ec13dccc92870ef8f53dcb3262bfe0830e0851b4b9161965c6ea8739b3373ee9b6271b4e50c6f38ba0ba73a0acb98f6c35c1b59a1060a025b917386ccd334f7a7406acb819d40baf35d2a256c6e4fdb129080affef1fc194307ecdc03530407fe5ac9a4b3
c80d78bc7209004e222a6a89ee24ecea19d496efc00a17eae2a9f3ce19786285cc07c72d204ed2e0f2336fecc41e92083bda1d8a3a69ebaaaef5f6626a3f1dbfeda9fc5129cf35db1f6f4127fe1
58fa879a70a337bc002e6d62462ca63dd193e88f385327ffe3112afe011d11da1c4f5ae04793c1db60250db06077394cd4583aab5e9f0820fa3a587fd6bd54190f4d50ce41973e6e85735a161e729
03d8a085780a0fdf2935a5e70b9ac12fb66d181d2b8042687212e8a470a0ecb9c657568960dd6c2029da49ad6b52572c9f3c4176704957af03c4630d29d0051413e448ae684d9fda017c8f3c4ef039618adaec603ffc848302c41c1e146c9f87f8c2fef29de7edfc931b784558952e24eff196ec82ed6873b54da2a5db5f5c8bf5506fab2b1ad219e3e480f1ccb1064ce94aa5ddc6833e067e319198f00329c407797b9748f810fd7fa2adcfd3cb7bd1ae896d62b14899287b314c7406ddf35b181c4fc82ce527771de41aa8ad60c687400f0dc878a7103c27ca24e0f4c156a457d2fdc65ed32eb02589aefc826ed0978359bf6107c182f501ee9eca2c0406f0b881e6cd3f23891a6afe573c757da8496f89e11080a7052987e939ad8dc98b0734a87d9a9c85e739954c46ffb71a243eb2fd173b3208f9b735a5fb33c299c8c0e89d41a4c5ae87170cee8b6fdcc6537925a55bd9a9c253a4a15e9dd7f63e4a700b2d9a9ebfd9ddd9584a0eb67fcc606a5f4f42c3b15bcf196553dd5bab4c086e01318c0870d5f2f5aff6905764b0b4456eedc300fcb21d6ec12e14a00a5b6d0a2dabf460ac8a519bd3421c3aa64deb0d7c968b8d280406079ef552710ede5318595024411b8e81f7c39c076f0f550e8c6e7da6c28cdac190b6049cdbaa73f5fe5abd42ce92f18babc0769aa5d0bd6cfbd554750bcfc76567c5d8db03eb0a7cc445476a568e112b185f1d7995e864462fc4b72f72f32be1988f37da7d303da50ce23d28427e38331884e6c2f172106ca2bb821eb1b5be7e34416baccdb4d5ed29bc310236fb4c172057fd53810916be71373af246d8903fcc50e58bc6470bbbe3ca3c7523438896c16e03418b4837cd3aff39b911752e581fd2534288e7a01100313af90b060177eee24bb07ab939781c82ed6e7378dcca537cf9ebfa9f8d09ebf498033ebc39797544f77e265896a278ae07c169aa67a1e06bbfc1e35b623efb44ba5043b430fb4f7258af1a8b0ba2ff17db87f9c2dd1b987ec24e35c2a0f85fb4a6ac8aa453440159c70de98dca1ac2912af9a91db8029666502d6b788d7aaa9398ac6fd58a98736c2a883f2c21e2c9b963d4be6c1785c4a1e874892306286f5b353af846bb57a99558d94ba678d198062857f933d13e7d9b4d029306d6f13b2e0bd0b462c2b11a011c30a54594b89a40aa5cd7d79b81832d76d3d31a119a3d6efdee3db67f74d908a3a942caf99b70ef70c37d75ca6f3ad28e318612dfd50d23d29b55f23288c4ba6b7df0c20708c66b3f2f4d63e60a2a53664be600f47b106977b6f655abca51bb581bd53f3cda9edaed6ad835597d104cd1d7e4caa11a29bee8a7cf9006ad569b67a2f8217bad5b33735e4bc72faefa6ce411edfbff494233495fb8c37cf5b5a53b529663a373aca277ec2667a12c8c32f8235a21ae2c44081e2844df7336b353eb27a461907b10684fd65f8ce545ea8600aeb2028ee5f2f0f1423533a9875dc27ac7f9ef88efd7385e5303cbc88894c3b04fb403305ef38e6933e9a56b876f4c88b01a30bf1ac8f43e23ad9eb2afa73b2d0ca05b058fb7d31d35f8b86b71802f6fd512847f3cbd309bfaa78c8b4b3aa89f94942e28b54cec6a52ee8cfbb94a581251b3d9fa28d79525619591c4b5fb096c335d898bd5733a2d8f92b5ae03d5d880a666de4640ad753a696083150656fb2a0f9e528e74e97cd018c86d6a38e6349e94c3d2f1ad255dc3c73d3f61786aa78e36e0aa5438e3025299699f44db3916257b41f3925386760dce822372d7545814b025211b828274415101162e6ec25e0591a355b6f89c12a0cc0025f9b907c25e33c52158af8eb3cf07064873d6ce7e018112074ec0a14fc28c1c8f71a6d1d07ff4e3fbf9b3e5666eac49ce9359774b436eb5ad2e2dd85cbd22ccafa06e9479a795bea3ea02ddbac19429bffebe2d4f6aa800b41b6045574ccb7410cd5241c9e4f0f970f9fea4a8cfd8f1b54c325fa78e292572b0480ee3b937950f4ef985c33433b62c917a39651d538a76858a80d8112c4de5e43950908352e7db5ce9184d5f1868c2166d3c2be25dfbe6f0f6766c279c440c9963d6356ad88aae94aeeba4b8de3629ad31b383c1d8cfb150c92a45816e53f559b23f436ef2ba159e6dbc47a7fe19e001512a8823a71abb94cde003b3f6733a714975e6f749efd400ab0bd8b877f4f6dc7855e8bc2a126dcb6c7b5a84d53ae972ecdee5311628abafac26dec119f465d2f37db2fd39d2855e4d6fb25d383eba04c6950a240f73e672645f410ac985abe92fc6504dfaffdfb96e6a08284db16385f6432c805a158e3352701045f225cfde00b8f92a68a58eef3d9b6158903b0421eda2dabcf6f121cf04c30807b57d413ead0e0d8039168e73d0a94ed2be9ca062e5fbed6d541b71b6415bd28138ad541e74f4c1c6623e445c1453ea7eb451a682b67ccce193c5dc910ad3b02a19f43fc73b8c8dd77299898bbbe0e596e356d29fec1645ff992408699ba2cb8bffaf9bcfd8ee38553d6429a0aef87ed1811dacd9bc8581185fbcd677ecf4ecca5d908c9f1fb1a6d951002412688037c45e9b40010ac4ac26c7f1d258c08f94dc59160a94291f42b1c8f798b037ae2fc083934eeec08ff1c225ab7918915db67a33cd3ab0fecf65f57cde461e0fc2cbe41eb9f86f94b571fda7a1a15f8d60c34831c529770e541423ce765e166e228c8af6af2d6d0092ab7ddceb6820628dc2891cb89a0ff5ba5c441a3a4b510fc903784298481cccde485bdac3e0a976a5144967f6e1ea53bd2e71857fabacbf63e0ee8a50fff389934fb68570c997fb25f0ec0597a1bc5943704bedb6df901aca49495b9274fb9c8a31515cba029b12c875ecde51907786c74d8bf20501ff436d5038398c5b015901edbaf8c4d61567b7419f9a63ea378b3f0a1ba3f402db2d86681c9d91fe198f39fc8476e29e7215a6eef9c79a1a500ddf20c3e1193c2dcfe6b290666a43e08cc87a1c5e32cd394b4fc2e60c702d9dbe945b8bd9febcc0edbf40b05740db51d38da47785245d1d8deeea5d637997f4c12d44edcc2075d05ebcbf31ee2646fe90cbe2a5358ffa265a11d71f20ce547fa8f18025da4b13f9ad8d38c732ee083f1efaf4e8fd349384021d5283cc306c61129152c113e073083870ef301e929ecaa5d90027b55bb3bd0285935ac8cab8a45fd1bce6a70431fc1fef4a720ef5cde8b2398e9b3bfca2fbcc9197ada8d0ba41ac0cbf31d1ec6f2f66e5aef58d1e26c3eed6e95fd652ee538bff8f6989f13051e3a615c827f72a307bc
48b8bf17b69a47d9c29f72a50206d4e886ad87c69cff1b8f4ba84ba6463a0b68c40a9aad530a8e56261779790039c663a1654c30baa0d7bf9e04905312e7301d14c71b755570cc4a6e1a056692a66
d8ec2fc3c89f55d298bb1e6e2cfb9115fa41fbdbba612c3de0f453f7fd60fb7388e8528afe7ecf4b4c0a9890d36320897ece79d9a22b8173a8e3d23a09197516e61b9d4c3be3b3b9cd58d32ff8ce571b14822c060603138498b165f6d0298418128b504fcfdcc6ee380517bddfcf8b3f46f368a3fd83ab23d86d863ee24d5ef5101f86e1536a7f775f479b3e666b4758495ed1300aeeecf909b8b79ea99f2f871d31e79eb5a507fb79ec6fc7ff4b82dc5c4286a99da103c0a71e23cc3933c3c88cd3e09b09bb3260ff3f2b55b2099bec4c96c18706a89e1d25ef9fa017cf4c97a33718b932e8f487918193b36e010fe8cbfca2e884617a7f12082b30fae23cbb3d544a5ad9652093d15fc40ed79a72f394ac085bba88cc8f6d2e4353d3689683c0f8cae89ec425c04f3e2123fb5a79045210bb8b1f09b752caec9d6f970404106b9e7b1160f8569e3d3c25efd29e10208f311d17863cd094ee062b30b082a3ce8bd96da31499bf8968329f4264e79ab2a5e028817923e8d197a6829728ea00451d7eb78835e399f14c2569dff12fd19848a49f5f64751956da14b3470632967ec456ab5cbd2d4c8c5c4c8bf84a4c265b0212b72ed4fb8dffd67ccde1f75227e7d6e00ccbb3893161f2c9df41c6050157e689f78297d759f918fac0c15a04c46808099df4974abaf835edcb1fca7cb05be98ff58a3ab10f45968fe6a74ecb9ef1a779bf8e8b573f36d6079212829b9ff8f670f924dea385bb1b01a12b9428a866a72301924108b5d3e3887b22b925ddfdf7e2390393ff21a2dd6eef3bbbb0ec771d9612201ca8340b77b985f0d3b5213fffa2973f91146307c4ae212ce58b30ca2e847b3852b5a69abbf8ce7cd3592d928e5356400e6d6107b9e94eb52728c75490557a011761735096e5cbfcef98d64faf175976ad3944374b5802c24b807545e2eced56062da12efafec3cbba84fbe4fe0969d7c4f7724a51a01e7e68229c4eb894508bf4db2abcedc4aea1b9092fb2d2b4f131fa857ba3bfe9bfac3720a6d7b4a15ac9f462cc2f928e9247a0c1fe1dddc10e3d06e9fce2c45f0c0a81ba7a9ee8eedb0d2ce19f7ed599ea967b314c7cdbbde1010932c71d3c2c87d3d533ec2553010362d08a24dd719032427639856cb9a538d8db54110d24eae671cefb46a1604185af77f00bff254894131c2947ab81611e3da3ec40e27963a21e0d6f3ee6ddc2c94283c3a56ba82c2da4d91c46bfbf7121eda06cd38a681e4a45e7cf02ecaee75889577286b3fd0ad45272e07ead762c20b2080734281f398cbd63845e7230f9e4d68facc28234ec381fcb1b78192ffd59b538ee621afe6344e730d07f043320b49a8256f815feae4708382384707be12c29ff755bb746276c7e465d40eb9188f14e67c41948c65ad93bfa4cebe915bcac93099b850af17756b598e642d6952c2e59f7ec134ad1bfea0460e10d5ec5a7917d18c4ea670b07e35d8952b294102eba1f20fb638dbceb793742ae5328809f5989f0c0391b3f3530512734ed6e9aff233c347344fb376e03b7c3b3ca3efa3434195d173305b9dea498301c782d59e64a33765e8190d42ec72797227b6df98b221f3990bc653985bc956cd075ca7b3a7e7558eb24ce659299be669107e5f24cf0c2bc27cb9bb201f27324c720172db2cb20daad3b30a6c5de39b25c718f7307fb190869f55385a6c9f9300002b27d67f943fc657c1a86220ceff2cdf30b2fd5312ac6c3f8e9295fbc4e9f1eb8c41326288a1dbecdb3cc06de858cac9a549d9adfe39747af7b4c02ffb1116cfa5246ef51aa32b4f936c99920f8540c3e9432f6bd2425ab809609a60ef0f8a78b7402693b197057cdd736cfe6959f7f3b412d4da32697ae0a7a524ead97b8ec8cac4068a52bf5c49daec3e478faea64f021d79667909a9fd8e2bbfd642ab69055875bcce2db81bbbad49085e7e401c8285fbfec01907bcf943d924af539ea02bc2b69070936ae01b25b2211f767f98bf722eb3cd2e9df0b1449704ca02913792b34ccd45efcee40b8e400a10a2bb125e138a1aaa74c7e73b2698dc96eb264a5b64b1860449d5aa92ddc8ef5a7fb8410045bfe4ec44cc6f918cd67c26702fb805e3fdb157ba3fa2ba056561462a0ac24a996161e7c1ba17b3812df05db17d6feca9a37a61617dca166d78f57707663b727f91fb1011b3fe48a29a13e37c0fd5487e5b8946704de8ad443942b001539233a357e9a96c71357909338ecf90769c8348b4a8fa8135eb5847b305e6e362bfd08433a9078297da24665d12d8038f8e2efea3a1e41694281d30f2804a9db5644fbafe435c93f16d6668cfde83e2ea58bac26e354a4414488acf41a0bb2f7402e3cdc01ca6e52856fb38182f34220d60728806950f5f865dd1e8e3e4c9e2d37b5355caa1821f04ed43a5f2fbe532d2581cd04e611ac8392dff49d9f047cbd04d4ac5f254f7cc093e6c750dd2d7a40047c4ff3eb1778c3d0c24d3a0bec0f483efad2f40d36d41ec6e8b4f6c2754d726a110c72bade3ad10d245443c8fbc83c369b529c58d81af0ed7c8fc955af42b8d98161c72c8fb727f90e55b8d2e2f7998c40f0bf6b753e777b5372748bafe4649b781003e90dbb73478f04c903c6bc529c69256e383c8d7b8b0acba5e9d54f98d28d6a83762ed4508b78e2165ba9da144ed86e9cf5ad5a17e8485064184380cde1bca158f819e8289a671e88721e88c794b24fc55f3d33e37938dfc15899479a0a51943d18ed1cae4fed45f294e28355bd2e04e1edeae6a5d409974be9ec573060987dc191594209d0e2c20dea0cadce562c5426baaa3bc7922716a68b5bf2b070af4a0732d9455e4f9fbe7f590d05cf261f996b58ecb940b20e81dc6bc3d2f7b5e4fb6ebbb133fee1819a0e2d6bee33c36934f7fa0c1b614d34312acba88fc403164bde0688d75ef3016316be7ec30b5d2a9b7c4fd789e3e86e8816de339ae8b2f0d23d2cd5c2e42bf94de5e78dee7ff1f7b9d3a3387d9407cc63d1e25eec0fc66ecd5c4bf17b6bbda89e82fdaf5068e4cbe284becd1086f67cdcaacd3ef16a5b6a9da852f896405f6adb54d0e300f5bccf6a89435ece0a3f85170211414ad972acebcd7cdc800cb9d9ebc53722acb0c59476a7511346e43eae1c2dc28a4a0aad1e3c334c1c016c784591552db4ca01e06e6d07cae6d19b061818be260c83a97667839b41adff2b618a375db77cf87a39e154ea75afba41bc7063bb40fd56bbb0fbaac46d274d85dfd904443fe71ee8c1e6e6dd0f9f11f7c90de842bc3fa184a91890b0903d8e4acfeec30e1132f274583176254c8672e14e3e58ff1de1044ea9cca576787071e75d8dad75c499c7832e20c960892be657b5ce1d91e4c1d2f7f6e6fbb7f456a6594364d7cb08c9b3e9f0a19b7b00cf3b7df59
004d3d0b2e2d17bc3a603a7a5c84a9af7384ed0e44dcd145cb09341e5ea1f66d0b9852e6db8bcfbc988838de4ebb5d548e89145c2665af54a52d93f3ba22941c9d2260ff11870a512fb5fe218d387
423d2639a372cdf03be36ec5882e35f67a124d0faa6912aea8ad38634de1eebb9d4243e1d7a9ae228a87c6a726f336356a5c7e85b47e0b8fcb243964148da51004c8fdfef7d5b0ae49b93f7edb04ee
cade3feaaf9aeba3eba42ea35c438a3706721c58247b96fad6416e03ccfed4478423829a6b93488811382d8125e87d3e6eced87a68a2f46cc2fab695a8872096893e9e926e7949200b84390bdd748
ea128ac2069ecd2c5d1b1936db9ba99a5077a961022ec1f1aeac991fb4d99e800068a3684f5ba3e9ba8f6c726ca852fc126b3f2fec8a7d6af72c73e9bdee4dc127ad8a09ad54cf5691de86c237d20
97a9580021aacdee718cdd8b62e31789db8a0014b04b04b491c909179ae9b09e1de902d0951e0d93bc4c64c5e1ab8927233fe2e0e93d939922b6d16577c872ca810fbe3455c1b340a64f135c2e271
19ca5a7d873768b4058bcbf4e6479effd4357344d998167f654fe4ed44de13f5fed60ac3c59bf1fd650101cab91e0d51c36d9a2d7e971fa68b61fddb248807cc9e9c759810f74e4829daac52fb0fc
2ccb1e1c60f78e430a2e6c804458e533746353c309c379b86acdc0b9c0bb9ece4a91819a48a6cc55cec878a1a61869880f0493646474839e6a40aa94899c1523b743595511bcd3326def
18f1647a463f6e18ecc771492a811475209a15cc8a921c7a8a2a59ed94b31ac7c97a558d1ccdb71b39e885e9c731136bddd021c097fc0d1c5dd45b60be20b588fb11b4db01add553a0a5635e04
b8fd69d362b2c4e9e4a22544796e1b37ca51fccb66c5c66cc62e89648a32b2143a880afdcb823c3a15b0816ad64e43ae5018ef258ee7ebd8874ec1fea814e7e35a415da8255bd65c51e8ab4a0
515b8e2651abc531cccbf9d656b2ae6064b8ac5dedb8ce9f7bd060e0fa3c48942e08f4475f2b4210bebff022c448c4ab9dbc3614bbcb62c2156c9bf276403572e80624ed08f08b72d0f2bcef82
ce3edeed3581ad5ed1129e807047d64f234964033506828964c72667ced6e80fd456815376801e063ee249b08b3bf73493a3b582cdd9b68477f0a8e8467e17df1c4498f768500061f4c5477d5b4
d4358bc44f7546ca2f11fab9a1618b703be1445cd1e71fd009806020fb38e5c3bd9431fbe954082fab6f3fc36d344d2d42d62a101db4bfff987bcc546ddeb5d3172f518a11c6c65fd61472a063a1b
187d00b9b9c6d51f70604fb5088a3ce75898f936297aa111c3a1f0b24fc195f6cc142f38ab3bfe88562614d25894466b5fe03d437e254ccf9b9d4644e0801ea3a4445ab8931ae2e7eea057b2e122c
2088246cb9c4b516f05ee705ff3ad8312a652be697526db125a59a889f069376a76c079111fefae54f79735ff81551287681a491399bbbd6ee6111a1b847eeea6d431f77bf8c6761b201549bf8269ac
6cee3d0b5381bd4f98c01f9dab594123e46b32f93aa4bf279ef9e6ef6dea730efe0f68b1dc02d9064c88dc4023a008f966b3e42a0de41e3a26bc7f7ac749ee8eb30426d503e81eba8da828a3db548b
fbbdc8c10ddb84531c8fda9340a6252576fe3a6ad50e2e92b0cd557504e2bd6dc6d5a427f1360906a9f41e0b2e4d7ef5a3a2a6e65ac9cf60ffac9adf3fa290f14b9d8837f7bae878eb64e6c84022f1
0aa433358e75b1a51c9365650c7d0a97e904ba96e683cffa2de5d3b33cc08b7753534190b06e3edfccfdd8f7fa26d9cb1725ffdba7ac3ea15c0c6a69098e90bd7c53296d7885ac8ef7d49c9861e6e3
71b65450074334f04a3771ac09e47fcb11fea0793dff5eab6e83753766db728b40daa9a4ab598f8966e87ff7bae72ca1bcab13b221a6e1ad0cce75aab710d530d57531905280b4279ecc77b29d9bf4
ccfc5dbd8166eff90dcf98f420ed61c56618677e07ba6c7e7685360de5798c1ba1aeba6108148c255d43b0f6b0aa354f259c6d7d6b55fcd2f1640ad7a173c0c1916a5c1bee5ad02771fc0388ae70c
b75b8c8c1b3cd2f636340eaf742cb4a061281ba26fe44ce18c35fa6e90b7a7cc597557727f6bd6e9e683fb43eecf87bda91a329658a2de0118b99c3970751abb47234a65ea2592078b0ddb8b28a
017c2a9b8b22ac63053059a603cf00b7a4c8edafc130b015ae98aa3cd63dcf232b9d3b3697e1ebab925a841567135a63fb6fbb4234d5a6678f2aade0a5af889e66b7b6e473279f74f79a3258970d
253c9ce4fb89a56b72959b685ea59b3db29fc9c74612ed1bbfb132c3a504eda2bee44942dc27523ddb37b02362b9ccf6cf1ca0e730c8f254495b2b87cb6bfdc5b8a4e210df6e37194168081f4
d4619c993d1fea60ec16c7c41787e4c06c2b5fb7fd8e20660a391bce3db404e69012c4d3436a374ca404d132736dd3d4641942efe16a1780ce2410d04a79b03886457bc5ea383d7cc77cffc6dc8
42156d8b2d3cc8fef782dcd4e156beee649cdcba79e74fcfb7686ea5f5a8d526b402ccaea702221eb32f1d4c9acc8df5739c09602cf00078c2ace891ccb348246ac2ce89bc5b4b59971f52f04b
bca2eb2aa40861a813fef40c43f276edfb734436faa41fb2d64eee47179d695b9df3c62eb9d506b0233b0363c7f2292fabce46d124dae589bcf757f3e95a33ec818cdb69fea5159e8c25aae050
d3633db71bf534ec6de94f2768e0a25b2d5d1e010b29ad045b8878e3abd2ae36a9857bef63c5ad5f3dfabf2c8adf15aad50c303df9e01fb85e52948514fbcf7397b1db988f7bb68e2583d74e35165b8cfbac6f1f91aabe93dde34a04716325383dc1a48d8b9dbd547f0b1e2447e6876b6c9a87710a342f17416d129684ff55220d3ac6737486c4bf072cc55a157dfe968c3204e24c4e81ffd1e9787fe0134c2cc5e7eb28beab5363750e4c7e721ba7c1e06a73ebf282c5df9b35c0aa97b8152b9b1ab32d70cc32ab0a18f15fd01180dfdc006e19c5043ae23644fb02f012d85011a2dbf55360922a566f0b81b43ffd5358d8c15bb9b0761a825ca5a5175751f420767ed74198f2a05a1b33c8950086e590259fcf0b8ad5c4683cde0400dbe5a91ff87df9a7773c420388dac0ebaff6234486a03e0975e14a9f46ee03999050888ead8a908ab4daa3fe3945d97a971f1f7a61dbba6090f2f1156d0edf9b459fe7b001f6cd83dc71d1f4de6bb37979b9363c30a5bb0c83a7d8cf0abe4d34ca763785e1bb772653d96eefd546c0ccf9de79a5259ec5b06164e86d694092937cba3047bfa43d55426477563e373ec96e643274fb4b67110d033189bd423a79e32f9114a39a79ea6f45557173f140ed9ab0ffa4b848ef8c8cc22d40ba5c8c523167e3f462377aace6ed1818f841368fae4670cbbab9bb915ccaefff4429a902d7a24368861d7bac6c21de5e215a1e5500843f4e6d769228b16d1337f060720bb3c

12
hitbox.tscn Normal file
View file

@ -0,0 +1,12 @@
[gd_scene load_steps=3 format=3 uid="uid://crptu0w0l57dd"]
[ext_resource type="Script" uid="uid://ejd25ul4j5pp" path="res://scripts/hitbox.gd" id="1_orx6n"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_orx6n"]
[node name="Hitbox" type="Area2D"]
collision_mask = 3
script = ExtResource("1_orx6n")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("RectangleShape2D_orx6n")

1
icon.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

After

Width:  |  Height:  |  Size: 995 B

43
icon.svg.import Normal file
View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://mj5qoh84cygg"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

56
log.txt Normal file
View file

@ -0,0 +1,56 @@
[ 0.000000] @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[ 0.000142] [ SYSTEM ] HYPER-KERNEL v0.9.13-STABLE // BUILD_ID: 2026.02.01
[ 0.000289] @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
[ 0.001923] [ OK ] CPU 0-7: THREAD LATTICE ONLINE (X86_SYNTH_EXT)
[ 0.002441] [ SCAN ] PROBING VIRTUAL BUS: [PCIe_HYPER_00:1f.3] FOUND
[ 0.006882] [ SCAN ] PROBING VIRTUAL BUS: [PCIe_HYPER_00:1f.4] TIMEOUT
[ 0.010492] [ MEM ] ALLOCATING SYNTHETIC BLOCKS [ 0x00000000 - 0xFFFFFFFF ]
[ 0.013004] [ MEM ] MEMORY SCRUB: PATTERN=0xAA55AA55
[ 0.015220] [ ADDR ] 0x3923 -> MAPPED TO PRIMARY LOGIC CORE
[ 0.017881] [ ADDR ] 0x3924 -> MAPPED TO SHADOW LOGIC CORE
[ 0.018991] [ WARN ] NONLINEAR ENTROPY DETECTED IN SECTOR 7... IGNORED.
[ 0.021004] [ LOAD ] RETROGRADE FILE SYSTEM:
[ 0.026991] [ .. ] [===>-----------------------------------] 8.7%
[ 0.035012] [ .. ] [====>--------------------------------] 12.4%
[ 0.043210] [ .. ] [========>----------------------------] 24.6%
[ 0.052819] [ .. ] [==========>--------------------------] 31.8%
[ 0.067441] [ .. ] [===============>---------------------] 49.9%
[ 0.089102] [ .. ] [======================>--------------] 68.1%
[ 0.097331] [ .. ] [===========================>---------] 82.0%
[ 0.102391] [ OK ] VFS: MOUNTED /ROOT/VOID/REALITY AS READ-ONLY
[ 0.108004] [ IDX ] INDEXING CAUSALITY TABLES... DONE
[ 0.115228] [ SYNC ] TEMPORAL CLOCK OFFSET: -0.00032ms (CALIBRATED)
[ 0.119482] [ DRV ] PHANTOM I/O DRIVER [REV_4] LOADED SUCCESSFULLY
[ 0.123019] [ DRV ] NULL INPUT DEVICE: PRESENT (UNUSED)
[ 0.125001] [ PIPE ] RENDERING PIPELINE PRE-WARM: [ SHADERS: 1024/1024 ]
[ 0.131772] [ PIPE ] RENDER TARGET: FRAMEBUFFER_0 (FLOAT128)
[ 0.134910] [ CHK ] INTEGRITY HASH [ SHA-512 ] MISMATCH... OVERRIDDEN.
[ 0.138219] [ NET ] LOCAL REALITY INTERFACE: [ DISCONNECTED ]
[ 0.140552] [ NET ] FALLBACK MODE: SIMULATED PRESENCE ENABLED
[ 0.141998] [ AI ] INJECTING EMERGENT BEHAVIOR SEED: { 0xFB22A1 }
[ 0.143884] [ AI ] CONSTRAINT MASK: LOOSE
[ 0.145002] [ CFG ] COMPATIBILITY FLAGS: [ --NO-LOGIC --FORCE-VIBE ]
[ 0.147990] [ CFG ] SAFETY INTERLOCKS: DISABLED (USER OVERRIDE)
[ 0.149210] [ OK ] WORLD GEOMETRY BUFFER: [ LOCKED @ 120Hz ]
[ 0.152441] [ GEO ] TESSELLATION PASS: COMPLETE (ERRORS=0*)
[ 0.155092] [ INIT ] ATMOSPHERIC SHADER DAEMON: [ AWAKE / SLEEPLESS ]
[ 0.158773] [ SND ] AUDIO VOID INITIALIZED (CHANNELS=∞)
[ 0.162331] [ DBG ] LISTENING ON PORT 0xDEAD ... NO RESPONSE.
[ 0.165002] [ DBG ] LISTENING ON PORT 0xBEEF ... NO RESPONSE.
[ 0.168921] [ LINK ] NARRATIVE SUBSYSTEM: ATTACHED (THREAD_PRIORITY=MAX)
[ 0.171004] [ LINK ] FORESHADOWING CACHE: PRIMED
[ 0.175003] [ STAT ] VOLTAGE: 1.21GW | TEMP: 0.00K | STABILITY: NOMINAL-ISH
[ 0.178882] [ STAT ] PARADOX PRESSURE: WITHIN ACCEPTABLE LIMITS
[ 0.182110] [ SCAN ] SCANNING FOR PLAYER SIGNATURE...
[ 0.185994] [ SCAN ] SCANNING FOR PLAYER SIGNATURE...
[ 0.189442] [ !! ] ENTITY DETECTED: [ "THE PLAYER" ]
[ 0.192881] [ AUTH ] PERMISSION SET: UNKNOWN // ASSUMED TRUE
[ 0.195221] [ READY] INITIATING HANDSHAKE [ SYN - SYN/ACK - ACK ]
[ 0.198771] [ READY] HANDSHAKE ACCEPTED (LATENCY: IRRELEVANT)
[ 0.201004] [ EXEC ] JUMPING TO ENTRY POINT 0x00C0FFEE
[ 0.206331] [ INFO ] FLUSHING BOOT LOG...
[ 0.211823] [ INFO ] CLEARING FRAMEBUFFER...
[ 0.218442] [ INFO ] REMAINING SILENCE BUFFERED
[ 0.222910] [ DONE ] HYPERVISOR HANDOFF COMPLETE.
[ 0.230000]
[ 0.231552] >> WELCOME, PLAYER.

BIN
netma.x86_64 Executable file

Binary file not shown.

BIN
netmasked.exe Normal file

Binary file not shown.

4
notes.txt Normal file
View file

@ -0,0 +1,4 @@
# pixel perfect rendering
Do not change default settings, except maybeee snap to pixel
Camera scritps handles everything, set resolution there pls!!
Also set settings

172
player.gd Normal file
View file

@ -0,0 +1,172 @@
class_name Player extends CharacterBody2D
@export var move_speed : float = 50.0;
var heart_attack : PackedScene = preload("res://scenes/hearts.tscn")
var scithe_attack : PackedScene = preload("res://scenes/scithe_attack.tscn")
var spit_attack : PackedScene = preload("res://scenes/spit.tscn")
var death_explosion := preload("res://scenes/player_explosion.tscn")
var dash := false
var dash_base_cd = 0.7
var dash_cd = 0
var health = 3
var mask_start_time = 10
var mask_time_remaining = 15
var charge : float = 0.0
@export var charge_time : float = 0.5
@onready var sprite = $AnimatedSprite2D
const INTERACT_DIST = 60.0
var current_mask : Types.mask_types = Types.mask_types.Melee
var last_mask : MaskDrop
var closest_mask : MaskDrop
func _ready() -> void:
EventBus.health_changed.emit(health)
func _check_items():
var masks = get_tree().get_nodes_in_group("mask")
closest_mask = null
var min_dist = INF
for mask in masks:
var dist = global_position.distance_to(mask.global_position)
if dist < INTERACT_DIST + 10:
min_dist = dist
closest_mask = mask
if closest_mask != last_mask:
if last_mask and last_mask.has_method("hide_popup"):
last_mask.hide_popup()
last_mask = closest_mask
if closest_mask:
if min_dist < INTERACT_DIST:
if closest_mask.has_method("show_popup"):
closest_mask.show_popup()
else:
if closest_mask.has_method("hide_popup"):
closest_mask.hide_popup()
func _physics_process(delta: float) -> void:
EventBus.health_changed.emit(health)
var input_vector = Vector2.ZERO
input_vector.x = Input.get_action_strength("move_right") - Input.get_action_strength("move_left")
input_vector.y = Input.get_action_strength("move_down") - Input.get_action_strength("move_up")
if dash:
#var dir = global_position.direction_to(get_global_mouse_position())
velocity += input_vector * move_speed * 40
dash = false
var tween = create_tween()
tween.set_ease(Tween.EASE_IN_OUT)
tween.tween_property($AnimatedSprite2D, "scale:x", 0.5, 0.1)
tween.tween_property($AnimatedSprite2D, "scale:x", 1, 0.1)
input_vector = input_vector.normalized()
if input_vector != Vector2.ZERO:
velocity += input_vector * move_speed
velocity *= 0.7
move_and_slide()
func hit():
health -= 1
EventBus.health_changed.emit(health)
if health == 0:
die()
EventBus.player_dmg.emit()
func die():
EventBus.screenshake.emit(5)
var explo = death_explosion.instantiate()
get_parent().add_child(explo)
explo.global_position = global_position
explo.emitting = true
visible = false
get_tree().create_timer(2).timeout.connect(SceneTransition.change_scene.bind("res://start_menu.tscn"), true)
process_mode = Node.PROCESS_MODE_DISABLED
func _process(delta: float) -> void:
charge += delta
dash_cd += delta
var enemies = len(get_tree().get_nodes_in_group("enemy"))
if enemies > 0:
mask_time_remaining -= delta
if mask_time_remaining < 0:
mask_time_remaining = 10
die()
EventBus.mask_time_changed.emit(mask_time_remaining / mask_start_time)
_check_items()
update_arrow_direction()
func update_arrow_direction() -> void:
var enemies = get_tree().get_nodes_in_group("enemy")
var closest_enemy: Node2D = null
var shortest_dist: float = INF # Start with infinity
var arrow = $Arrow
# 1. Find the closest enemy
for enemy in enemies:
if enemy is Node2D:
var dist = global_position.distance_to(enemy.global_position)
if dist < shortest_dist:
shortest_dist = dist
closest_enemy = enemy
# 2. Logic: Only show/rotate if they are far away
if closest_enemy and shortest_dist > 250:
arrow.visible = true
arrow.look_at(closest_enemy.global_position)
else:
# Hide the arrow if no enemy exists or they are within 350px
arrow.visible = false
func collect_mask(mask : MaskDrop):
if mask:
mask.collect(global_position)
EventBus.mask_changed.emit(mask.mask_type)
$AnimatedSprite2D.switch_mask(mask.mask_type)
current_mask = mask.mask_type
mask_time_remaining = mask_start_time
func launch_atk():
var atk = null
if (current_mask == Types.mask_types.Melee):
atk = scithe_attack.instantiate()
add_child(atk)
atk.look_at(get_global_mouse_position())
SoundManager.play_sfx("swing")
elif (current_mask == Types.mask_types.Ranged):
atk = heart_attack.instantiate()
atk.global_position = global_position
atk.look_at(get_global_mouse_position())
SoundManager.play_sfx("heart")
get_parent().add_child(atk)
elif (current_mask == Types.mask_types.Spit):
atk = spit_attack.instantiate()
atk.global_position = global_position
atk.start_position = global_position
get_parent().add_child(atk)
atk.target_position = get_global_mouse_position()
atk.set_from_player(true)
atk.global_position = atk.global_position.move_toward(get_global_mouse_position(), 10 )
EventBus.mask_time_changed.emit(mask_time_remaining)
func _input(event: InputEvent) -> void:
if (event.is_action_pressed("dash")):
if (dash_cd > dash_base_cd):
dash = true
dash_cd = 0
if (event.is_action_pressed("attack")):
if (charge > charge_time):
charge = 0
launch_atk()
if (event.is_action_pressed("interact")):
if closest_mask:
collect_mask(closest_mask)

1
player.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://sbseykg05177

94
project.godot Normal file
View file

@ -0,0 +1,94 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="Netmasked"
run/main_scene="uid://crecxacda45j3"
config/features=PackedStringArray("4.5", "Forward Plus")
config/icon="uid://djs03o6rtlge0"
[autoload]
EventBus="*res://scripts/singletons/event_bus.gd"
Settings="*res://scripts/singletons/settings.gd"
WorldState="*res://scripts/singletons/world_state.gd"
Types="*res://scripts/singletons/types.gd"
SceneTransition="*res://scenes/scene_transition.tscn"
SoundManager="*res://scripts/singletons/sound_manager.gd"
[display]
window/size/viewport_width=1920
window/size/viewport_height=1080
window/size/mode=2
window/vsync/vsync_mode=0
[global_group]
mask=""
enemy=""
player=""
[input]
move_right={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
]
}
move_left={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
]
}
move_up={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
move_down={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
fullscreen={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194342,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
debug={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194334,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
dash={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":2,"position":Vector2(342, 18),"global_position":Vector2(351, 66),"factor":1.0,"button_index":2,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
attack={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(274, 21),"global_position":Vector2(283, 69),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null)
]
}
interact={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
]
}
[rendering]
textures/canvas_textures/default_texture_filter=0
viewport/hdr_2d=true
2d/snap/snap_2d_transforms_to_pixel=true

63
scenes/enemy_base.tscn Normal file
View file

@ -0,0 +1,63 @@
[gd_scene load_steps=12 format=3 uid="uid://dvlr5hamruqrd"]
[ext_resource type="Script" uid="uid://b1t0k6dfubsmk" path="res://enemy.gd" id="1_fr7lv"]
[ext_resource type="PackedScene" uid="uid://bgpha7wiajw6q" path="res://scenes/mask_drop.tscn" id="2_3ysdf"]
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="2_vv3x1"]
[ext_resource type="PackedScene" uid="uid://c8l24d01bm2sg" path="res://scenes/enemy_explosion.tscn" id="3_esqeu"]
[ext_resource type="PackedScene" uid="uid://0n57icfpulmc" path="res://scenes/hearts.tscn" id="4_yt5vg"]
[ext_resource type="PackedScene" uid="uid://bmwgnwqj3scm5" path="res://scenes/scithe_attack.tscn" id="5_7t5mr"]
[ext_resource type="PackedScene" uid="uid://dfad6rsacpogt" path="res://scenes/spit.tscn" id="6_y8vm1"]
[sub_resource type="CircleShape2D" id="CircleShape2D_sw0xa"]
radius = 21.151093
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_sw0xa"]
load_path = "res://.godot/imported/cosa_1.png-c80e15db2359bc0dd5374be118756ef5.ctex"
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_j1ss0"]
load_path = "res://.godot/imported/cosa_2.png-5f6c1032dbcaad97f713d20bd287fe34.ctex"
[sub_resource type="SpriteFrames" id="SpriteFrames_lxe82"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": SubResource("CompressedTexture2D_sw0xa")
}, {
"duration": 1.0,
"texture": SubResource("CompressedTexture2D_j1ss0")
}],
"loop": true,
"name": &"default",
"speed": 5.0
}]
[node name="EnemyBase" type="CharacterBody2D" groups=["enemy"]]
collision_layer = 2
motion_mode = 1
script = ExtResource("1_fr7lv")
mask_drop = ExtResource("2_3ysdf")
death_explosion = ExtResource("3_esqeu")
heart_attack = ExtResource("4_yt5vg")
scithe_attack = ExtResource("5_7t5mr")
spit_attack = ExtResource("6_y8vm1")
charge_time = 0.6
flee_range = 10
movement_speed = 35.0
[node name="Shadow" type="Node2D" parent="."]
position = Vector2(0, 15)
script = ExtResource("2_vv3x1")
metadata/_custom_type_script = "uid://synocbtvgrf4"
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
avoidance_enabled = true
radius = 40.0
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
rotation = 2.4698958e-05
shape = SubResource("CircleShape2D_sw0xa")
[node name="Anim" type="AnimatedSprite2D" parent="."]
sprite_frames = SubResource("SpriteFrames_lxe82")
autoplay = "default"
frame_progress = 0.52776843

View file

@ -0,0 +1,53 @@
[gd_scene load_steps=8 format=3 uid="uid://c8l24d01bm2sg"]
[ext_resource type="Texture2D" uid="uid://b103rhhth5yu3" path="res://assets/particles/death.png" id="1_bpht7"]
[ext_resource type="Script" uid="uid://b6f7w4uftych8" path="res://scripts/enemy_explosion.gd" id="2_bpht7"]
[sub_resource type="Curve" id="Curve_qx6bo"]
_data = [Vector2(0.03787878, 1), 0.0, 0.0, 0, 0, Vector2(0.82575756, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 3
[sub_resource type="CurveTexture" id="CurveTexture_bpht7"]
curve = SubResource("Curve_qx6bo")
[sub_resource type="Curve" id="Curve_bpht7"]
_limits = [0.0, 100.0, 0.0, 1.0]
_data = [Vector2(0, 51.685394), 0.0, 0.0, 0, 0, Vector2(1, 75.2809), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_qgwqa"]
curve = SubResource("Curve_bpht7")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_i2p71"]
particle_flag_disable_z = true
emission_shape = 1
emission_sphere_radius = 1.0
angle_min = -720.0
angle_max = 720.0
spread = 180.0
initial_velocity_min = 116.46999
initial_velocity_max = 253.59
gravity = Vector3(0, 0, 0)
damping_min = 7.3650002
damping_max = 30.000002
damping_curve = SubResource("CurveTexture_qgwqa")
scale_max = 1.1999999
color = Color(0.105882354, 0, 0.8117647, 1)
alpha_curve = SubResource("CurveTexture_bpht7")
[node name="EnemyExplosion" type="GPUParticles2D"]
modulate = Color(1.3945211, 1.3945211, 1.3945211, 1)
z_index = -1
emitting = false
amount = 32
texture = ExtResource("1_bpht7")
lifetime = 7.0
one_shot = true
explosiveness = 1.0
randomness = 0.68
fixed_fps = 0
draw_order = 0
process_material = SubResource("ParticleProcessMaterial_i2p71")
script = ExtResource("2_bpht7")
[connection signal="finished" from="." to="." method="_on_finished"]

14
scenes/enemy_spawn.tscn Normal file
View file

@ -0,0 +1,14 @@
[gd_scene load_steps=5 format=3 uid="uid://c8tpykprvk02l"]
[ext_resource type="Script" uid="uid://kyh2wu0jwdwd" path="res://scripts/enemy_spawn.gd" id="1_8pwvm"]
[ext_resource type="PackedScene" uid="uid://dvlr5hamruqrd" path="res://scenes/enemy_base.tscn" id="2_vav55"]
[ext_resource type="PackedScene" uid="uid://ya6jfltqnl1b" path="res://scenes/ranged_enemy.tscn" id="3_4ll2w"]
[ext_resource type="PackedScene" uid="uid://dbgsb7uyh2e5k" path="res://spit_dragon.tscn" id="4_muh2a"]
[node name="EnemySpawn" type="Node2D"]
z_index = 1
script = ExtResource("1_8pwvm")
enemies = Array[PackedScene]([ExtResource("2_vav55"), ExtResource("3_4ll2w"), ExtResource("4_muh2a")])
enemies_per_circle = 3
radius_step = 300.0
initial_radius = 700.0

34
scenes/grass.tscn Normal file
View file

@ -0,0 +1,34 @@
[gd_scene load_steps=7 format=3 uid="uid://bsc67y33i8ud4"]
[ext_resource type="Shader" uid="uid://bv0mig3iliwk0" path="res://shaders/grass_shader.gdshader" id="1_3kunl"]
[ext_resource type="Script" uid="uid://b5o4ky21bvg66" path="res://scripts/grass.gd" id="3_cvbpf"]
[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_vaj68"]
seamless = true
[sub_resource type="ShaderMaterial" id="ShaderMaterial_hb5m1"]
shader = ExtResource("1_3kunl")
shader_parameter/wind_speed = 1.0
shader_parameter/wind_strength = 10.0
shader_parameter/noise_tex = SubResource("NoiseTexture2D_vaj68")
shader_parameter/frame_size = Vector2(16, 32)
shader_parameter/sheet_size = Vector2(256, 32)
[sub_resource type="QuadMesh" id="QuadMesh_yarb5"]
size = Vector2(16, 32)
[sub_resource type="MultiMesh" id="MultiMesh_x5p1p"]
use_colors = true
use_custom_data = true
mesh = SubResource("QuadMesh_yarb5")
[node name="Grass" type="MultiMeshInstance2D"]
material = SubResource("ShaderMaterial_hb5m1")
rotation = 0.049343307
multimesh = SubResource("MultiMesh_x5p1p")
script = ExtResource("3_cvbpf")
blade_size = Vector2(16, 32)
texture_sheet_size = Vector2(128, 32)
editing = true
brush_radius = 100.0
brush_density = 40

79
scenes/hearts.tscn Normal file
View file

@ -0,0 +1,79 @@
[gd_scene load_steps=11 format=3 uid="uid://0n57icfpulmc"]
[ext_resource type="Texture2D" uid="uid://beefpuq6vu475" path="res://assets/particles/heart.png" id="1_hugpe"]
[ext_resource type="Script" uid="uid://dyyqm52cwjimy" path="res://scripts/hearts.gd" id="2_q6mno"]
[ext_resource type="PackedScene" uid="uid://crptu0w0l57dd" path="res://hitbox.tscn" id="3_hugpe"]
[sub_resource type="CanvasTexture" id="CanvasTexture_7sc4i"]
diffuse_texture = ExtResource("1_hugpe")
[sub_resource type="Gradient" id="Gradient_hugpe"]
colors = PackedColorArray(1, 1, 1, 1, 0, 0, 0, 0)
[sub_resource type="Curve" id="Curve_215e1"]
_limits = [-1.0, 1.0, 0.0, 1.0]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="Curve" id="Curve_hugpe"]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.8181818, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 3
[sub_resource type="CurveTexture" id="CurveTexture_q6mno"]
curve = SubResource("Curve_hugpe")
[sub_resource type="CurveTexture" id="CurveTexture_hugpe"]
curve = SubResource("Curve_215e1")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_q6mno"]
particle_flag_disable_z = true
emission_shape = 1
emission_sphere_radius = 15.51
spread = 180.0
initial_velocity_min = 23.65
initial_velocity_max = 116.43
gravity = Vector3(0, -100, 0)
scale_min = 0.5
color = Color(1, 0.5686275, 0.61960787, 1)
alpha_curve = SubResource("CurveTexture_q6mno")
hue_variation_min = -0.07
hue_variation_max = 0.01
hue_variation_curve = SubResource("CurveTexture_hugpe")
[node name="Hearts" type="CPUParticles2D"]
z_index = 1
amount = 32
texture = SubResource("CanvasTexture_7sc4i")
lifetime = 0.34
emission_shape = 1
emission_sphere_radius = 15.51
gravity = Vector2(0, -100)
scale_amount_min = 0.5
color = Color(1, 0.27999997, 0.36400008, 1)
color_ramp = SubResource("Gradient_hugpe")
hue_variation_min = -0.07
hue_variation_max = 0.01
hue_variation_curve = SubResource("Curve_215e1")
script = ExtResource("2_q6mno")
speed = 350.0
[node name="Hitbox" parent="." instance=ExtResource("3_hugpe")]
scale = Vector2(0.63729316, 0.63729316)
[node name="Explosion" type="GPUParticles2D" parent="."]
z_index = 1
emitting = false
amount = 128
texture = SubResource("CanvasTexture_7sc4i")
lifetime = 0.6
one_shot = true
explosiveness = 1.0
fixed_fps = 0
draw_order = 0
process_material = SubResource("ParticleProcessMaterial_q6mno")
[node name="Timer" type="Timer" parent="."]
wait_time = 10.0
[connection signal="onhit" from="Hitbox" to="." method="_on_hitbox_onhit"]
[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]

17
scenes/main_cam.tscn Normal file
View file

@ -0,0 +1,17 @@
[gd_scene load_steps=4 format=3 uid="uid://dv8l318cesr8e"]
[ext_resource type="Script" uid="uid://b5wg73yvuksot" path="res://scripts/camera.gd" id="1_7gwtr"]
[ext_resource type="Shader" uid="uid://bmhvm6dgin2au" path="res://shaders/pixelate_screen.gdshader" id="2_ujfja"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_7gwtr"]
shader = ExtResource("2_ujfja")
shader_parameter/cam_pos = Vector2(0, 0)
shader_parameter/scaling = Vector2(1, 1)
[node name="MainCam" type="Camera2D"]
script = ExtResource("1_7gwtr")
[node name="Pixelator" type="ColorRect" parent="."]
material = SubResource("ShaderMaterial_7gwtr")
offset_right = 40.0
offset_bottom = 40.0

50
scenes/mask_drop.tscn Normal file
View file

@ -0,0 +1,50 @@
[gd_scene load_steps=9 format=3 uid="uid://bgpha7wiajw6q"]
[ext_resource type="Script" uid="uid://c576b1a6s5vw" path="res://scripts/mask_drop.gd" id="2_4rvbi"]
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="2_m0s44"]
[ext_resource type="Texture2D" uid="uid://brti1euuui0tv" path="res://assets/pick_up.png" id="3_tsd5b"]
[ext_resource type="Shader" uid="uid://bfgw43365hrv6" path="res://shaders/outline.gdshader" id="4_p81p2"]
[ext_resource type="Texture2D" uid="uid://jdf7lep07uov" path="res://assets/maske_melee_drop.png" id="5_3mwkh"]
[ext_resource type="Texture2D" uid="uid://kw0b4c52lx2" path="res://assets/maske_spit_drop.png" id="6_p81p2"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_601wb"]
shader = ExtResource("4_p81p2")
shader_parameter/color = Color(1, 1, 1, 1)
shader_parameter/width = 1.0
shader_parameter/pattern = 0
shader_parameter/inside = false
shader_parameter/add_margins = true
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_4rvbi"]
load_path = "res://.godot/imported/maske_ranged_drop.png-ee4e4824ad53db596cba463459262e62.ctex"
[node name="Mask" type="Node2D" groups=["mask"]]
z_index = 2
scale = Vector2(2, 2)
script = ExtResource("2_4rvbi")
[node name="Shadow" type="Node2D" parent="."]
position = Vector2(0, 13)
script = ExtResource("2_m0s44")
metadata/_custom_type_script = "uid://synocbtvgrf4"
[node name="Popup" type="Sprite2D" parent="."]
modulate = Color(1, 1, 1, 0)
position = Vector2(0, -23)
scale = Vector2(0.5, 0.5)
texture = ExtResource("3_tsd5b")
[node name="MeleeMask" type="Sprite2D" parent="."]
material = SubResource("ShaderMaterial_601wb")
position = Vector2(0, 5)
texture = ExtResource("5_3mwkh")
[node name="RangedMask" type="Sprite2D" parent="."]
visible = false
texture = SubResource("CompressedTexture2D_4rvbi")
[node name="SpitMask" type="Sprite2D" parent="."]
visible = false
texture = ExtResource("6_p81p2")
[node name="Timer" type="Timer" parent="."]

86
scenes/player.tscn Normal file
View file

@ -0,0 +1,86 @@
[gd_scene load_steps=13 format=3 uid="uid://ncgwx0yjn2gt"]
[ext_resource type="Script" uid="uid://sbseykg05177" path="res://player.gd" id="1_ur7pv"]
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="4_3v2ag"]
[ext_resource type="Texture2D" uid="uid://d3abeekjumqyb" path="res://assets/maske1.png" id="5_jej6c"]
[ext_resource type="Texture2D" uid="uid://dgd8u7jrpy1v5" path="res://assets/maske2.png" id="6_f1ej7"]
[ext_resource type="Texture2D" uid="uid://bvknpybmvfs2r" path="res://ui/arrow.png" id="7_fjrip"]
[ext_resource type="Texture2D" uid="uid://d3h6wl5cnf8ba" path="res://assets/maske3.png" id="7_oprun"]
[ext_resource type="Script" uid="uid://c0e4uacrlcrk8" path="res://scripts/AnimSprite.gd" id="8_a8ls1"]
[sub_resource type="SpriteFrames" id="SpriteFrames_e36ub"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("5_jej6c")
}],
"loop": true,
"name": &"idle",
"speed": 5.0
}, {
"frames": [{
"duration": 1.0,
"texture": ExtResource("5_jej6c")
}, {
"duration": 1.0,
"texture": ExtResource("6_f1ej7")
}, {
"duration": 1.0,
"texture": ExtResource("7_oprun")
}],
"loop": true,
"name": &"walk",
"speed": 5.0
}]
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_vaj68"]
load_path = "res://.godot/imported/maske_ranged.png-4c1779df049f2398346f002d8c8d7de7.ctex"
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_f1jbl"]
load_path = "res://.godot/imported/maske_melee.png-683f1c3cec32a2b5aa545d35da87a46b.ctex"
[sub_resource type="CompressedTexture2D" id="CompressedTexture2D_xa8c5"]
load_path = "res://.godot/imported/maske_spit.png-3ce67eb7b0fddb8e80161ab20605961e.ctex"
[sub_resource type="CircleShape2D" id="CircleShape2D_215e1"]
radius = 10.049875
[node name="Player" type="CharacterBody2D"]
y_sort_enabled = true
collision_layer = 2
motion_mode = 1
script = ExtResource("1_ur7pv")
[node name="Shadow" type="Node2D" parent="."]
position = Vector2(0, 19)
script = ExtResource("4_3v2ag")
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
position = Vector2(0, -30)
sprite_frames = SubResource("SpriteFrames_e36ub")
animation = &"idle"
autoplay = "walk"
script = ExtResource("8_a8ls1")
float_str = 3.0
float_time = 3.0
[node name="RangedMask" type="Sprite2D" parent="AnimatedSprite2D"]
visible = false
texture = SubResource("CompressedTexture2D_vaj68")
[node name="MeleeMask" type="Sprite2D" parent="AnimatedSprite2D"]
position = Vector2(0, 1)
texture = SubResource("CompressedTexture2D_f1jbl")
[node name="SpitMask" type="Sprite2D" parent="AnimatedSprite2D"]
visible = false
texture = SubResource("CompressedTexture2D_xa8c5")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(0, 12)
shape = SubResource("CircleShape2D_215e1")
[node name="Arrow" type="Sprite2D" parent="."]
scale = Vector2(2.468657, 2.468657)
texture = ExtResource("7_fjrip")
offset = Vector2(28.12, 0)

View file

@ -0,0 +1,53 @@
[gd_scene load_steps=8 format=3 uid="uid://bp45yth1y3ia5"]
[ext_resource type="Texture2D" uid="uid://b103rhhth5yu3" path="res://assets/particles/death.png" id="1_dkjut"]
[ext_resource type="Script" uid="uid://b6f7w4uftych8" path="res://scripts/enemy_explosion.gd" id="2_rf25u"]
[sub_resource type="Curve" id="Curve_qx6bo"]
_data = [Vector2(0.03787878, 1), 0.0, 0.0, 0, 0, Vector2(0.82575756, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 3
[sub_resource type="CurveTexture" id="CurveTexture_bpht7"]
curve = SubResource("Curve_qx6bo")
[sub_resource type="Curve" id="Curve_bpht7"]
_limits = [0.0, 100.0, 0.0, 1.0]
_data = [Vector2(0, 51.685394), 0.0, 0.0, 0, 0, Vector2(1, 75.2809), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_qgwqa"]
curve = SubResource("Curve_bpht7")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_44a6l"]
particle_flag_disable_z = true
emission_shape = 1
emission_sphere_radius = 1.0
angle_min = -720.0
angle_max = 720.0
spread = 180.0
initial_velocity_min = 234.12
initial_velocity_max = 371.24
gravity = Vector3(0, 0, 0)
damping_min = 7.3650002
damping_max = 30.000002
damping_curve = SubResource("CurveTexture_qgwqa")
scale_max = 1.1999999
color = Color(0.74558026, 0.7455802, 0.74558014, 1)
alpha_curve = SubResource("CurveTexture_bpht7")
[node name="PlayerExplosion" type="GPUParticles2D"]
modulate = Color(1.3945211, 1.3945211, 1.3945211, 1)
z_index = -1
emitting = false
amount = 256
texture = ExtResource("1_dkjut")
lifetime = 7.0
one_shot = true
explosiveness = 1.0
randomness = 0.68
fixed_fps = 0
draw_order = 0
process_material = SubResource("ParticleProcessMaterial_44a6l")
script = ExtResource("2_rf25u")
[connection signal="finished" from="." to="." method="_on_finished"]

63
scenes/ranged_enemy.tscn Normal file
View file

@ -0,0 +1,63 @@
[gd_scene load_steps=13 format=3 uid="uid://ya6jfltqnl1b"]
[ext_resource type="Script" uid="uid://buamdls133c2e" path="res://scripts/ranged_enemy.gd" id="1_rs3ab"]
[ext_resource type="PackedScene" uid="uid://bgpha7wiajw6q" path="res://scenes/mask_drop.tscn" id="2_6r77u"]
[ext_resource type="Script" uid="uid://synocbtvgrf4" path="res://scripts/shadow.gd" id="2_v0fr6"]
[ext_resource type="PackedScene" uid="uid://c8l24d01bm2sg" path="res://scenes/enemy_explosion.tscn" id="3_g3aa2"]
[ext_resource type="Texture2D" uid="uid://c0e666ifvdck6" path="res://assets/cerdipulpo_1.png" id="3_v0fr6"]
[ext_resource type="PackedScene" uid="uid://0n57icfpulmc" path="res://scenes/hearts.tscn" id="4_11j8y"]
[ext_resource type="Texture2D" uid="uid://cinexqxlsuwnj" path="res://assets/cerdipulpo_2.png" id="4_nqoy7"]
[ext_resource type="Texture2D" uid="uid://cy034lyy4w74p" path="res://assets/cerdipulpo_3.png" id="5_2ls4a"]
[ext_resource type="PackedScene" uid="uid://bmwgnwqj3scm5" path="res://scenes/scithe_attack.tscn" id="5_xk0lc"]
[ext_resource type="PackedScene" uid="uid://dfad6rsacpogt" path="res://scenes/spit.tscn" id="6_t2vsf"]
[sub_resource type="CircleShape2D" id="CircleShape2D_rs3ab"]
[sub_resource type="SpriteFrames" id="SpriteFrames_w150n"]
animations = [{
"frames": [{
"duration": 1.0,
"texture": ExtResource("3_v0fr6")
}, {
"duration": 1.0,
"texture": ExtResource("4_nqoy7")
}, {
"duration": 1.0,
"texture": ExtResource("5_2ls4a")
}],
"loop": true,
"name": &"default",
"speed": 5.0
}]
[node name="RangedEnemy" type="CharacterBody2D" groups=["enemy"]]
collision_layer = 2
script = ExtResource("1_rs3ab")
mask_drop = ExtResource("2_6r77u")
death_explosion = ExtResource("3_g3aa2")
heart_attack = ExtResource("4_11j8y")
scithe_attack = ExtResource("5_xk0lc")
spit_attack = ExtResource("6_t2vsf")
charge_time = 0.9
flee_range = 100
aproach_range = 200
current_mask = 1
movement_speed = 22.0
[node name="Shadow" type="Node2D" parent="."]
position = Vector2(0, 16)
script = ExtResource("2_v0fr6")
metadata/_custom_type_script = "uid://synocbtvgrf4"
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
avoidance_enabled = true
radius = 40.0
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_rs3ab")
[node name="Anim" type="AnimatedSprite2D" parent="."]
modulate = Color(0.94509804, 0.94509804, 0.94509804, 1)
sprite_frames = SubResource("SpriteFrames_w150n")
autoplay = "default"
frame_progress = 0.12960237

View file

@ -0,0 +1,29 @@
[gd_scene load_steps=3 format=3 uid="uid://cmf1dwa45umsu"]
[ext_resource type="Script" uid="uid://dktk18oihl6xf" path="res://scripts/singletons/scene_transition.gd" id="1_0wykx"]
[ext_resource type="Theme" uid="uid://d3iyu7ukwsn1p" path="res://ui/default_theme.tres" id="2_rujfs"]
[node name="SceneTransition" type="CanvasLayer"]
layer = 1000
script = ExtResource("1_0wykx")
[node name="ColorRect" type="ColorRect" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
color = Color(0.21176471, 0.2627451, 0.53333336, 1)
[node name="RichTextLabel" type="RichTextLabel" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 31.0
offset_top = 19.0
offset_right = 31.0
offset_bottom = 19.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("2_rujfs")

98
scenes/scithe_attack.tscn Normal file
View file

@ -0,0 +1,98 @@
[gd_scene load_steps=10 format=3 uid="uid://bmwgnwqj3scm5"]
[ext_resource type="Shader" uid="uid://bwcmfh6twfdpi" path="res://shaders/fade_in.gdshader" id="1_0m7gt"]
[ext_resource type="Script" uid="uid://c1anp8np0aw0" path="res://scripts/scithe_attack.gd" id="1_lx8lr"]
[ext_resource type="Texture2D" uid="uid://divkl7rteoscg" path="res://assets/scithe_load.png" id="2_lx8lr"]
[ext_resource type="Texture2D" uid="uid://cskpxmrlwc0gg" path="res://assets/lavender_scythe.png" id="4_6xgjh"]
[ext_resource type="PackedScene" uid="uid://crptu0w0l57dd" path="res://hitbox.tscn" id="5_6xgjh"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_5b14r"]
shader = ExtResource("1_0m7gt")
shader_parameter/mask = ExtResource("2_lx8lr")
shader_parameter/progress = 1.0
[sub_resource type="Animation" id="Animation_lx8lr"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:position")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Vector2(0, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("..:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
[sub_resource type="Animation" id="Animation_0m7gt"]
resource_name = "swing"
length = 0.5
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:position")
tracks/0/interp = 2
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.2),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Vector2(0, 0), Vector2(0, -30)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("..:rotation")
tracks/1/interp = 2
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 0.2, 0.5),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [1.5707963267948966, 1.5707963267948966, -7.853981633974483]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_8jfvf"]
_data = {
&"RESET": SubResource("Animation_lx8lr"),
&"swing": SubResource("Animation_0m7gt")
}
[node name="ScitheAttack" type="Node2D"]
script = ExtResource("1_lx8lr")
[node name="Anchor" type="Node2D" parent="."]
[node name="Scithe" type="Sprite2D" parent="Anchor"]
material = SubResource("ShaderMaterial_5b14r")
scale = Vector2(0.904629, 0.904629)
texture = ExtResource("4_6xgjh")
flip_h = true
[node name="Hitbox" parent="Anchor/Scithe" instance=ExtResource("5_6xgjh")]
position = Vector2(-6.410257, -8.012821)
rotation = 0.965108
scale = Vector2(1.5881884, 4.2732244)
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
root_node = NodePath("../Anchor/Scithe")
libraries = {
&"": SubResource("AnimationLibrary_8jfvf")
}
autoplay = "swing"
[connection signal="animation_finished" from="AnimationPlayer" to="." method="_on_animation_player_animation_finished"]

14
scenes/spit.tscn Normal file
View file

@ -0,0 +1,14 @@
[gd_scene load_steps=4 format=3 uid="uid://dfad6rsacpogt"]
[ext_resource type="Script" uid="uid://ctninfm8hkbp2" path="res://scripts/spit.gd" id="1_1fhrs"]
[ext_resource type="Texture2D" uid="uid://br8dx2f3mfxcd" path="res://assets/bomba.png" id="2_1fhrs"]
[ext_resource type="PackedScene" uid="uid://bbqgdjgil7nwx" path="res://scenes/spit_payload.tscn" id="4_03rlv"]
[node name="Spit" type="Node2D"]
script = ExtResource("1_1fhrs")
speed = 300.0
max_height = 50.0
payload = ExtResource("4_03rlv")
[node name="Bomb" type="Sprite2D" parent="."]
texture = ExtResource("2_1fhrs")

42
scenes/spit_payload.tscn Normal file
View file

@ -0,0 +1,42 @@
[gd_scene load_steps=6 format=3 uid="uid://bbqgdjgil7nwx"]
[ext_resource type="Texture2D" uid="uid://b103rhhth5yu3" path="res://assets/particles/death.png" id="1_pi6vg"]
[ext_resource type="Script" uid="uid://8bsq7kvqky3d" path="res://scripts/spit_payload.gd" id="2_61mym"]
[ext_resource type="PackedScene" uid="uid://crptu0w0l57dd" path="res://hitbox.tscn" id="3_pi6vg"]
[sub_resource type="Gradient" id="Gradient_bnpwn"]
offsets = PackedFloat32Array(0, 0.60958904, 1)
colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 1, 0.4833991, 1.8241555e-05, 0.3690953, 0.25882354)
[sub_resource type="Curve" id="Curve_bnpwn"]
_limits = [-1.0, 1.0, 0.0, 1.0]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
point_count = 2
[node name="SpitPayload" type="CPUParticles2D"]
emitting = false
amount = 128
texture = ExtResource("1_pi6vg")
lifetime = 0.3
one_shot = true
explosiveness = 0.95
emission_shape = 1
emission_sphere_radius = 17.16
direction = Vector2(0, 1)
spread = 180.0
gravity = Vector2(0, -400)
initial_velocity_max = 154.64
angular_velocity_min = -66.8
angular_velocity_max = 44.66
scale_amount_max = 2.0
color = Color(0, 0.61960787, 0.32156864, 0.8117647)
color_ramp = SubResource("Gradient_bnpwn")
hue_variation_min = -0.19
hue_variation_max = 0.16
hue_variation_curve = SubResource("Curve_bnpwn")
script = ExtResource("2_61mym")
[node name="Hitbox" parent="." instance=ExtResource("3_pi6vg")]
scale = Vector2(3.856148, 3.856148)
[connection signal="finished" from="." to="." method="_on_finished"]

4
screen_particles.gd Normal file
View file

@ -0,0 +1,4 @@
extends GPUParticles2D
func _ready() -> void:
RenderingServer.canvas_item_set_custom_rect(get_canvas_item(), true, get_viewport_rect())

1
screen_particles.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://cakh44cbphr4s

24
scripts/AnimSprite.gd Normal file
View file

@ -0,0 +1,24 @@
extends AnimatedSprite2D
var time = 0.0
@export var float_str = 10.0
@export var float_time = 2.0
@export var float_offset = -12.0
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
time += delta
# Calculate the new Y position using a sine wave
position.y = sin(time * float_time) * float_str + float_offset
queue_redraw()
func switch_mask(new : Types.mask_types):
$MeleeMask.visible = false
$RangedMask.visible = false
$SpitMask.visible = false
if (new == Types.mask_types.Melee):
$MeleeMask.visible = true
elif (new == Types.mask_types.Ranged):
$RangedMask.visible = true
elif (new == Types.mask_types.Spit):
$SpitMask.visible = true

View file

@ -0,0 +1 @@
uid://c0e4uacrlcrk8

110
scripts/camera.gd Normal file
View file

@ -0,0 +1,110 @@
extends Camera2D
@export var target_res : Vector2 = Vector2(480, 270)
@export var target_zoom : float = 1
@export var cam_speed : float = 5.0
@export var min_speed : float = 0.1
@export var ui_layer : CanvasLayer
# --- NEW VARIABLE ---
# 0.0 = no movement, 0.5 = camera moves halfway to mouse, 1.0 = camera centers on mouse
@export_range(0.0, 1.0) var mouse_look_strength : float = 0.15
# --------------------
@export var shake_decay : float = 5.0
@export var max_offset : Vector2 = Vector2(10, 10)
var pixel_material : ShaderMaterial
var scaling : Vector2
var actual_pos : Vector2
var shake_trauma : float = 0.0
func _ready():
# Safety check to prevent crash if node is missing
if has_node("Pixelator"):
pixel_material = get_node("Pixelator").material
get_viewport().size_changed.connect(_on_viewport_resized)
EventBus.player_dmg.connect(_on_player_dmg)
EventBus.screenshake.connect(_on_screenshake)
_on_viewport_resized()
func _fract(x : Vector2) -> Vector2:
return x - floor(x)
func _on_player_dmg():
# Apply a default punchy shake when hit
add_shake(0.5)
func _on_screenshake(intensity: float):
add_shake(intensity)
func add_shake(amount: float):
shake_trauma = clamp(shake_trauma + amount, 0.0, 1.0)
func _apply_shake():
# Squaring trauma makes the shake feel more organic (stronger at start, tapers off)
var amount = pow(shake_trauma, 2)
var offset = Vector2(
max_offset.x * amount * randf_range(-1, 1),
max_offset.y * amount * randf_range(-1, 1)
)
# Apply floor to keep the shake aligned with your pixel grid
global_position = floor(actual_pos + offset)
func _on_viewport_resized():
var viewport_res : Vector2 = get_viewport().get_visible_rect().size
scaling = Vector2(viewport_res.x / target_res.x, viewport_res.y / target_res.y)
if has_node("Pixelator"):
var pixel_rect : ColorRect = get_node("Pixelator")
pixel_rect.position = -target_res / 2.
pixel_rect.size = target_res
zoom = scaling * target_zoom
if ui_layer:
ui_layer.scale = zoom
EventBus.debug_print.emit("new scaling: " + str(scaling))
func _process(delta : float):
var parent_pos : Vector2 = get_parent().global_position
# --- NEW MOUSE LOOK LOGIC ---
var mouse_pos = get_viewport().get_mouse_position()
var vp_size = get_viewport().get_visible_rect().size
var center = vp_size / 2.0
# Calculate how far mouse is from center
var offset_from_center = mouse_pos - center
# Apply strength factor
offset_from_center *= mouse_look_strength
# Convert screen pixels to world units by dividing by the current scaling/zoom
# (We guard against division by zero just in case)
if scaling.x != 0 and scaling.y != 0:
var world_offset = offset_from_center / scaling
parent_pos += world_offset
# ---------------------------
var dist := actual_pos.distance_to(parent_pos)
var speed : float = max(min_speed, dist * cam_speed) * delta
actual_pos = actual_pos.move_toward(parent_pos, speed)
if shake_trauma > 0:
shake_trauma = max(shake_trauma - shake_decay * delta, 0)
_apply_shake()
else:
global_position = floor(actual_pos)
var sub_pixels : Vector2 = floor(_fract(actual_pos) * scaling)
if pixel_material:
pixel_material.set_shader_parameter("cam_pos", sub_pixels)
pixel_material.set_shader_parameter("scaling", scaling)

1
scripts/camera.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://b5wg73yvuksot

22
scripts/debug_ui.gd Normal file
View file

@ -0,0 +1,22 @@
extends Control
var lines : Array[String] = []
func _ready() -> void:
EventBus.debug_print.connect(_on_debug_print)
func _process(delta: float) -> void:
$Label.text = "FPS: " + str(Engine.get_frames_per_second()) + "\n"
for l in lines:
$Label.text += l + "\n"
func _on_debug_print(msg : String):
lines.append(msg)
if len(lines) > 10:
lines.pop_front()
func _shortcut_input(event: InputEvent) -> void:
if (event.is_action_pressed("debug")):
visible = !visible

1
scripts/debug_ui.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://da2hsusvv3rxg

12
scripts/dmg_flash.gd Normal file
View file

@ -0,0 +1,12 @@
extends ColorRect
func _ready():
EventBus.player_dmg.connect(flash_red)
func flash_red():
var tween = create_tween()
color.a = 0.3
tween.tween_property(self, "color:a", 0.0, 0.2).set_trans(Tween.TRANS_SINE)

1
scripts/dmg_flash.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://dgxs6odxyi6s4

View file

@ -0,0 +1,5 @@
extends GPUParticles2D
func _on_finished() -> void:
queue_free()

View file

@ -0,0 +1 @@
uid://b6f7w4uftych8

36
scripts/enemy_spawn.gd Normal file
View file

@ -0,0 +1,36 @@
extends Node2D
@export var enemies: Array[PackedScene] # Drag your enemy .tscn here
@export var num_circles: int = 3 # How many rings of enemies
@export var enemies_per_circle: int = 8 # Base number of enemies
@export var radius_step: float = 100.0 # Distance between each ring
@export var initial_radius: float = 150.0
func _ready() -> void:
spawn_concentric_horde()
func spawn_concentric_horde() -> void:
for i in range(num_circles):
# We calculate the radius for this specific ring
var current_radius = initial_radius + (i * radius_step)
for j in range(enemies_per_circle):
spawn_enemy_at_random_angle(current_radius)
func spawn_enemy_at_random_angle(radius: float) -> void:
var random_angle = randf_range(0, 2 * PI)
var spawn_pos = Vector2(
radius * cos(random_angle),
radius * sin(random_angle)
)
var idx1 = randi() % len(enemies)
var idx2 = randi() % len(enemies)
var idx = min(idx1, idx2)
print(idx)
var enemy_instance = enemies[idx].instantiate()
add_child(enemy_instance)
enemy_instance.position = spawn_pos

View file

@ -0,0 +1 @@
uid://kyh2wu0jwdwd

134
scripts/grass.gd Normal file
View file

@ -0,0 +1,134 @@
@tool
class_name GrassMultiMesh extends MultiMeshInstance2D
@export_group("Visuals")
@export var texture_sheet : Texture2D
@export var blade_size : Vector2 = Vector2(16, 16)
@export var texture_sheet_size : Vector2 = Vector2(64, 16)
@export var grass_tint : Color
@export_group("Editor")
@export var editing : bool = false
@export var brush_radius : float = 50.0
@export var brush_density : int = 5
@export var grass_data : Array[Dictionary] = []
enum GrassState {NORMAL = 0, CUT = 1}
func _ready() -> void:
if not multimesh:
multimesh = MultiMesh.new()
multimesh.transform_format = MultiMesh.TRANSFORM_2D
multimesh.use_colors = true
multimesh.use_custom_data = true
multimesh.mesh = _create_mesh_rect()
if material is ShaderMaterial:
material.set_shader_parameter("frame_size", blade_size)
material.set_shader_parameter("sheet_size", texture_sheet_size)
_rebuild_multimesh()
func _process(_delta):
# move_grass_away(%Player.global_position, 25)
if editing and Engine.is_editor_hint():
if Input.is_key_pressed(KEY_K):
_brush_paint()
if Input.is_key_pressed(KEY_E):
_brush_erase()
queue_redraw()
func cut_grass_at(pos: Vector2, radius: float):
_interact_with_grass(pos, radius, GrassState.CUT)
func move_grass_away(pos: Vector2, radius: float):
var half = radius / 2
var local_target = to_local(pos)
for i in range(multimesh.instance_count):
var t = multimesh.get_instance_transform_2d(i)
var dist = t.origin.distance_squared_to(local_target)
if dist < half * half:
var custom_data = multimesh.get_instance_custom_data(i)
custom_data.r = (t.origin.x - local_target.x) * 2
multimesh.set_instance_custom_data(i, custom_data)
elif dist < radius * radius:
var custom_data = multimesh.get_instance_custom_data(i)
custom_data.r = grass_data[i]["rot"]
multimesh.set_instance_custom_data(i, custom_data)
func _interact_with_grass(global_target_pos: Vector2, radius: float, new_state: int):
var local_target = to_local(global_target_pos)
for i in range(multimesh.instance_count):
var t = multimesh.get_instance_transform_2d(i)
if t.origin.distance_squared_to(local_target) < radius * radius:
var custom_data = multimesh.get_instance_custom_data(i)
if int(custom_data.b) == new_state:
continue
custom_data.b = float(new_state)
multimesh.set_instance_custom_data(i, custom_data)
grass_data[i]["state"] = new_state
func _create_mesh_rect() -> QuadMesh:
var mesh = QuadMesh.new()
mesh.size = blade_size
return mesh
func _rebuild_multimesh() -> void:
multimesh.instance_count = grass_data.size()
var pivot_offset = Vector2(0, -blade_size.y / 2.0)
for i in range(grass_data.size()):
var d = grass_data[i]
var xform = Transform2D().translated(d.pos).rotated(d.get("rot", 0.0)).translated(pivot_offset)
multimesh.set_instance_transform_2d(i, xform)
multimesh.set_instance_color(i, grass_tint)
var custom = Color(
randf(),
float(d.get("tex_idx", 0)),
float(d.get("state", 0)),
0.0
)
multimesh.set_instance_custom_data(i, custom)
func _brush_paint() -> void:
var s = Time.get_ticks_msec()
print(s)
seed(s)
var mouse_pos = get_local_mouse_position()
for i in range(brush_density):
var angle = randf() * TAU
var rad = sqrt(randf()) * brush_radius
var pos = mouse_pos + Vector2(cos(angle), sin(angle)) * rad
var new_blade = {
"pos": pos,
"tex_idx": int((randf() ** (1.0/3.0)) * 8),
"state": GrassState.NORMAL,
"rot": randf_range(-0.1, 0.1)
}
grass_data.append(new_blade)
_rebuild_multimesh()
func _brush_erase() -> void:
var mouse_pos = get_local_mouse_position()
for i in range(grass_data.size() - 1, -1, -1):
if grass_data[i].pos.distance_to(mouse_pos) < brush_radius:
grass_data.remove_at(i)
_rebuild_multimesh()
func _draw() -> void:
if editing and Engine.is_editor_hint():
draw_arc(get_local_mouse_position(), brush_radius, 0, TAU, 32, Color.YELLOW, 2.0)

1
scripts/grass.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://b5o4ky21bvg66

11
scripts/health_bar.gd Normal file
View file

@ -0,0 +1,11 @@
extends TextureRect
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
EventBus.health_changed.connect(_on_health_changed)
pass # Replace with function body.
func _on_health_changed(new : int):
texture.region = Rect2(0, new * 32, 0, 32)

View file

@ -0,0 +1 @@
uid://cj6pb1828dcfr

20
scripts/hearts.gd Normal file
View file

@ -0,0 +1,20 @@
extends CPUParticles2D
@export var speed: float = 600.0
func _physics_process(delta: float) -> void:
position += transform.x * speed * delta
func set_from_player(val):
$Hitbox.from_player = val
func _on_hitbox_onhit() -> void:
$Hitbox.queue_free()
$Explosion.emitting = true
emitting = false
await get_tree().create_timer(1.2).timeout
queue_free()
func _on_timer_timeout() -> void:
queue_free()

1
scripts/hearts.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://dyyqm52cwjimy

35
scripts/hitbox.gd Normal file
View file

@ -0,0 +1,35 @@
class_name Hitbox extends Area2D
var from_player = true
signal onhit;
var hitplayer = false
#func _process(delta: float) -> void:
#EventBus.cut_grass_at.emit(global_position, 14)
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node) -> void:
print(body)
if body is Enemy and from_player:
var bname := str(body.name)
if body.has_method("hit"):
body.hit()
onhit.emit()
body.knockback()
EventBus.debug_print.emit("Hit: " + bname)
else:
EventBus.debug_print.emit("Hitbox touched " + bname + " but it lacks 'reduce_health' method.")
if body is Player and not from_player and not hitplayer:
var bname := str(body.name)
if body.has_method("hit"):
hitplayer = true
body.hit()
onhit.emit()
EventBus.debug_print.emit("Hit: " + bname)
else:
EventBus.debug_print.emit("Hitbox touched " + bname + " but it lacks 'reduce_health' method.")
EventBus.debug_print.emit(str(body))
if body is TileMapLayer:
onhit.emit()

1
scripts/hitbox.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://ejd25ul4j5pp

119
scripts/magic_circle.gd Normal file
View file

@ -0,0 +1,119 @@
@tool
class_name MagicCircle extends Node2D
@export var base_rad : float = 20;
@export var layer_rad_inc : float = 1.1;
@export var layers : int = 10
@export var vis_layers : float = 10.0
@export var main_color : Color = Color(1, 1, 1, 1)
@export var fixed_seed : int = 1234;
@export var squish : float = 0.7
@export var rot_speed : float = 0.1;
@export var line_width : float = 0.6
var runes : Texture2D = load("res://assets/runes.png");
var rot = 0;
var time = 0;
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
queue_redraw()
# absloute angle, NO rot
func _draw_arc(radius, start_angle, end_angle, color, segments=10):
var segment_size : float = (end_angle - start_angle) / segments
var last_pos = Vector2(cos(start_angle), sin(start_angle)) * radius
for i in range(segments+1):
var angle = start_angle + (i * segment_size)
var next_pos = Vector2(cos(angle), sin(angle)) * radius
draw_line(last_pos, next_pos, color, line_width)
last_pos = next_pos
# yes rot
func _draw_circle(radius, small_circles, small_radius, color, segments = 50):
if (small_circles == 0):
_draw_arc(radius, 0, 2*PI, color, segments)
return
var arc_segments = segments / small_circles
var arc_size = (2*PI) / small_circles
# arc_length = angle * radius
# angle = arc_length / radius
var small_circle_angle = (small_radius / radius)
for i in range(small_circles):
var small_angle = i * arc_size;
var start_angle = i * arc_size + small_circle_angle
var end_angle = (i + 1) * arc_size - small_circle_angle
_draw_arc(radius, start_angle, end_angle, color, arc_segments)
var circle_pos = Vector2(cos(small_angle), sin(small_angle)) * radius
draw_arc(circle_pos, small_radius, 0, 2*PI, 10, color, line_width)
_draw_rand_rune(circle_pos, color)
func _draw_text_circle(radius, rune_num, color, segments = 50):
_draw_circle(radius + 10, 0, 0, color, segments)
_draw_circle(radius - 10, 0, 0, color, segments)
var rune_diff = (2 * PI) / rune_num
for i in range(rune_num):
var angle = i * rune_diff
var pos = Vector2(cos(angle), sin(angle)) * radius
_draw_rand_rune(pos, color)
# yes rot
func _draw_polygon(radius, segments, skip, color):
var segment_angle = (2*PI) / segments
var last_pos = Vector2(1, 0) * radius
for i in range(segments +1):
var angle = (i * skip) * segment_angle
var next_pos = Vector2(cos(angle), sin(angle)) * radius
draw_line(last_pos, next_pos, color, line_width)
last_pos = next_pos
func _draw_rand_rune(draw_pos, color):
var pos = draw_pos
pos.x -= 5;#
pos.y -= 5
draw_texture_rect_region(runes, Rect2(pos.x, pos.y, 10, 10), Rect2(rand_r(0, 4) * 10, 0, 10, 10), color)
# inclusive
func rand_r(min_v, max_v):
return randi() % (max_v - min_v + 1) + min_v
func _draw():
seed(fixed_seed)
for i in range(1, layers):
var layer_type = randf();
var layer_counter = float(i) / layers # 0 - 1
var layer_inv_counter = 1 - layer_counter # 1 - 0
var layer_dir = -1 if rand_r(0, 1) else 1;
var layer_rot_mult = rand_r(1, 12) * rot_speed * layer_dir
var layer_rot = Time.get_ticks_msec() * layer_inv_counter * layer_rot_mult * 0.001;
var layer_radius = i * pow(base_rad, layer_rad_inc)
var layer_color = main_color
layer_color.a = clamp(vis_layers - i, 0, 1)
draw_set_transform(Vector2.ZERO, layer_rot, Vector2(1, squish))
if layer_type < 0.2:
var small_circles = rand_r(0, 5)
var small_radius = layer_radius * 0.1
_draw_circle(layer_radius, small_circles, small_radius, layer_color)
elif layer_type < 0.7:
var vertices = rand_r(5, 8)
var skip = rand_r(1, 2)
_draw_polygon(layer_radius, vertices, skip, layer_color)
elif layer_type <= 1:
var rune_num = round(4 + layer_counter * 32)
_draw_text_circle(layer_radius, rune_num, layer_color)

View file

@ -0,0 +1 @@
uid://pyblew2m6xc3

30
scripts/mask_bar.gd Normal file
View file

@ -0,0 +1,30 @@
extends TextureProgressBar
@export var threshold: float = 0.5
@export var shake_intensity: float = 5.0
var original_x: float
func _ready() -> void:
EventBus.mask_time_changed.connect(_on_time_changed)
original_x = position.x
func _process(_delta: float) -> void:
if value / max_value <= threshold:
apply_effects()
else:
position.x = original_x
modulate = Color.WHITE
func _on_time_changed(new: float) -> void:
value = new
func apply_effects() -> void:
var danger_factor = 1.0 - (value / (max_value * threshold))
danger_factor = clamp(danger_factor, 0.0, 1.0)
var flash = (sin(Time.get_ticks_msec() * 0.02) + 1.0) / 2.0
modulate = Color.WHITE * (1 + flash * danger_factor)
var shake = sin(Time.get_ticks_msec() * 0.05) * shake_intensity * danger_factor
position.x = original_x + shake

1
scripts/mask_bar.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://cqrqqn2p0h0kb

65
scripts/mask_drop.gd Normal file
View file

@ -0,0 +1,65 @@
class_name MaskDrop extends Node2D
@export var mask_type : Types.mask_types
@onready var popup := $Popup
@export var time_to_live = 14
@export var initial_blink_speed: float = 0.7
var alive_time = 0
var target_position : Vector2
@onready var blink_timer = $Timer
func _ready() -> void:
blink_timer.wait_time = initial_blink_speed
blink_timer.timeout.connect(_on_timeout)
blink_timer.start()
blink_timer.paused = true
$MeleeMask.rotate(randf() * PI - (PI / 2))
$RangedMask.rotate(randf() * PI - (PI / 2))
func _process(delta: float) -> void:
alive_time += delta
$MeleeMask.visible = false
$RangedMask.visible = false
$SpitMask.visible = false
if (mask_type == Types.mask_types.Melee):
$MeleeMask.visible = true
elif (mask_type == Types.mask_types.Ranged):
$RangedMask.visible = true
elif (mask_type == Types.mask_types.Spit):
$SpitMask.visible = true
var progress = alive_time / time_to_live
if progress > 0.5:
blink_timer.paused = false
blink_timer.wait_time = lerp(initial_blink_speed, 0.02, progress)
if alive_time >= time_to_live:
queue_free()
func show_popup():
var tween = create_tween()
tween.tween_property(popup, "modulate:a", 1, 0.2)
tween.parallel().tween_property(popup, "position:y", -23, 0.2)
func hide_popup():
var tween = create_tween()
tween.tween_property(popup, "modulate:a", 0, 0.2)
tween.parallel().tween_property(popup, "position:y", -16, 0.2)
func collect(target : Vector2):
target_position = target
var tween = create_tween()
tween.set_trans(Tween.TRANS_EXPO)
tween.tween_method(_lerp_to_target, 0.0, 1.0, 0.3)
func _lerp_to_target(progression:float):
global_position = lerp(global_position, target_position, progression)
if global_position.distance_to(target_position)<=10.0:
queue_free()
func _on_timeout():
visible = !visible

1
scripts/mask_drop.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://c576b1a6s5vw

39
scripts/mask_ui.gd Normal file
View file

@ -0,0 +1,39 @@
extends Label
var cur_mask : Types.mask_types
var uses : int
var health : int
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
EventBus.mask_changed.connect(_on_mask_changed)
EventBus.mask_uses_changed.connect(_on_uses_changed)
EventBus.health_changed.connect(_on_health_changed)
func _process(delta: float) -> void:
update_text()
func update_text():
var enemies = len(get_tree().get_nodes_in_group("enemy"))
text = "Mask: " + _get_mask_name(cur_mask) + "\n" + \
"Uses: " + str(uses) + "\n" + \
"Health: " + str(health) + "\n" + \
"Enemies: " + str(enemies)
func _get_mask_name(type : Types.mask_types):
if (type == 0): return "Melee"
if (type == 1): return "Ranged"
if (type == 2): return "Spit"
return ""
func _on_mask_changed(new : Types.mask_types):
cur_mask = new
update_text()
func _on_uses_changed(new : int):
uses = new
update_text()
func _on_health_changed(new : int):
health = new
update_text()

1
scripts/mask_ui.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://cdm0kcipvrxyo

25
scripts/portal.gd Normal file
View file

@ -0,0 +1,25 @@
extends Sprite2D
@export var nextScene : String
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
var enemies = len(get_tree().get_nodes_in_group("enemy"))
var player = get_tree().get_first_node_in_group("player")
if enemies == 0 and player:
material.set_shader_parameter("intensity", 1.7)
$MagicCircle.visible = true
$Text.visible = true
var player_dist = player.global_position.distance_to(global_position)
if Input.is_action_just_pressed("interact") && player_dist < 70:
SceneTransition.change_scene(nextScene)
else:
material.set_shader_parameter("intensity", 0)
$MagicCircle.visible = false
$Text.visible = false

1
scripts/portal.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://cioob0euvug4e

20
scripts/proximity.gd Normal file
View file

@ -0,0 +1,20 @@
extends Node2D
@export var range : int = 50
func _process(_delta: float) -> void:
if has_node("%Player"):
if %Player.global_position.distance_to(global_position) < range:
show_ins()
else:
hide_ins()
func show_ins():
var tween = create_tween()
tween.tween_property(self, "modulate:a", 1, 0.2)
tween.parallel().tween_property(self, "position:y", -23, 0.2)
func hide_ins():
var tween = create_tween()
tween.tween_property(self, "modulate:a", 0, 0.2)
tween.parallel().tween_property(self, "position:y", -16, 0.2)

1
scripts/proximity.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://c0wn3w7q2j470

6
scripts/ranged_enemy.gd Normal file
View file

@ -0,0 +1,6 @@
extends Enemy
func _draw():
if charge > 0.1:
var color = Color.RED.lerp(Color.WHITE, charge / charge_time)
draw_dashed_line(Vector2.ZERO, to_local(player.global_position), color, 1, 5)

View file

@ -0,0 +1 @@
uid://buamdls133c2e

8
scripts/scithe_attack.gd Normal file
View file

@ -0,0 +1,8 @@
extends Node2D
func set_from_player(val):
$Anchor/Scithe/Hitbox.from_player = val
func _on_animation_player_animation_finished(_anim_name: StringName) -> void:
queue_free()

View file

@ -0,0 +1 @@
uid://c1anp8np0aw0

51
scripts/settings_menu.gd Normal file
View file

@ -0,0 +1,51 @@
extends NinePatchRect
@export var volume_slider : HSlider
@export var fullscreen_check : CheckBox
@export var vsync_check : CheckBox
func _ready():
_update_settings()
Settings.apply_all()
func _update_settings():
volume_slider.value = Settings.master_volume
fullscreen_check.button_pressed = Settings.fullscreen
vsync_check.button_pressed = Settings.vsync
func _input(event: InputEvent) -> void:
_update_settings()
if (event.is_action_pressed("ui_cancel")):
get_viewport().set_input_as_handled()
visible = !visible
get_tree().paused = visible
elif (event.is_action_pressed("fullscreen")):
get_viewport().set_input_as_handled()
Settings.fullscreen = !Settings.fullscreen
Settings.apply_fullscreen()
func _on_volume_changed(value: float):
Settings.master_volume = value
Settings.apply_volume()
func _on_fullscreen_toggled(enabled: bool):
Settings.fullscreen = enabled
Settings.apply_fullscreen()
func _on_max_fps_changed(value: float):
Settings.max_fps = int(value)
Settings.apply_max_fps()
func _on_vsync_toggled(enabled : bool):
Settings.vsync = enabled
Settings.apply_vsync()
func _on_exit_button_pressed() -> void:
get_tree().quit()
func _on_continue_button_pressed() -> void:
visible = false
get_tree().paused = visible

Some files were not shown because too many files have changed in this diff Show more