refactor: add state machine on tower + disable option
This commit is contained in:
parent
dd7ec4229f
commit
4bd9a76d64
8 changed files with 105 additions and 62 deletions
|
|
@ -85,6 +85,7 @@ viewport_path = NodePath("EnergyBar3D/SubViewport")
|
|||
tower_name = "Pierre"
|
||||
type = 1
|
||||
icone = ExtResource("2_lcjqw")
|
||||
price = 100
|
||||
projectileRessource = SubResource("Resource_r52mr")
|
||||
towerRange = SubResource("SphereShape3D_c55ds")
|
||||
action_cooldown = 1.0
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ extends StaticBody3D
|
|||
class_name Tower
|
||||
|
||||
|
||||
enum STATE { BLUEPRINT, REST, ACTION, EXHAUSTED, DISABLED }
|
||||
|
||||
|
||||
signal state_changed
|
||||
signal energy_changed
|
||||
signal changed
|
||||
|
||||
|
|
@ -17,26 +21,25 @@ var projectileScene : PackedScene = preload("res://Towers/Projectiles/projectile
|
|||
@export_group("Base data")
|
||||
@export var icone : Texture2D
|
||||
@export var bio : String
|
||||
@export var price : int = 100
|
||||
@export var price : int
|
||||
|
||||
@export_group("Attack")
|
||||
@export var projectileRessource : ProjectileResource
|
||||
@export var towerRange : Shape3D
|
||||
@export var action_cooldown : float = 1.5:
|
||||
@export var action_cooldown : float = 0.3 :
|
||||
set(value):
|
||||
action_cooldown = clamp(value, 0.3, 999)
|
||||
|
||||
@export_group("Energy")
|
||||
@export var max_energy : float = 100.0 :
|
||||
@export var max_energy : float :
|
||||
set(value):
|
||||
var diff : int = value - max_energy
|
||||
var diff : float = value - max_energy
|
||||
max_energy = value
|
||||
if not Engine.is_editor_hint() && is_node_ready():
|
||||
energy += diff
|
||||
energyBar.max_value = max_energy
|
||||
changed.emit()
|
||||
@export var energy_regen : float = 10.0
|
||||
@export var energy_cost : float = 50.0
|
||||
@export var energy_regen : float
|
||||
@export var energy_cost : float
|
||||
|
||||
@export_group("Button")
|
||||
@export var buttonTooltip : String
|
||||
|
|
@ -47,18 +50,22 @@ var projectileScene : PackedScene = preload("res://Towers/Projectiles/projectile
|
|||
@onready var energyRecoveryCooldown : Timer = $EnergyRecoveryCooldown
|
||||
|
||||
|
||||
var state : STATE = STATE.BLUEPRINT :
|
||||
set(value):
|
||||
state = value
|
||||
state_changed.emit()
|
||||
var energy : float :
|
||||
set(value):
|
||||
energyBar.value = value
|
||||
energy = clampf(value, 0.0, max_energy)
|
||||
is_exhausted = energy < energy_cost
|
||||
energy = clampf(value, 0, max_energy)
|
||||
energyBar.value = energy
|
||||
energy_changed.emit()
|
||||
if not energy:
|
||||
state = STATE.EXHAUSTED
|
||||
|
||||
var availableTargets : Array[Enemy]
|
||||
var is_exhausted : bool = false
|
||||
var is_rest : bool :
|
||||
get(): return not energyRecoveryCooldown.is_stopped()
|
||||
var builded : bool = false
|
||||
var selectable : bool :
|
||||
get():
|
||||
return state != STATE.DISABLED && (state != STATE.BLUEPRINT || Game.money >= price)
|
||||
|
||||
|
||||
@export_category("Upgrades")
|
||||
|
|
@ -68,9 +75,9 @@ var builded : bool = false
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
energy = max_energy
|
||||
# WARNING : Prevent .tscn file to be modified by the load of the scene in editor
|
||||
if not Engine.is_editor_hint():
|
||||
energy = max_energy
|
||||
energyRecoveryCooldown.timeout.connect(func(): energy += energy_regen + Game.energy_boost)
|
||||
collision_layer = 0
|
||||
collision_mask = 0
|
||||
|
|
@ -79,10 +86,24 @@ func _ready() -> void:
|
|||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if visible && $AttackCooldown.is_stopped() && builded:
|
||||
if state == STATE.ACTION && $AttackCooldown.is_stopped():
|
||||
shoot()
|
||||
|
||||
|
||||
func changeState(newState : STATE) -> void:
|
||||
if [STATE.BLUEPRINT, STATE.DISABLED].has(state):
|
||||
return
|
||||
|
||||
match newState:
|
||||
STATE.ACTION when state == STATE.REST: in_action()
|
||||
STATE.ACTION when not energy: newState = STATE.EXHAUSTED
|
||||
STATE.ACTION: pass
|
||||
STATE.REST: resting()
|
||||
_: return # NOTE Prevent changing of state
|
||||
|
||||
state = newState
|
||||
|
||||
|
||||
func shoot() -> void:
|
||||
var target : Enemy = choose_target()
|
||||
if not target:
|
||||
|
|
@ -99,20 +120,16 @@ func resting() -> void:
|
|||
visible = false
|
||||
collision_layer = 0
|
||||
collision_mask = 0
|
||||
|
||||
if builded:
|
||||
toggleConnection(false)
|
||||
energyRecoveryCooldown.start()
|
||||
toggleConnection(false)
|
||||
energyRecoveryCooldown.start()
|
||||
|
||||
|
||||
func in_action() -> void:
|
||||
visible = true
|
||||
|
||||
if builded:
|
||||
toggleConnection(true)
|
||||
collision_layer = 0b100
|
||||
collision_mask = 0b100
|
||||
energyRecoveryCooldown.stop()
|
||||
collision_layer = 0b100
|
||||
collision_mask = 0b100
|
||||
toggleConnection(true)
|
||||
energyRecoveryCooldown.stop()
|
||||
|
||||
|
||||
func choose_target() -> Enemy:
|
||||
|
|
@ -125,13 +142,14 @@ func choose_target() -> Enemy:
|
|||
|
||||
|
||||
func build() -> bool:
|
||||
if builded || not Game.spendMoney(price):
|
||||
if state != STATE.BLUEPRINT || not Game.spendMoney(price):
|
||||
return false
|
||||
|
||||
sprite.modulate = "ffffffff"
|
||||
$EnergyBar3D.visible = true
|
||||
builded = true
|
||||
$PriceTag.visible = false
|
||||
state = STATE.ACTION
|
||||
in_action()
|
||||
changed.emit()
|
||||
return true
|
||||
|
||||
|
|
@ -155,3 +173,10 @@ func toggleConnection(activate : bool) -> void:
|
|||
$Range.body_entered.disconnect(onBodyEntered)
|
||||
$Range.body_exited.disconnect(onBodyExited)
|
||||
availableTargets.clear()
|
||||
|
||||
|
||||
func disable(duration : float) -> void:
|
||||
state = STATE.DISABLED
|
||||
resting()
|
||||
await get_tree().create_timer(duration).timeout
|
||||
state = STATE.REST
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ func _ready() -> void:
|
|||
#$AnimationPlayer.play("arrow_bobbing")
|
||||
EventBus.mouse_entered_gui.connect(onMouseEnteredGui)
|
||||
EventBus.mouse_exited_gui.connect(onMouseExitedGui)
|
||||
Game.allowed_tower_has_change.connect(connectTowerSignals)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
|
|
@ -42,6 +43,10 @@ func _process(_delta: float) -> void:
|
|||
else:
|
||||
EventBus.tower_selected.emit(Tower.TYPE.NONE)
|
||||
|
||||
if Input.is_action_just_pressed("test"):
|
||||
if tower:
|
||||
tower.disable(3)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey:
|
||||
|
|
@ -71,7 +76,7 @@ func handle_player_controls() -> Node3D:
|
|||
selection_icon.visible = true
|
||||
global_position = collider.global_position + Vector3(0.0, 0.21, 0.0)
|
||||
|
||||
if selected_tower && not selected_tower.builded:
|
||||
if selected_tower && selected_tower.state == Tower.STATE.BLUEPRINT:
|
||||
selected_tower.sprite.modulate = "ff4545c8" # If the tower can't be placed he is red
|
||||
selection_icon.visible = false # If we are placing a tower, hide the selector model
|
||||
if collider is GameTile && isTileFree(collider):
|
||||
|
|
@ -84,7 +89,7 @@ func placeTower() -> void:
|
|||
if not selected_tower:
|
||||
return
|
||||
|
||||
if not selected_tower.builded:
|
||||
if selected_tower.state == Tower.STATE.BLUEPRINT:
|
||||
if not selected_tower.build():
|
||||
return
|
||||
buildedTower += 1
|
||||
|
|
@ -105,11 +110,11 @@ func moveTower(tower: Tower, toPosition: Vector3) -> void:
|
|||
usedLocations.erase(tower.global_position.round())
|
||||
|
||||
if toPosition == Vector3.INF:
|
||||
tower.resting()
|
||||
tower.changeState(Tower.STATE.REST)
|
||||
else:
|
||||
usedLocations.set(toPosition.round(), tower)
|
||||
tower.global_position = toPosition
|
||||
tower.in_action()
|
||||
tower.changeState(Tower.STATE.ACTION)
|
||||
|
||||
var inAction : int = usedLocations.size()
|
||||
EventBus.team_in_action_changed.emit(inAction)
|
||||
|
|
@ -118,22 +123,22 @@ func moveTower(tower: Tower, toPosition: Vector3) -> void:
|
|||
|
||||
func onTowerSelect(towerType: Tower.TYPE):
|
||||
# Hide current not builded tower
|
||||
if selected_tower && not selected_tower.builded:
|
||||
if selected_tower && selected_tower.state == Tower.STATE.BLUEPRINT:
|
||||
selected_tower.visible = false
|
||||
|
||||
if selected_tower && selected_tower.type == towerType || towerType == Tower.TYPE.NONE:
|
||||
if selected_tower && not selected_tower.builded:
|
||||
if selected_tower && selected_tower.state == Tower.STATE.BLUEPRINT:
|
||||
remove_child(selected_tower)
|
||||
selected_tower = null
|
||||
else:
|
||||
selected_tower = Game.towers.get(towerType)
|
||||
if not selected_tower.builded:
|
||||
if selected_tower.state == Tower.STATE.BLUEPRINT:
|
||||
selected_tower.visible = true
|
||||
add_child(selected_tower)
|
||||
|
||||
|
||||
func selectTower(towerType: Tower.TYPE, force : bool = false) -> void:
|
||||
if towerType && (force || not selected_tower || selected_tower.builded):
|
||||
if towerType && (force || not selected_tower || selected_tower.state):
|
||||
EventBus.tower_selected.emit(towerType)
|
||||
|
||||
|
||||
|
|
@ -162,3 +167,14 @@ func onMouseEnteredGui() -> void:
|
|||
|
||||
func onMouseExitedGui() -> void:
|
||||
is_on_gui = false
|
||||
|
||||
|
||||
func connectTowerSignals() -> void:
|
||||
for tower : Tower in Game.towers.values():
|
||||
tower.state_changed.connect(onTowerStateChange.bind(tower))
|
||||
|
||||
|
||||
func onTowerStateChange(tower : Tower) -> void:
|
||||
if tower.state == Tower.STATE.DISABLED && tower == selected_tower:
|
||||
EventBus.tower_selected.emit(Tower.TYPE.NONE)
|
||||
moveTower(tower, Vector3.INF)
|
||||
|
|
|
|||
|
|
@ -86,7 +86,3 @@ metadata/_custom_type_script = "uid://blnmjxmusrsa7"
|
|||
[node name="PriceTag" type="Label3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
|
||||
billboard = 2
|
||||
|
||||
[connection signal="body_entered" from="Range" to="." method="_on_range_body_entered"]
|
||||
[connection signal="body_exited" from="Range" to="." method="_on_range_body_exited"]
|
||||
[connection signal="timeout" from="AttackCooldown" to="." method="_on_cooldown_timeout"]
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ func _ready() -> void:
|
|||
|
||||
func onCubeSelected() -> void:
|
||||
visible = true
|
||||
$PanelContainer2.visible = false
|
||||
$InfoContainer.visible = false
|
||||
createTowerUpgradeButtons(Game.upgrades)
|
||||
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ func onTowerSelected(towerType : Tower.TYPE) -> void:
|
|||
visible = false
|
||||
return
|
||||
|
||||
$PanelContainer2.visible = true
|
||||
$InfoContainer.visible = true
|
||||
tower = Game.towers.get(towerType)
|
||||
tower.energy_changed.connect(onEnergyChange)
|
||||
tower.changed.connect(onTowerChange)
|
||||
|
|
@ -48,12 +48,11 @@ func onTowerChange() -> void:
|
|||
# TODO Check for better UI to display it
|
||||
#%TowerBio.text = tower.bio
|
||||
|
||||
if tower.builded && not %UpgradeContainer.visible:
|
||||
createTowerUpgradeButtons(tower.upgrades)
|
||||
createTowerUpgradeButtons(tower.upgrades)
|
||||
|
||||
|
||||
func createTowerUpgradeButtons(upgrades : Array) -> void:
|
||||
if tower && not tower.builded:
|
||||
if tower && tower.state == Tower.STATE.BLUEPRINT:
|
||||
%UpgradeContainer.visible = false
|
||||
return
|
||||
|
||||
|
|
|
|||
22
UI/gui.tscn
22
UI/gui.tscn
|
|
@ -185,30 +185,30 @@ grow_vertical = 0
|
|||
theme_override_constants/separation = 0
|
||||
script = ExtResource("9_3lugd")
|
||||
|
||||
[node name="PanelContainer2" type="PanelContainer" parent="InfoPanel"]
|
||||
[node name="InfoContainer" type="PanelContainer" parent="InfoPanel"]
|
||||
custom_minimum_size = Vector2(200, 0)
|
||||
layout_mode = 2
|
||||
mouse_filter = 1
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_h4fn5")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="InfoPanel/PanelContainer2"]
|
||||
[node name="MarginContainer" type="MarginContainer" parent="InfoPanel/InfoContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 10
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_right = 10
|
||||
theme_override_constants/margin_bottom = 10
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="InfoPanel/PanelContainer2/MarginContainer"]
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="InfoPanel/InfoContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="InfoPanel/PanelContainer2/MarginContainer/VBoxContainer"]
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="InfoPanel/InfoContainer/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 20
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="InfoPanel/PanelContainer2/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="InfoPanel/InfoContainer/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TowerName" type="Label" parent="InfoPanel/PanelContainer2/MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer"]
|
||||
[node name="TowerName" type="Label" parent="InfoPanel/InfoContainer/MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 1
|
||||
|
|
@ -219,7 +219,7 @@ text = "Name"
|
|||
horizontal_alignment = 2
|
||||
vertical_alignment = 2
|
||||
|
||||
[node name="TowerEnergy" type="Label" parent="InfoPanel/PanelContainer2/MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer"]
|
||||
[node name="TowerEnergy" type="Label" parent="InfoPanel/InfoContainer/MarginContainer/VBoxContainer/HBoxContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Le nombre de héros en action sur le terrain !"
|
||||
|
|
@ -228,14 +228,14 @@ theme_override_colors/font_color = Color(0.2, 0.2, 0.2, 1)
|
|||
text = "0/0"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="TowerIcon" type="TextureRect" parent="InfoPanel/PanelContainer2/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
[node name="TowerIcon" type="TextureRect" parent="InfoPanel/InfoContainer/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme = ExtResource("5_wpcnu")
|
||||
texture = ExtResource("10_parkk")
|
||||
expand_mode = 3
|
||||
|
||||
[node name="TowerDamage" type="Label" parent="InfoPanel/PanelContainer2/MarginContainer/VBoxContainer"]
|
||||
[node name="TowerDamage" type="Label" parent="InfoPanel/InfoContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Le nombre de héros disponible dans le cube !"
|
||||
|
|
@ -244,7 +244,7 @@ theme_override_colors/font_color = Color(0.2, 0.2, 0.2, 1)
|
|||
text = "0"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="TowerCooldown" type="Label" parent="InfoPanel/PanelContainer2/MarginContainer/VBoxContainer"]
|
||||
[node name="TowerCooldown" type="Label" parent="InfoPanel/InfoContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Le nombre de héros en action sur le terrain !"
|
||||
|
|
@ -253,7 +253,7 @@ theme_override_colors/font_color = Color(0.2, 0.2, 0.2, 1)
|
|||
text = "0"
|
||||
horizontal_alignment = 2
|
||||
|
||||
[node name="TowerBio" type="Label" parent="InfoPanel/PanelContainer2/MarginContainer/VBoxContainer"]
|
||||
[node name="TowerBio" type="Label" parent="InfoPanel/InfoContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ func towerLinked(value) -> void:
|
|||
add_theme_stylebox_override("pressed", GameStyleBoxFlat.createWithColor(GameColor.COLOR.SELECTED))
|
||||
tower.changed.connect(onTowerChanged)
|
||||
tower.energy_changed.connect(func(): $EnergyBar.value = tower.energy)
|
||||
Game.money_changed.connect(onMoneyChanged)
|
||||
tower.state_changed.connect(updateDisabled)
|
||||
Game.money_changed.connect(updateDisabled)
|
||||
EventBus.tower_selected.connect(func(_type): set_pressed_no_signal(_type == tower.type))
|
||||
toggled.connect(func(state): EventBus.tower_selected.emit(tower.type if state else Tower.TYPE.NONE))
|
||||
tooltip_text = tower.name
|
||||
|
|
@ -29,11 +30,11 @@ func towerLinked(value) -> void:
|
|||
|
||||
|
||||
func onTowerChanged() -> void :
|
||||
disabled = not tower.builded && Game.money < tower.price
|
||||
disabled = not tower.selectable
|
||||
$EnergyBar.max_value = tower.max_energy
|
||||
$EnergyBar.visible = tower.builded
|
||||
$LeFond.visible = not tower.builded
|
||||
$EnergyBar.visible = tower.selectable
|
||||
$LeFond.visible = tower.state == Tower.STATE.BLUEPRINT
|
||||
|
||||
|
||||
func onMoneyChanged() -> void:
|
||||
disabled = not tower.builded && Game.money < tower.price
|
||||
func updateDisabled() -> void:
|
||||
disabled = not tower.selectable
|
||||
|
|
|
|||
|
|
@ -101,6 +101,11 @@ pause_game={
|
|||
"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":80,"key_label":0,"unicode":112,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
test={
|
||||
"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":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":3,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[layer_names]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue