Pārlūkot izejas kodu

:sparkles: first kinda working prototype

Felix Bytow 10 mēneši atpakaļ
vecāks
revīzija
74a1b72bc1

+ 5 - 1
assets/autoload/global_input.gd

@@ -5,7 +5,9 @@ const FULL_SCREEN_MODES: Array[DisplayServer.WindowMode] = [
 	DisplayServer.WINDOW_MODE_FULLSCREEN,
 ]
 
-var dragging_disabled = false
+var visible_vision: bool = false
+
+var dragging_disabled: bool = false
 var dragged_object: Node2D = null
 var drop_spot: Node2D = null
 var original_z: int = 0
@@ -47,5 +49,7 @@ func toggle_fullscreen():
 func _input(event: InputEvent):
 	if event.is_action_pressed("toggle_fullscreen"):
 		toggle_fullscreen()
+	elif event.is_action_pressed("toddle_vision"):
+		visible_vision = !visible_vision
 	elif event is InputEventMouseMotion && is_dragging():
 		dragged_object.global_position = event.global_position

+ 8 - 6
assets/scenes/bot.gd

@@ -7,7 +7,7 @@ const SKINS: Array[String] = ["blue", "green", "red", "yellow"]
 @onready var base: Area2D = null
 @onready var explosion_scene: PackedScene = load("res://assets/scenes/explosion.tscn")
 
-@export var speed: float = 42.0
+@export var speed: float = 64.0
 
 func _ready() -> void:
 	base = get_tree().get_first_node_in_group("base")
@@ -26,11 +26,13 @@ func _physics_process(delta: float) -> void:
 	if base != null:
 		global_position = global_position.move_toward(get_base_center(), speed * delta)
 
+func die():
+	var explosion = explosion_scene.instantiate()
+	explosion.global_position = global_position
+	add_sibling(explosion)
+	queue_free()
+
 func _on_area_entered(area: Area2D):
 	if area.is_in_group("base"):
-		var explosion = explosion_scene.instantiate()
-		explosion.global_position = global_position
-		add_sibling(explosion)
-		queue_free()
-		
+		die()
 		

+ 67 - 0
assets/scenes/demon.gd

@@ -1,8 +1,39 @@
 extends Area2D
 class_name Demon
 
+@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
+@onready var vision: ShapeCast2D = $Vision
+@onready var vision_polygon: Polygon2D = $Vision/Polygon2D
+
+@onready var gun_shot_sound: AudioStreamPlayer = $GunShotSound
+
+var ammo: int = 0
+
+enum State {
+	STATE_IDLE,
+	STATE_SHOOTING,
+	STATE_RELOADING,
+}
+var state: State = State.STATE_IDLE
+
 var is_being_dragged: bool = false
 
+func _ready() -> void:
+	ammo = Game.max_ammo
+
+func _process(_delta: float) -> void:
+	vision_polygon.visible = GlobalInput.visible_vision
+
+func _physics_process(_delta: float) -> void:
+	vision.force_shapecast_update()
+	if not vision.is_colliding():
+		return
+	
+	var foe = vision.get_collider(0)
+	if foe == null || not (foe is Area2D):
+		return
+	shoot_at(foe as Area2D)
+
 func _on_mouse_entered():
 	if not GlobalInput.is_dragging():
 		# TODO: add custom cursor image
@@ -27,3 +58,39 @@ func handle_click(event: InputEventMouseButton):
 	elif not event.pressed && is_being_dragged:
 		is_being_dragged = false
 		GlobalInput.stop_dragging()
+
+func reload() -> void:
+	state = State.STATE_RELOADING
+	print("%s: reloading (%.2f seconds)" % [name, Game.reload_time])
+	await get_tree().create_timer(Game.reload_time).timeout
+	print(name + ": done reloading")
+	ammo = Game.max_ammo
+	state = State.STATE_IDLE
+
+func shoot_at(foe: Area2D) -> void:
+	if state != State.STATE_IDLE:
+		return
+	
+	print("%s: %d/%d" % [name, ammo, Game.max_ammo])
+	
+	if ammo <= 0:
+		await reload()
+	else:
+		state = State.STATE_SHOOTING
+		ammo -= 1
+		gun_shot_sound.play()
+		sprite.play("shoot")
+		if foe.has_method("die"):
+			foe.die()
+		else:
+			foe.queue_free()
+		await sprite.animation_finished
+		if ammo <= 0:
+			await reload()
+		else:
+			state = State.STATE_IDLE
+
+func _on_animation_finished():
+	sprite.animation = "idle"
+	sprite.frame = 0
+	

+ 26 - 5
assets/scenes/demon.tscn

@@ -1,4 +1,4 @@
-[gd_scene load_steps=9 format=3 uid="uid://gq6u7iesbchb"]
+[gd_scene load_steps=11 format=3 uid="uid://gq6u7iesbchb"]
 
 [ext_resource type="Texture2D" uid="uid://dl8yyrdb8o001" path="res://assets/textures/survivors/purple/demon_idle.png" id="1_efcu4"]
 [ext_resource type="Script" path="res://assets/scenes/demon.gd" id="1_h64o5"]
@@ -6,6 +6,7 @@
 [ext_resource type="Texture2D" uid="uid://c3kee723fcojn" path="res://assets/textures/survivors/purple/demon_shoot_2.png" id="3_nvdqw"]
 [ext_resource type="Texture2D" uid="uid://gg4oygilqclm" path="res://assets/textures/survivors/purple/demon_shoot_3.png" id="4_g3y15"]
 [ext_resource type="Texture2D" uid="uid://b23076s18srxm" path="res://assets/textures/survivors/purple/demon_shoot_4.png" id="5_50e2m"]
+[ext_resource type="AudioStream" uid="uid://uet8fl1medm2" path="res://assets/audio/explosion_2.wav" id="7_sniq6"]
 
 [sub_resource type="SpriteFrames" id="SpriteFrames_nbhay"]
 animations = [{
@@ -13,9 +14,9 @@ animations = [{
 "duration": 1.0,
 "texture": ExtResource("1_efcu4")
 }],
-"loop": true,
+"loop": false,
 "name": &"idle",
-"speed": 5.0
+"speed": 1.0
 }, {
 "frames": [{
 "duration": 1.0,
@@ -30,14 +31,17 @@ animations = [{
 "duration": 1.0,
 "texture": ExtResource("5_50e2m")
 }],
-"loop": true,
+"loop": false,
 "name": &"shoot",
-"speed": 5.0
+"speed": 20.0
 }]
 
 [sub_resource type="RectangleShape2D" id="RectangleShape2D_d3lxi"]
 size = Vector2(246.529, 258.823)
 
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_pkx02"]
+size = Vector2(1400, 1000)
+
 [node name="Demon" type="Area2D" groups=["drag", "survivor"]]
 z_as_relative = false
 scale = Vector2(0.17, 0.17)
@@ -53,6 +57,23 @@ animation = &"shoot"
 [node name="DragShape" type="CollisionShape2D" parent="."]
 shape = SubResource("RectangleShape2D_d3lxi")
 
+[node name="Vision" type="ShapeCast2D" parent="."]
+position = Vector2(0, -658.823)
+shape = SubResource("RectangleShape2D_pkx02")
+collision_mask = 4
+collide_with_areas = true
+collide_with_bodies = false
+
+[node name="Polygon2D" type="Polygon2D" parent="Vision"]
+visible = false
+position = Vector2(-5.88235, 664.706)
+color = Color(0.0470588, 1, 1, 0.207843)
+polygon = PackedVector2Array(700, -135.294, 700, -1135.29, -700, -1135.29, -700, -135.294)
+
+[node name="GunShotSound" type="AudioStreamPlayer" parent="."]
+stream = ExtResource("7_sniq6")
+
 [connection signal="input_event" from="." to="." method="_on_input_event"]
 [connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"]
 [connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"]
+[connection signal="animation_finished" from="AnimatedSprite2D" to="." method="_on_animation_finished"]

+ 27 - 27
assets/scenes/explosion.tscn

@@ -35,6 +35,33 @@ animations = [{
 "speed": 20.0
 }]
 
+[sub_resource type="Animation" id="Animation_3m7yr"]
+length = 0.001
+tracks/0/type = "value"
+tracks/0/imported = false
+tracks/0/enabled = true
+tracks/0/path = NodePath("AnimatedSprite2D:frame")
+tracks/0/interp = 1
+tracks/0/loop_wrap = true
+tracks/0/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 1,
+"values": [1]
+}
+tracks/1/type = "value"
+tracks/1/imported = false
+tracks/1/enabled = true
+tracks/1/path = NodePath("AudioStreamPlayer:volume_db")
+tracks/1/interp = 1
+tracks/1/loop_wrap = true
+tracks/1/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 0,
+"values": [-80.0]
+}
+
 [sub_resource type="Animation" id="Animation_03hgi"]
 resource_name = "explosion"
 length = 0.6
@@ -78,33 +105,6 @@ tracks/2/keys = {
 "values": [0.0, -80.0]
 }
 
-[sub_resource type="Animation" id="Animation_3m7yr"]
-length = 0.001
-tracks/0/type = "value"
-tracks/0/imported = false
-tracks/0/enabled = true
-tracks/0/path = NodePath("AnimatedSprite2D:frame")
-tracks/0/interp = 1
-tracks/0/loop_wrap = true
-tracks/0/keys = {
-"times": PackedFloat32Array(0),
-"transitions": PackedFloat32Array(1),
-"update": 1,
-"values": [1]
-}
-tracks/1/type = "value"
-tracks/1/imported = false
-tracks/1/enabled = true
-tracks/1/path = NodePath("AudioStreamPlayer:volume_db")
-tracks/1/interp = 1
-tracks/1/loop_wrap = true
-tracks/1/keys = {
-"times": PackedFloat32Array(0),
-"transitions": PackedFloat32Array(1),
-"update": 0,
-"values": [-80.0]
-}
-
 [sub_resource type="AnimationLibrary" id="AnimationLibrary_opklp"]
 _data = {
 "RESET": SubResource("Animation_3m7yr"),

+ 4 - 0
assets/scenes/player_spawn_zone.gd

@@ -7,9 +7,13 @@ class_name PlayerSpawnZone
 const DEMON_SCENE: PackedScene = preload("res://assets/scenes/demon.tscn")
 
 func add_survivor() -> void:
+	if get_tree().get_nodes_in_group("survivor").size() >= 9:
+		return
+	
 	var spot = find_first_free_spot()
 	if spot == null:
 		return
+	
 	var demon = DEMON_SCENE.instantiate()
 	spot.add_child(demon)
 

+ 1 - 1
assets/scenes/transition.gd

@@ -27,5 +27,5 @@ func transition(display_text: String = "", callback: Callable = Callable()):
 	text = display_text
 	await slide_in()
 	callback.call()
-	await get_tree().create_timer(2).timeout
+	await get_tree().create_timer(1).timeout
 	await slide_out()

+ 5 - 0
project.godot

@@ -37,6 +37,11 @@ toggle_fullscreen={
 "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"echo":false,"script":null)
 ]
 }
+toddle_vision={
+"deadzone": 0.5,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":86,"key_label":0,"unicode":118,"echo":false,"script":null)
+]
+}
 
 [layer_names]
 

+ 34 - 12
screens/game.gd

@@ -1,4 +1,5 @@
 extends Node2D
+class_name Game
 
 @onready var health_bar: HealthBar = $HealthBar
 @onready var overlay: CanvasLayer = $Overlay
@@ -35,6 +36,10 @@ enum Loot {
 	LOOT_LOWER_COOLDOWN,
 }
 
+const RELOAD_TIME: float = 4.0
+static var reload_time: float = RELOAD_TIME # seconds
+static var max_ammo: int = 1 # shots
+
 var phase: Phase = Phase.PHASE_SETUP:
 	set(new_value):
 		phase = new_value
@@ -52,9 +57,12 @@ var waves_completed: int = 0:
 		wave_label.text = "WAVE: %d" % (new_value + 1)
 
 func _ready() -> void:
+	Game.reload_time = RELOAD_TIME # seconds
+	Game.max_ammo = 1 # shots
 	waves_completed = 0
 	phase = Phase.PHASE_SETUP
 	overlay.visible = true
+	player_spawn.add_survivor()
 	transition.slide_out()
 	await transition.game_fully_visible
 
@@ -77,6 +85,7 @@ func go_to_main_menu() -> void:
 func spawn_bot(spawn: Marker2D) -> void:
 	var bot: Bot = bot_scene.instantiate()
 	bot.global_position = spawn.global_position
+	bot.speed += waves_completed
 	bot.visible = false
 	add_child(bot)
 
@@ -103,12 +112,13 @@ func activate_foe() -> void:
 
 func reset_survivors() -> void:
 	var survivors = get_survivors()
+	#for survivor in survivors:
+	#	survivor.reparent(self)
 	for survivor in survivors:
-		survivor.reparent(self)
-	for survivor in survivors:
-		player_spawn.drop(survivor)
-		survivor.position = Vector2.ZERO
-		survivor.rotation = 0.0
+		#player_spawn.drop(survivor)
+		#survivor.position = Vector2.ZERO
+		#survivor.rotation = 0.0
+		survivor.ammo = Game.max_ammo
 
 func _on_timer_timeout() -> void:
 	activate_foe()
@@ -116,7 +126,7 @@ func _on_timer_timeout() -> void:
 func base_hit() -> void:
 	health_bar.health -= 1
 	if health_bar.health == 0:
-		game_over()
+		await game_over()
 
 func game_over() -> void:
 	spawn_timer.stop()
@@ -144,14 +154,24 @@ func start_combat() -> void:
 	combat_start_sound.play()
 	activate_foe()
 
+const LOOT_SURVIVOR: String = "+1 SURVIVOR"
+const LOOT_HEALTH: String = "+3 HEALTH"
+const LOOT_AMMO: String = "BIGGER MAGAZINES"
+const LOOT_RELOAD_TIME: String = "FASTER RELOADING"
+const LOOTS: Array[String] = [LOOT_SURVIVOR, LOOT_HEALTH, LOOT_AMMO, LOOT_RELOAD_TIME]
+
 func end_combat() -> void:
 	phase = Phase.PHASE_LOOT
 	spawn_timer.stop()
+	await get_tree().create_timer(1.5).timeout
 	var vs: Victory = victory_scene.instantiate()
 	ui_root.add_child(vs)
-	vs.set_loot_options("+1 SURVIVOR", "+3 HEALTH", "FASTER SHOTS")
+	var loots: Array[String] = []
+	loots.assign(LOOTS)
+	loots.shuffle()
+	vs.set_loot_options(loots[0], loots[1], loots[2])
 	await vs.loot_selected
-	var loot = vs.selection
+	var loot: String = loots[vs.selection]
 	print("selected loot: " + str(loot))
 	var next_wave = waves_completed + 2
 	await transition.transition("WAVE %d" % next_wave, func ():
@@ -160,12 +180,14 @@ func end_combat() -> void:
 		self.next_phase_button.disabled = false
 		self.reset_survivors()
 		match loot:
-			0:
+			LOOT_SURVIVOR:
 				self.player_spawn.add_survivor()
-			1:
+			LOOT_HEALTH:
 				self.health_bar.health += 3
-			2:
-				pass # TODO: increase fire rate
+			LOOT_RELOAD_TIME:
+				Game.reload_time *= 0.9
+			LOOT_AMMO:
+				Game.max_ammo += 1
 	)
 	GlobalInput.dragging_disabled = false
 

+ 2 - 2
screens/game.tscn

@@ -120,7 +120,7 @@ position = Vector2(641, 207)
 
 [node name="SurvivorSpot" parent="SurvivorSpots" instance=ExtResource("6_c1csp")]
 position = Vector2(497, 451)
-rotation = -0.785398
+rotation = -1.5708
 
 [node name="SurvivorSpot7" parent="SurvivorSpots" instance=ExtResource("6_c1csp")]
 position = Vector2(575, 451)
@@ -141,7 +141,7 @@ rotation = -1.5708
 
 [node name="SurvivorSpot4" parent="SurvivorSpots" instance=ExtResource("6_c1csp")]
 position = Vector2(778, 451)
-rotation = 0.785398
+rotation = 1.5708
 
 [node name="SurvivorSpot5" parent="SurvivorSpots" instance=ExtResource("6_c1csp")]
 position = Vector2(778, 511)