├── addons └── godot_gameplay_systems │ ├── inventory_system │ ├── assets │ │ ├── DropIcon.png │ │ ├── ItemIcon.png │ │ ├── DropNode2D.png │ │ ├── DropNode3D.png │ │ ├── DropGroupIcon.png │ │ ├── DropTableIcon.png │ │ ├── EquipmentIcon.png │ │ ├── Equipped3DIcon.png │ │ ├── InventoryIcon.png │ │ ├── RadialMenuIcon.png │ │ ├── DropIcon.png.import │ │ ├── ItemIcon.png.import │ │ ├── DropNode2D.png.import │ │ ├── DropNode3D.png.import │ │ ├── DropGroupIcon.png.import │ │ ├── DropTableIcon.png.import │ │ ├── EquipmentIcon.png.import │ │ ├── InventoryIcon.png.import │ │ ├── Equipped3DIcon.png.import │ │ └── RadialMenuIcon.png.import │ ├── nodes │ │ ├── drop_3d.gd │ │ ├── drop_2d.gd │ │ ├── radial_menu_container.gd │ │ ├── pickable_item_2d.gd │ │ ├── pickable_item_3d.gd │ │ ├── equipped_item_3d.gd │ │ ├── drop.gd │ │ └── inventory.gd │ ├── resources │ │ ├── drop_table.gd │ │ ├── drop_group.gd │ │ ├── equipment_slot.gd │ │ └── item.gd │ ├── objects │ │ └── item_activation_event.gd │ └── plugin.gd │ ├── interactables │ ├── assets │ │ ├── Interaction2DIcon.png │ │ ├── Interaction3DIcon.png │ │ ├── InteractionIcon.png │ │ ├── InteractableArea2D.png │ │ ├── InteractableArea3D.png │ │ ├── InteractionRayCast2DIcon.png │ │ ├── InteractionRayCast3DIcon.png │ │ ├── InteractionIcon.png.import │ │ ├── Interaction2DIcon.png.import │ │ ├── Interaction3DIcon.png.import │ │ ├── InteractableArea2D.png.import │ │ ├── InteractableArea3D.png.import │ │ ├── InteractionRayCast2DIcon.png.import │ │ └── InteractionRayCast3DIcon.png.import │ ├── nodes │ │ ├── 2d │ │ │ ├── interactable_area_2d.gd │ │ │ └── interaction_raycast_2d.gd │ │ ├── 3d │ │ │ ├── interactable_area_3d.gd │ │ │ └── interaction_raycast_3d.gd │ │ └── interaction_manager.gd │ ├── plugin.gd │ └── resources │ │ └── interaction.gd │ ├── attributes_and_abilities │ ├── assets │ │ ├── Ability@0.15x.png │ │ ├── Attribute@0.15x.png │ │ ├── TimedEffect@0.15x.png │ │ ├── AttributeEffect@0.15x.png │ │ ├── AttributeTable@0.15x.png │ │ ├── GameplayAttributes.sketch │ │ ├── GameplayEffect@0.15x.png │ │ ├── AbilityContainer@0.15x.png │ │ ├── GameplayAttributeMap@0.15x.png │ │ ├── TimedEffectOneShot@0.15x.png │ │ ├── Attribute.svg │ │ ├── AttributeEffect.svg │ │ ├── TimedEffect.svg │ │ ├── Ability@0.15x.png.import │ │ ├── Attribute@0.15x.png.import │ │ ├── TimedEffect@0.15x.png.import │ │ ├── AttributeTable@0.15x.png.import │ │ ├── GameplayEffect@0.15x.png.import │ │ ├── AbilityContainer@0.15x.png.import │ │ ├── AttributeEffect@0.15x.png.import │ │ ├── TimedEffectOneShot@0.15x.png.import │ │ ├── GameplayAttributeMap@0.15x.png.import │ │ ├── GameplayAttributeMap.svg │ │ ├── Ability.svg.import │ │ ├── Attribute.svg.import │ │ ├── TimedEffect.svg.import │ │ ├── AttributeTable.svg.import │ │ ├── GameplayEffect.svg.import │ │ ├── AttributeEffect.svg.import │ │ ├── AbilityContainer.svg.import │ │ ├── TimedEffectOneShot.svg.import │ │ ├── GameplayAttributeMap.svg.import │ │ ├── AttributeTable.svg │ │ ├── TimedEffectOneShot.svg │ │ ├── GameplayEffect.svg │ │ ├── Ability.svg │ │ └── AbilityContainer.svg │ ├── resources │ │ ├── stop_effect_if_0.gd │ │ ├── attribute_table.gd │ │ ├── attribute_effect_condition.gd │ │ ├── attribute.gd │ │ ├── stop_effect_at_value.gd │ │ ├── attribute_effect.gd │ │ └── ability.gd │ ├── nodes │ │ ├── gameplay_effect.gd │ │ ├── effected_area2d.gd │ │ ├── effected_area3d.gd │ │ └── gameplay_attribute_map.gd │ ├── debug │ │ ├── abilities_debugger.tscn │ │ └── abilities_debugger.gd │ ├── inspector │ │ ├── gameplay_effect_inspector_plugin.gd │ │ ├── gameplay_attribute_map_inspector_plugin.gd │ │ ├── components │ │ │ ├── attribute_editor_row.gd │ │ │ ├── attribute_editor_row.tscn │ │ │ ├── attribute_effect_editor_row.tscn │ │ │ └── attribute_effect_editor_row.gd │ │ ├── attribute_editor.gd │ │ └── attribute_effect_editor.gd │ ├── objects │ │ ├── activation_event.gd │ │ └── attribute_spec.gd │ └── plugin.gd │ ├── camera_shake │ ├── plugin.gd │ └── nodes │ │ └── camera_shake.gd │ ├── plugin.cfg │ ├── slideshow │ ├── plugin.gd │ └── slide_show.gd │ ├── extended_character_nodes │ ├── plugin.gd │ └── nodes │ │ ├── 2d │ │ └── point_and_click_2d.gd │ │ └── 3d │ │ └── point_and_click_3d.gd │ ├── turn_based │ ├── plugin.gd │ ├── autoloads │ │ └── turn_manager.gd │ └── nodes │ │ ├── TurnSubscriber.gd │ │ └── TurnBasedGame.gd │ └── plugin.gd ├── LICENSE └── project.godot /addons/godot_gameplay_systems/inventory_system/assets/DropIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/DropNode2D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropNode2D.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/DropNode3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropNode3D.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/Interaction2DIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/Interaction2DIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/Interaction3DIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/Interaction3DIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/DropGroupIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropGroupIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/DropTableIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/DropTableIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/EquipmentIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/EquipmentIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/Equipped3DIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/Equipped3DIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/RadialMenuIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/inventory_system/assets/RadialMenuIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability@0.15x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability@0.15x.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute@0.15x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute@0.15x.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect@0.15x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect@0.15x.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable@0.15x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable@0.15x.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributes.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributes.sketch -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer@0.15x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer@0.15x.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap@0.15x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap@0.15x.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctoD/godot-gameplay-systems/HEAD/addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/camera_shake/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | 5 | func _enter_tree() -> void: 6 | add_custom_type("CameraShake", "Node", preload("./nodes/camera_shake.gd"), null) 7 | 8 | 9 | func _exit_tree() -> void: 10 | remove_custom_type("CameraShake") 11 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/nodes/2d/interactable_area_2d.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png") 2 | @tool 3 | class_name InteractableArea2D extends Area2D 4 | 5 | @export_category("Interaction") 6 | @export var interaction: Interaction = null 7 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/nodes/3d/interactable_area_3d.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png") 2 | @tool 3 | class_name InteractableArea3D extends Area3D 4 | 5 | @export_category("Interaction") 6 | @export var interaction: Interaction = null 7 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="GGS" 4 | description="Godot Gameplay Systems (formerly godot gameplay attributes) is a set of nodes and resources which speed up development of skills and attribute based gameplay mechanisms." 5 | author="OctoD" 6 | version="1.0.0" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/nodes/drop_3d.gd: -------------------------------------------------------------------------------- 1 | class_name Drop3D extends Node3D 2 | 3 | ## Is a drop zone in a 3D world 4 | 5 | 6 | # Emitted when items are dropped in this node. 7 | signal item_dropped(item: Item, node: Node3D) 8 | 9 | 10 | func _ready() -> void: 11 | add_to_group("ggs.drop-node") 12 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/nodes/drop_2d.gd: -------------------------------------------------------------------------------- 1 | class_name Drop2D extends Node2D 2 | 3 | 4 | ## Is a drop zone in a 2D world 5 | 6 | 7 | # Emitted when items are dropped in this node. 8 | signal item_dropped(item: Item, node: Node2D) 9 | 10 | 11 | func _ready() -> void: 12 | add_to_group("ggs.drop-node") 13 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/slideshow/plugin.gd: -------------------------------------------------------------------------------- 1 | extends EditorPlugin 2 | 3 | 4 | const slideshow_script = preload("./slide_show.gd") 5 | 6 | 7 | func _enter_tree() -> void: 8 | add_custom_type("SlideShow", "Node2D", slideshow_script, null) 9 | 10 | 11 | func _exit_tree() -> void: 12 | remove_custom_type("SlideShow") 13 | 14 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/extended_character_nodes/plugin.gd: -------------------------------------------------------------------------------- 1 | extends EditorPlugin 2 | 3 | 4 | func _enter_tree() -> void: 5 | add_custom_type("PointAndClick2D", "Node2D", preload("./nodes/2d/point_and_click_2d.gd"), null) 6 | add_custom_type("PointAndClick3D", "Node3D", preload("./nodes/3d/point_and_click_3d.gd"), null) 7 | 8 | 9 | func _exit_tree() -> void: 10 | remove_custom_type("PointAndClick2D") 11 | remove_custom_type("PointAndClick3D") 12 | 13 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/resources/stop_effect_if_0.gd: -------------------------------------------------------------------------------- 1 | class_name StopEffectIf0 extends AttributeEffectCondition 2 | 3 | 4 | func should_apply(attribute_effect: AttributeEffect, effect: GameplayEffect, gameplay_attributes: GameplayAttributeMap) -> bool: 5 | var attribute = gameplay_attributes.get_attribute_by_name(attribute_effect.attribute_name) 6 | 7 | if attribute: 8 | return attribute.current_value > 0 9 | 10 | return false 11 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/nodes/gameplay_effect.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect.svg") 2 | @tool 3 | class_name GameplayEffect extends Node 4 | 5 | 6 | @export_category("Effect") 7 | ## Is the [AttributeTable] resource 8 | @export var table: AttributeTable = null 9 | ## Is the array of [AttributeEffect] resources generated by the inspector 10 | @export var attributes_affected: Array[AttributeEffect] = [] 11 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute_table.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable.svg") 2 | 3 | class_name AttributeTable extends Resource 4 | 5 | ## Represents a table of attributes 6 | 7 | @export_category("Attributes") 8 | ## The list of attributes 9 | @export var attributes : Array[String] = [] 10 | 11 | 12 | func _init(p_attributes: Array[String] = []) -> void: 13 | attributes = p_attributes 14 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/debug/abilities_debugger.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cxh5dyttaogko"] 2 | 3 | [ext_resource type="Script" path="res://addons/godot_gameplay_systems/attributes_and_abilities/debug/abilities_debugger.gd" id="1_lb3v4"] 4 | 5 | [node name="AbilitiesDebugger" type="BoxContainer"] 6 | offset_right = 1152.0 7 | script = ExtResource("1_lb3v4") 8 | 9 | [node name="Timer" type="Timer" parent="."] 10 | wait_time = 5.0 11 | autostart = true 12 | 13 | [node name="Labels" type="VBoxContainer" parent="."] 14 | layout_mode = 2 15 | size_flags_horizontal = 3 16 | size_flags_vertical = 3 17 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/turn_based/plugin.gd: -------------------------------------------------------------------------------- 1 | extends EditorPlugin 2 | 3 | 4 | const TurnBasedGameScript = preload("./nodes/TurnBasedGame.gd") 5 | const TurnSubscriberScript = preload("./nodes/TurnSubscriber.gd") 6 | 7 | 8 | func _enter_tree() -> void: 9 | add_autoload_singleton("TurnManager", "./autoloads/turn_manager.gd") 10 | add_custom_type("TurnBasedGame", "Node", TurnBasedGameScript, null) 11 | add_custom_type("TurnSubscriber", "Node", TurnSubscriberScript, null) 12 | 13 | 14 | func _exit_tree() -> void: 15 | remove_autoload_singleton("TurnManager") 16 | remove_custom_type("TurnBasedGame") 17 | remove_custom_type("TurnSubscriber") 18 | 19 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute_effect_condition.gd: -------------------------------------------------------------------------------- 1 | class_name AttributeEffectCondition extends Resource 2 | 3 | 4 | ## Calls it's own method when the bound attribute should change 5 | ## 6 | ## If the condition expressed is met, then the effect can be applied 7 | ## This kind of resource is very useful to stop infinite time-based effects 8 | 9 | 10 | ## Returns [code]true[/code] if the effect should continue to be applied, [code]false[/code] otherwise 11 | func should_apply( 12 | attribute_effect: AttributeEffect, 13 | gameplay_effect: GameplayEffect, 14 | gameplay_attribute_map: GameplayAttributeMap 15 | ) -> bool: 16 | return true 17 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute.gd: -------------------------------------------------------------------------------- 1 | class_name AttributeResource extends Resource 2 | 3 | @export_category("Attribute") 4 | ## Is the attribute name 5 | @export var attribute_name := "" 6 | ## Is the attribute minimum value 7 | @export var minimum_value := 0.0 8 | ## Is the attribute maximum value 9 | @export var maximum_value := 0.0 10 | ## Is the attribute initial value 11 | @export var current_value := 0.0 12 | 13 | 14 | func _init(p_attribute_name: String = "", p_minimum_value: float = 0.0, p_maximum_value: float = 0.0, p_current_value: float = 0.0) -> void: 15 | attribute_name = p_attribute_name 16 | minimum_value = p_minimum_value 17 | maximum_value = p_maximum_value 18 | current_value = p_current_value 19 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/inspector/gameplay_effect_inspector_plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorInspectorPlugin 3 | 4 | 5 | var attribute_editor = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_editor.gd") 6 | var attribute_effect_editor = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_effect_editor.gd") 7 | 8 | 9 | func _can_handle(object) -> bool: 10 | return object is GameplayEffect 11 | 12 | 13 | func _parse_property(object: Object, type, name: String, hint_type, hint_string: String, usage_flags, wide: bool) -> bool: 14 | if type == 28 and name == "attributes_affected": 15 | add_property_editor(name, attribute_effect_editor.new(), true) 16 | return true 17 | if type == 24 and name == "table": 18 | return true 19 | return false 20 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Attribute 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/resources/drop_table.gd: -------------------------------------------------------------------------------- 1 | class_name DropTable extends Resource 2 | 3 | ## Represents a drop table. 4 | ## 5 | ## It has several [DropGroup] pools. Each pool has a chance of dropping one [Item] based on it's [member DropGroup.drop_chance]. 6 | 7 | @export_category("Drop") 8 | ## An array of [DropGroup]. Each pool can drop one item. 9 | @export var pools: Array[DropGroup] = [] 10 | 11 | 12 | ## Drops an array of [Item]s. 13 | ## [br]It accepts an optional argument [code]drop_modifier[/code] which increases (or decreases if negative) drop chances for each [DropGroup] pool. 14 | ## [br]The greater the modifier, the higher the chances to drop [Item]s from the pools. 15 | func drop(drop_modifier: float = 0.0) -> Array[Item]: 16 | var out: Array[Item] = [] 17 | 18 | for pool in pools: 19 | var maybe_item = pool.drop(drop_modifier) 20 | 21 | if maybe_item != null: 22 | out.append(maybe_item) 23 | 24 | return out 25 | 26 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | AttributeEffect 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/inspector/gameplay_attribute_map_inspector_plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorInspectorPlugin 3 | 4 | 5 | var attribute_editor = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_editor.gd") 6 | var attribute_effect_editor = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_effect_editor.gd") 7 | 8 | 9 | func _can_handle(object) -> bool: 10 | return object is GameplayAttributeMap 11 | 12 | 13 | func _parse_property(object: Object, type, name: String, hint_type, hint_string: String, usage_flags, wide: bool) -> bool: 14 | if type == 28 and name == "attributes": 15 | add_property_editor(name, attribute_editor.new(), true) 16 | return true 17 | if type == 28 and name == "attributes_affected": 18 | add_property_editor(name, attribute_effect_editor.new(), true) 19 | return true 20 | if type == 24 and name == "table": 21 | return true 22 | return false 23 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/turn_based/autoloads/turn_manager.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | 4 | ## The [TurnBasedGame] node. 5 | var turn_based_game: TurnBasedGame = null 6 | 7 | 8 | ## Called when the node enters the scene tree for the first time. 9 | func _ready() -> void: 10 | get_turn_based_game() 11 | 12 | 13 | ## Ends the current turn sequence. 14 | func end_turn_sequence() -> void: 15 | if get_turn_based_game(): 16 | get_turn_based_game().end_turn_sequence() 17 | 18 | 19 | ## Gets the [TurnBasedGame] node. 20 | func get_turn_based_game() -> TurnBasedGame: 21 | if turn_based_game: 22 | return turn_based_game 23 | 24 | for child in get_tree().get_nodes_in_group("ggs.turnbased"): 25 | if child is TurnBasedGame: 26 | turn_based_game = child 27 | return turn_based_game 28 | 29 | return null 30 | 31 | 32 | ## Starts a new turn sequence (aka, starts the turn based mode). 33 | func start_turn_sequence() -> void: 34 | if get_turn_based_game(): 35 | get_turn_based_game().start_turn_sequence() 36 | 37 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/DropIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bv0eepnnlr8rx" 6 | path="res://.godot/imported/DropIcon.png-5ab37537aade41394994437130bd02be.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropIcon.png" 14 | dest_files=["res://.godot/imported/DropIcon.png-5ab37537aade41394994437130bd02be.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://du6kdypiccr7d" 6 | path="res://.godot/imported/ItemIcon.png-e12a6e4cda0c5fff1bfec52b69b09882.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png" 14 | dest_files=["res://.godot/imported/ItemIcon.png-e12a6e4cda0c5fff1bfec52b69b09882.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/resources/stop_effect_at_value.gd: -------------------------------------------------------------------------------- 1 | class_name StopEffectAtValue extends AttributeEffectCondition 2 | 3 | @export_category("Rule") 4 | ## The value to stop at 5 | @export var value_to_stop_at: float = 0.0 6 | ## The value condition 7 | @export_enum("Equal", "Greater than", "Lesser than", "Greater Equal to", "Less Equal To") var operator = 0 8 | 9 | 10 | func should_apply(attribute_effect: AttributeEffect, effect: GameplayEffect, gameplay_attributes: GameplayAttributeMap) -> bool: 11 | var attribute = gameplay_attributes.get_attribute_by_name(attribute_effect.attribute_name) 12 | 13 | if attribute == null: 14 | return false 15 | 16 | match operator: 17 | 0: return attribute.current_value == value_to_stop_at 18 | 1: return attribute.current_value > value_to_stop_at 19 | 2: return attribute.current_value < value_to_stop_at 20 | 3: return attribute.current_value >= value_to_stop_at 21 | 4: return attribute.current_value <= value_to_stop_at 22 | 23 | 24 | return false 25 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/DropNode2D.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b8tbsu1m5bjj5" 6 | path="res://.godot/imported/DropNode2D.png-505269c443c6f334a033e07591488b87.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropNode2D.png" 14 | dest_files=["res://.godot/imported/DropNode2D.png-505269c443c6f334a033e07591488b87.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/DropNode3D.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c8b1c3dfii8cw" 6 | path="res://.godot/imported/DropNode3D.png-543cba8655b833053b0a70d8f6995260.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropNode3D.png" 14 | dest_files=["res://.godot/imported/DropNode3D.png-543cba8655b833053b0a70d8f6995260.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b2gocrfty8qoa" 6 | path="res://.godot/imported/InteractionIcon.png-a371fc1d70b6e0b25ca8b6ba3e430f41.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png" 14 | dest_files=["res://.godot/imported/InteractionIcon.png-a371fc1d70b6e0b25ca8b6ba3e430f41.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/DropGroupIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://u02qmnlc1nk4" 6 | path="res://.godot/imported/DropGroupIcon.png-082ecee561eb77062736d4b1913eae84.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropGroupIcon.png" 14 | dest_files=["res://.godot/imported/DropGroupIcon.png-082ecee561eb77062736d4b1913eae84.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/DropTableIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bnlfgtgq5m3es" 6 | path="res://.godot/imported/DropTableIcon.png-dc6f72115d3a8fc32c80629bde52acf7.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/DropTableIcon.png" 14 | dest_files=["res://.godot/imported/DropTableIcon.png-dc6f72115d3a8fc32c80629bde52acf7.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/EquipmentIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dsam1x2nnts1n" 6 | path="res://.godot/imported/EquipmentIcon.png-125ef11927f42a69bf3a8290bc401ce8.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/EquipmentIcon.png" 14 | dest_files=["res://.godot/imported/EquipmentIcon.png-125ef11927f42a69bf3a8290bc401ce8.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c0t3nn02g2aae" 6 | path="res://.godot/imported/InventoryIcon.png-3eabb97464864e508b14fd93db8fc3fb.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png" 14 | dest_files=["res://.godot/imported/InventoryIcon.png-3eabb97464864e508b14fd93db8fc3fb.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/Equipped3DIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bmrsp6wmi5u2w" 6 | path="res://.godot/imported/Equipped3DIcon.png-cf3a3c5bcbecf44b4b7c648464a80b39.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/Equipped3DIcon.png" 14 | dest_files=["res://.godot/imported/Equipped3DIcon.png-cf3a3c5bcbecf44b4b7c648464a80b39.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/assets/RadialMenuIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cxfy3v83r43w1" 6 | path="res://.godot/imported/RadialMenuIcon.png-94dd769b061766a6aa27a71907d4a786.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/inventory_system/assets/RadialMenuIcon.png" 14 | dest_files=["res://.godot/imported/RadialMenuIcon.png-94dd769b061766a6aa27a71907d4a786.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | TimedEffect 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/Interaction2DIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://kckk1u3tv7fw" 6 | path="res://.godot/imported/Interaction2DIcon.png-23ca3edeaeabcacfdfb729c9f4985386.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/Interaction2DIcon.png" 14 | dest_files=["res://.godot/imported/Interaction2DIcon.png-23ca3edeaeabcacfdfb729c9f4985386.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/Interaction3DIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://0un3a4vtlxma" 6 | path="res://.godot/imported/Interaction3DIcon.png-bc4642cc3b89e7fa7b7356ec961e15cb.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/Interaction3DIcon.png" 14 | dest_files=["res://.godot/imported/Interaction3DIcon.png-bc4642cc3b89e7fa7b7356ec961e15cb.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability@0.15x.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://snrls3ve2xjq" 6 | path="res://.godot/imported/Ability@0.15x.png-b03d46ca8ad1e8c852ba988c59548ada.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability@0.15x.png" 14 | dest_files=["res://.godot/imported/Ability@0.15x.png-b03d46ca8ad1e8c852ba988c59548ada.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cuotecpt76nmw" 6 | path="res://.godot/imported/InteractableArea2D.png-1439c320787289216c2f380ade2852b0.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractableArea2D.png" 14 | dest_files=["res://.godot/imported/InteractableArea2D.png-1439c320787289216c2f380ade2852b0.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c76kg8ddxlos6" 6 | path="res://.godot/imported/InteractableArea3D.png-a5a8ac4d0da793dba97b4827e8e0165b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractableArea3D.png" 14 | dest_files=["res://.godot/imported/InteractableArea3D.png-a5a8ac4d0da793dba97b4827e8e0165b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute@0.15x.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://mdi7o2crtoen" 6 | path="res://.godot/imported/Attribute@0.15x.png-0f5f51bdd0a39e19d0753428626a2d40.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute@0.15x.png" 14 | dest_files=["res://.godot/imported/Attribute@0.15x.png-0f5f51bdd0a39e19d0753428626a2d40.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bbd35kb6frop4" 6 | path="res://.godot/imported/TimedEffect@0.15x.png-1f83d729305101aeba11b6b93a23f59e.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png" 14 | dest_files=["res://.godot/imported/TimedEffect@0.15x.png-1f83d729305101aeba11b6b93a23f59e.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cv7yqsjuso70y" 6 | path="res://.godot/imported/InteractionRayCast2DIcon.png-d59ca4ade7a08b61362770f1a1e0f239.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png" 14 | dest_files=["res://.godot/imported/InteractionRayCast2DIcon.png-d59ca4ade7a08b61362770f1a1e0f239.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b8rnslakt6c41" 6 | path="res://.godot/imported/InteractionRayCast3DIcon.png-0a6b8a5be286279c55c50412ed88b36d.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png" 14 | dest_files=["res://.godot/imported/InteractionRayCast3DIcon.png-0a6b8a5be286279c55c50412ed88b36d.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable@0.15x.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://djqr0krbf5bnc" 6 | path="res://.godot/imported/AttributeTable@0.15x.png-d256c289513bc255c1617d373c1c315f.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable@0.15x.png" 14 | dest_files=["res://.godot/imported/AttributeTable@0.15x.png-d256c289513bc255c1617d373c1c315f.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://ct6ls2s3ek5xy" 6 | path="res://.godot/imported/GameplayEffect@0.15x.png-522233c4cfda5c629c6801ea7eb9bcb3.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect@0.15x.png" 14 | dest_files=["res://.godot/imported/GameplayEffect@0.15x.png-522233c4cfda5c629c6801ea7eb9bcb3.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer@0.15x.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cm1prlnq3noon" 6 | path="res://.godot/imported/AbilityContainer@0.15x.png-dbf25c66b7ab7c8127c14a3ef31b5f9c.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer@0.15x.png" 14 | dest_files=["res://.godot/imported/AbilityContainer@0.15x.png-dbf25c66b7ab7c8127c14a3ef31b5f9c.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect@0.15x.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dico0si16oo6u" 6 | path="res://.godot/imported/AttributeEffect@0.15x.png-0ff52db701b052fdb134a2b52898e627.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect@0.15x.png" 14 | dest_files=["res://.godot/imported/AttributeEffect@0.15x.png-0ff52db701b052fdb134a2b52898e627.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://qdrl51ys1mv7" 6 | path="res://.godot/imported/TimedEffectOneShot@0.15x.png-dec4305913ec60d3bdba7a1c17434a4e.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png" 14 | dest_files=["res://.godot/imported/TimedEffectOneShot@0.15x.png-dec4305913ec60d3bdba7a1c17434a4e.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap@0.15x.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://2wdpq1f5p81h" 6 | path="res://.godot/imported/GameplayAttributeMap@0.15x.png-9773e9306c196855a875eb5d05f0adf6.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap@0.15x.png" 14 | dest_files=["res://.godot/imported/GameplayAttributeMap@0.15x.png-9773e9306c196855a875eb5d05f0adf6.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | GameplayAttributeMap 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Paolo Roth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c2d8ar1vogsgq" 6 | path="res://.godot/imported/Ability.svg-3f9962c26262717fc95c0b689b1f9c2c.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability.svg" 14 | dest_files=["res://.godot/imported/Ability.svg-3f9962c26262717fc95c0b689b1f9c2c.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cp7geybwp22pi" 6 | path="res://.godot/imported/Attribute.svg-f7a91e28dba79614248d03df87724328.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Attribute.svg" 14 | dest_files=["res://.godot/imported/Attribute.svg-f7a91e28dba79614248d03df87724328.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://b8osbt147xfdt" 6 | path="res://.godot/imported/TimedEffect.svg-7e0221bc7c621cbe2da09d683aeade15.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect.svg" 14 | dest_files=["res://.godot/imported/TimedEffect.svg-7e0221bc7c621cbe2da09d683aeade15.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cbhnaxdu8v24d" 6 | path="res://.godot/imported/AttributeTable.svg-5081b1f9f1075c6b197e825443a2b656.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable.svg" 14 | dest_files=["res://.godot/imported/AttributeTable.svg-5081b1f9f1075c6b197e825443a2b656.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bgfn647m855hg" 6 | path="res://.godot/imported/GameplayEffect.svg-2500388b0e2b1a8e049ad93ae746c2d3.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect.svg" 14 | dest_files=["res://.godot/imported/GameplayEffect.svg-2500388b0e2b1a8e049ad93ae746c2d3.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cmp3udx3jf0ut" 6 | path="res://.godot/imported/AttributeEffect.svg-59c4a27403293b505775db1a7d596d4c.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect.svg" 14 | dest_files=["res://.godot/imported/AttributeEffect.svg-59c4a27403293b505775db1a7d596d4c.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dn54fmpqnjrlm" 6 | path="res://.godot/imported/AbilityContainer.svg-94a2d27656beadd249024abbd7dad512.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer.svg" 14 | dest_files=["res://.godot/imported/AbilityContainer.svg-94a2d27656beadd249024abbd7dad512.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bpcerra7enco5" 6 | path="res://.godot/imported/TimedEffectOneShot.svg-618699cade701d04ede8d12766983954.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot.svg" 14 | dest_files=["res://.godot/imported/TimedEffectOneShot.svg-618699cade701d04ede8d12766983954.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dnu24g4ay5c6t" 6 | path="res://.godot/imported/GameplayAttributeMap.svg-a2ab7db92d9ac565eefeda7ab43c93c4.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap.svg" 14 | dest_files=["res://.godot/imported/GameplayAttributeMap.svg-a2ab7db92d9ac565eefeda7ab43c93c4.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeTable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | AttributeTable 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/nodes/radial_menu_container.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Container 3 | 4 | 5 | func _draw() -> void: 6 | var child_count = get_child_count() 7 | var pie_slice_radians = 2 * PI / child_count 8 | var child_index = 0 9 | 10 | for child in get_children(): 11 | if child is Control: 12 | # move child to a pie slice starting from the center of the container 13 | var child_angle = child_index * pie_slice_radians 14 | var child_radius = min(size.x, size.y) / 2 15 | var child_position = Vector2( 16 | child_radius * cos(child_angle), 17 | child_radius * sin(child_angle) 18 | ) 19 | 20 | child_position.x += size.x / 2 - child.size.x / 2 21 | child_position.y += size.y / 2 - child.size.y / 2 22 | 23 | child.set_position(child_position) 24 | 25 | child_index += 1 26 | 27 | 28 | func _enter_tree() -> void: 29 | child_entered_tree.connect(_handle_child_entered_tree) 30 | child_exiting_tree.connect(_handle_child_exiting_tree) 31 | 32 | 33 | func _handle_child_entered_tree(child: Node) -> void: 34 | _draw() 35 | 36 | 37 | func _handle_child_exiting_tree(child: Node) -> void: 38 | _draw() 39 | 40 | 41 | func _process(delta: float) -> void: 42 | if Engine.is_editor_hint(): 43 | _draw() -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/plugin.gd: -------------------------------------------------------------------------------- 1 | extends EditorPlugin 2 | 3 | 4 | func _enter_tree() -> void: 5 | add_custom_type("InteractableArea2D", "Area2D",preload("./nodes/2d/interactable_area_2d.gd"),preload("./assets/InteractableArea2D.png")) 6 | add_custom_type("InteractableArea3D", "Area3D",preload("./nodes/3d/interactable_area_3d.gd"),preload("./assets/InteractableArea2D.png")) 7 | add_custom_type("InteractionRayCast2D", "RayCast2D",preload("./nodes/2d/interaction_raycast_2d.gd"),preload("./assets/InteractionRayCast2DIcon.png")) 8 | add_custom_type("InteractionRayCast3D", "RayCast3D",preload("./nodes/3d/interaction_raycast_3d.gd"),preload("./assets/InteractionRayCast3DIcon.png")) 9 | add_custom_type("InteractionManager", "Node",preload("./nodes/interaction_manager.gd"),preload("./assets/InteractionIcon.png")) 10 | add_custom_type("Interaction", "Resource",preload("./resources/interaction.gd"),preload("./assets/InteractionIcon.png")) 11 | 12 | 13 | func _exit_tree() -> void: 14 | remove_custom_type("Interaction") 15 | remove_custom_type("InteractionManager") 16 | remove_custom_type("InteractableArea2D") 17 | remove_custom_type("InteractableArea3D") 18 | remove_custom_type("InteractionRayCast2D") 19 | remove_custom_type("InteractionRayCast3D") 20 | 21 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/resources/drop_group.gd: -------------------------------------------------------------------------------- 1 | class_name DropGroup extends Resource 2 | 3 | ## Represents a drop rule. 4 | ## 5 | ## 6 | 7 | @export_category("Drop") 8 | ## Represents the pool of droppable [Item]s. 9 | @export var items_pool: Array[Item] = [] 10 | ## Represents the chance of dropping one item from the [member DropGroup.items_pool] pool. 11 | ## [br]The higher the value, the higher the chances. 12 | @export_range(0.0, 100.0, 0.01) var drop_chance: float = 50.0 13 | 14 | 15 | ## Returns if the group can drop an item from the [member DropGroup.items_pool] pool. 16 | ## [br]It accepts an optional argument [code]drop_modifier[/code] which increases (or decreases if negative) drop chances. 17 | ## [br]The greater the modifier, the higher the chances to drop an [Item] from the pool. 18 | func can_be_dropped(modifier: float = 0.0) -> bool: 19 | return drop_chance + modifier >= randf_range(0.0, 100.0) 20 | 21 | 22 | ## Drops an [Item]. 23 | ## [br]If the drop is unsuccessfull, it returns [code]null[/code]. 24 | ## [br]It accepts an optional argument [code]drop_modifier[/code] which increases (or decreases if negative) drop chances. 25 | ## [br]The greater the modifier, the higher the chances to drop an [Item] from the pool. 26 | func drop(drop_modifier: float = 0.0) -> Item: 27 | if can_be_dropped(drop_modifier): 28 | return items_pool.pick_random() 29 | 30 | return null 31 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/nodes/2d/interaction_raycast_2d.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractionRayCast2DIcon.png") 3 | class_name InteractionRayCast2D extends RayCast2D 4 | 5 | 6 | ## Emitted when an [InteractableArea2D] is detected. 7 | signal interactable_detected(interactable: InteractableArea2D) 8 | signal interactable_focus_in(interactable: InteractableArea2D) 9 | signal interactable_focus_out(interactable: InteractableArea2D) 10 | 11 | 12 | @export_category("Interaction owner") 13 | @export_node_path("InteractionManager") var interaction_manager_path := NodePath() 14 | 15 | 16 | var manager: Node = null 17 | var interacting := false 18 | var previous_collider: Object = null 19 | 20 | 21 | func _get_configuration_warnings() -> PackedStringArray: 22 | if interaction_manager_path == null or interaction_manager_path.is_empty(): 23 | return ["Tag manager path cannot be empty"] 24 | return [] 25 | 26 | 27 | func _ready() -> void: 28 | collide_with_areas = true 29 | manager = get_node(interaction_manager_path) 30 | 31 | 32 | func _physics_process(delta: float) -> void: 33 | var collider = get_collider() 34 | 35 | if collider is InteractableArea2D: 36 | interactable_detected.emit(collider) 37 | interactable_focus_in.emit(collider) 38 | elif collider == null and previous_collider != null: 39 | interactable_focus_out.emit(previous_collider) 40 | previous_collider = null 41 | 42 | previous_collider = collider 43 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/nodes/3d/interaction_raycast_3d.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractionRayCast3DIcon.png") 3 | class_name InteractionRayCast3D extends RayCast3D 4 | 5 | 6 | ## Emitted when an [InteractableArea3D] is detected. 7 | signal interactable_detected(interactable: InteractableArea3D) 8 | signal interactable_focus_in(interactable: InteractableArea3D) 9 | signal interactable_focus_out(interactable: InteractableArea3D) 10 | 11 | 12 | @export_category("Interaction owner") 13 | @export_node_path("InteractionManager") var interaction_manager_path := NodePath() 14 | 15 | 16 | var manager: Node = null 17 | var interacting := false 18 | var previous_collider: Object = null 19 | 20 | 21 | func _get_configuration_warnings() -> PackedStringArray: 22 | if interaction_manager_path == null or interaction_manager_path.is_empty(): 23 | return ["Tag manager path cannot be empty"] 24 | return [] 25 | 26 | 27 | func _ready() -> void: 28 | collide_with_areas = true 29 | manager = get_node(interaction_manager_path) 30 | 31 | 32 | func _physics_process(delta: float) -> void: 33 | var collider = get_collider() 34 | 35 | if collider is InteractableArea3D: 36 | interactable_detected.emit(collider) 37 | interactable_focus_in.emit(collider) 38 | elif collider == null and previous_collider != null: 39 | interactable_focus_out.emit(previous_collider) 40 | previous_collider = null 41 | 42 | previous_collider = collider 43 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/turn_based/nodes/TurnSubscriber.gd: -------------------------------------------------------------------------------- 1 | class_name TurnSubscriber extends Node 2 | 3 | ## A turn based participant 4 | ## 5 | ## It represents a scene which can partecipate to a turn based game 6 | 7 | 8 | ## Emitted when this subscriber turn ends 9 | signal turn_ended() 10 | ## Emitted when this subscriber turn starts 11 | signal turn_started() 12 | ## Emitter when the turn round ended, 13 | signal turn_round_ended() 14 | 15 | 16 | @export_group("Turn based game") 17 | ## Priority of this subscriber. The higher the priority, the sooner the turn starts. 18 | @export var priority: int = 0 19 | 20 | 21 | func _enter_tree() -> void: 22 | if TurnManager.get_turn_based_game() != null: 23 | TurnManager.get_turn_based_game().add_subscriber(self) 24 | 25 | 26 | func _exit_tree() -> void: 27 | if TurnManager.get_turn_based_game() != null: 28 | TurnManager.get_turn_based_game().remove_subscriber(self) 29 | 30 | 31 | func _ready() -> void: 32 | add_to_group("ggs.turnbased") 33 | 34 | if TurnManager.get_turn_based_game() != null: 35 | TurnManager.get_turn_based_game().add_subscriber(self) 36 | 37 | 38 | ## Called when the turn round ended 39 | ## [br]It's a virtual method, it can be overrided 40 | func _turn_ended() -> void: 41 | pass 42 | 43 | 44 | ## Called when the turn round started 45 | ## [br]It's a virtual method, it can be overrided 46 | func _turn_started() -> void: 47 | pass 48 | 49 | 50 | ## Ends the turn of this subscriber 51 | func end_turn() -> void: 52 | if TurnManager.get_turn_based_game() != null: 53 | TurnManager.get_turn_based_game().next_turn() 54 | 55 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/objects/item_activation_event.gd: -------------------------------------------------------------------------------- 1 | class_name ItemActivationEvent extends RefCounted 2 | 3 | ## The activation type 4 | var activation_type: int = 0 5 | ## [code]true[/code] if owner is not [code]null[/code], [code]false[/code] otherwise. 6 | var has_owner: bool: 7 | get: 8 | return owner != null 9 | ## The inventory owner 10 | var owner: Node 11 | ## The equipment which emits the activation event or is bound to an [Inventory]. It could be [code]null[/code]. 12 | var equipment: Equipment 13 | ## The inventory which emits the activation event or is bound to an [Equipment]. It could be [code]null[/code]. 14 | var inventory: Inventory 15 | 16 | 17 | ## Builds the event 18 | func _init(_inventory_or_equipment: Variant, _activation_type: int = 0) -> void: 19 | var someone_really_called_me = false 20 | 21 | assert(_activation_type != null, "activation_type cannot be null") 22 | 23 | activation_type = _activation_type 24 | 25 | if _inventory_or_equipment is Inventory: 26 | inventory = _inventory_or_equipment 27 | equipment = inventory.equipment 28 | someone_really_called_me = true 29 | 30 | if not inventory.owner_path.is_empty(): 31 | owner = inventory.get_node(inventory.owner_path) 32 | 33 | if _inventory_or_equipment is Equipment: 34 | equipment = _inventory_or_equipment 35 | inventory = equipment.inventory 36 | someone_really_called_me = true 37 | 38 | if not equipment.owner_path.is_empty(): 39 | owner = equipment.get_node(equipment.owner_path) 40 | 41 | assert(someone_really_called_me, "an ItemActivationEvent can be called by an Equipment or Inventory node") 42 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | TimedEffectOneShot 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_editor_row.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends VBoxContainer 3 | 4 | 5 | signal attribute_updated(key: String, value: float) 6 | 7 | 8 | @export_category("Attribute") 9 | @export var attribute: AttributeResource = null: 10 | get: 11 | return attribute 12 | set(value): 13 | attribute = value 14 | _update_ui() 15 | 16 | 17 | @onready var label: Label = $Label 18 | @onready var max_value: SpinBox = $GridContainer/MaximumValue/SpinBox 19 | @onready var min_value: SpinBox = $GridContainer/MinimumValue/SpinBox 20 | @onready var cur_value: SpinBox = $GridContainer/CurrentValue/SpinBox 21 | 22 | 23 | var _updating_ui = false 24 | 25 | 26 | func _init() -> void: 27 | pass 28 | 29 | 30 | func _ready() -> void: 31 | max_value.step = 0.01 32 | min_value.step = 0.01 33 | cur_value.step = 0.01 34 | max_value.value_changed.connect(func (value): 35 | if not _updating_ui: 36 | attribute_updated.emit("maximum_value", value) 37 | ) 38 | min_value.value_changed.connect(func (value): 39 | if not _updating_ui: 40 | attribute_updated.emit("minimum_value", value) 41 | ) 42 | cur_value.value_changed.connect(func (value): 43 | if not _updating_ui: 44 | attribute_updated.emit("current_value", value) 45 | ) 46 | 47 | _update_ui() 48 | 49 | 50 | func _update_ui() -> void: 51 | if attribute != null: 52 | _updating_ui = true 53 | label.text = attribute.attribute_name 54 | max_value.value = attribute.maximum_value 55 | min_value.value = attribute.minimum_value 56 | cur_value.value = attribute.current_value 57 | _updating_ui = false 58 | 59 | 60 | func set_values(res: AttributeResource) -> void: 61 | await ready 62 | attribute = res 63 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/nodes/pickable_item_2d.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Area2D 3 | 4 | 5 | ## It represents an [Item] which can be picked in a 2D world. 6 | 7 | 8 | @export_category("Item Pick") 9 | ## Automatically triggers a pick if the collision layer hits this area. 10 | @export var automatically_pick: bool = false 11 | ## The item to pick. 12 | @export var item: Item = null: 13 | get: 14 | return item 15 | set(value): 16 | item = value 17 | _render() 18 | 19 | 20 | ## A reference to the rendered [Item]. 21 | var rendered_item: Node2D 22 | 23 | 24 | ## Handles the collision 25 | func _handle_collision(body: Node2D) -> void: 26 | if automatically_pick: 27 | if body.has_meta("ggsInventory"): 28 | pick(body.get_meta("ggsInventory", null)) 29 | elif body.has_meta("ggsEquipment"): 30 | pick(body.get_meta("ggsEquipment", null)) 31 | 32 | 33 | ## Renders the bound [Item] only if it has a valid [member Item.scene] member. 34 | func _render() -> void: 35 | if item and item.scene and item.scene.can_instantiate(): 36 | if rendered_item: 37 | rendered_item.queue_free() 38 | 39 | rendered_item = item.scene.instantiate() 40 | add_child(rendered_item) 41 | 42 | 43 | func _ready() -> void: 44 | if not Engine.is_editor_hint(): 45 | area_entered.connect(_handle_collision) 46 | body_entered.connect(_handle_collision) 47 | 48 | 49 | ## Triggers the pick mechanism programmatically. 50 | func pick(by: Node) -> void: 51 | assert(item != null, "item should not be null") 52 | 53 | if by is Inventory: 54 | if by.can_add(item): 55 | by.add_item(item) 56 | queue_free() 57 | elif by is Equipment: 58 | ## Adds tags because it "picked" the item. 59 | by.add_tags(item.tags_added_on_add) 60 | 61 | for slot in by.slots: 62 | if slot.can_equip(item): 63 | slot.equip(item) 64 | 65 | queue_free() 66 | 67 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/nodes/pickable_item_3d.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Area3D 3 | 4 | 5 | ## It represents an [Item] which can be picked in a 3D world. 6 | 7 | 8 | @export_category("Item Pick") 9 | ## Automatically triggers a pick if the collision layer hits this area. 10 | @export var automatically_pick: bool = false 11 | ## The item to pick. 12 | @export var item: Item = null: 13 | get: 14 | return item 15 | set(value): 16 | item = value 17 | _render() 18 | 19 | 20 | ## A reference to the rendered [Item]. 21 | var rendered_item: Node3D 22 | 23 | 24 | ## Handles the collision 25 | func _handle_collision(body: Node3D) -> void: 26 | if automatically_pick: 27 | if body.has_meta("ggsInventory"): 28 | pick(body.get_meta("ggsInventory", null)) 29 | elif body.has_meta("ggsEquipment"): 30 | pick(body.get_meta("ggsEquipment", null)) 31 | 32 | 33 | ## Renders the bound [Item] only if it has a valid [member Item.scene] member. 34 | func _render() -> void: 35 | if item and item.scene and item.scene.can_instantiate(): 36 | if rendered_item: 37 | rendered_item.queue_free() 38 | 39 | rendered_item = item.scene.instantiate() 40 | add_child(rendered_item) 41 | 42 | 43 | func _ready() -> void: 44 | if not Engine.is_editor_hint(): 45 | area_entered.connect(_handle_collision) 46 | body_entered.connect(_handle_collision) 47 | 48 | 49 | ## Triggers the pick mechanism programmatically. 50 | func pick(by: Node) -> void: 51 | assert(item != null, "item should not be null") 52 | 53 | if by is Inventory: 54 | if by.can_add(item): 55 | by.add_item(item) 56 | queue_free() 57 | elif by is Equipment: 58 | ## Adds tags because it "picked" the item. 59 | by.add_tags(item.tags_added_on_add) 60 | 61 | for slot in by.slots: 62 | if slot.can_equip(item): 63 | slot.equip(item) 64 | 65 | queue_free() 66 | 67 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | const attributes_and_abilities_plugin_script = preload("./attributes_and_abilities/plugin.gd") 5 | const camera_shake_plugin_script = preload("./camera_shake/plugin.gd") 6 | const extended_character_nodes_script = preload("./extended_character_nodes/plugin.gd") 7 | const inventory_system_script = preload("./inventory_system/plugin.gd") 8 | const interactables_script = preload("./interactables/plugin.gd") 9 | const slideshow_script = preload("./slideshow/plugin.gd") 10 | const turn_based_script = preload("./turn_based/plugin.gd") 11 | 12 | 13 | var attributes_and_abilities_plugin: EditorPlugin 14 | var camera_shake_plugin: EditorPlugin 15 | var extended_character_nodes: EditorPlugin 16 | var inventory_system: EditorPlugin 17 | var interactables: EditorPlugin 18 | var slideshow: EditorPlugin 19 | var turn_based: EditorPlugin 20 | 21 | 22 | func _init() -> void: 23 | attributes_and_abilities_plugin = attributes_and_abilities_plugin_script.new() 24 | camera_shake_plugin = camera_shake_plugin_script.new() 25 | extended_character_nodes = extended_character_nodes_script.new() 26 | inventory_system = inventory_system_script.new() 27 | interactables = interactables_script.new() 28 | slideshow = slideshow_script.new() 29 | turn_based = turn_based_script.new() 30 | 31 | 32 | func _enter_tree(): 33 | attributes_and_abilities_plugin._enter_tree() 34 | camera_shake_plugin._enter_tree() 35 | extended_character_nodes._enter_tree() 36 | inventory_system._enter_tree() 37 | interactables._enter_tree() 38 | slideshow._enter_tree() 39 | turn_based._enter_tree() 40 | 41 | 42 | func _exit_tree(): 43 | attributes_and_abilities_plugin._exit_tree() 44 | camera_shake_plugin._exit_tree() 45 | extended_character_nodes._exit_tree() 46 | inventory_system._exit_tree() 47 | interactables._exit_tree() 48 | slideshow._exit_tree() 49 | turn_based._exit_tree() 50 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/nodes/effected_area2d.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name EffectedArea2D extends Area2D 3 | 4 | ## An extension to base class with automatism for GameplayAttributeMap 5 | ## 6 | ## Applies [GameplayEffect]s automatically when a collision is triggered and [method should_apply_effect] returns [code]true[/code]. 7 | 8 | 9 | @export_category("Gameplay Effect Handling") 10 | ## Removes the EffectedArea2D if [method should_apply_effect] returns [code]true[/code] 11 | @export var remove_self_on_apply := true 12 | ## Removes all child [GameplayEffect]s when colliding and when [method should_apply_effect] returns [code]true[/code] 13 | @export var remove_effects_on_apply := true 14 | 15 | 16 | ## Gets all child GameplayEffect nodes 17 | var effects: Array[GameplayEffect] = []: 18 | get: 19 | var _effects: Array[GameplayEffect] = [] 20 | 21 | for child in get_children(): 22 | if child is GameplayEffect: 23 | _effects.append(child) 24 | 25 | return _effects 26 | 27 | 28 | func _ready() -> void: 29 | if not Engine.is_editor_hint(): 30 | area_entered.connect(func (body: Area2D): 31 | if should_apply_effect(body): 32 | for effect in effects: 33 | if remove_effects_on_apply: 34 | remove_child(effect) 35 | body.add_child(effect) 36 | else: 37 | body.add_child(effect.duplicate()) 38 | if remove_self_on_apply: 39 | queue_free() 40 | ) 41 | 42 | body_entered.connect(func (body: Node2D): 43 | if should_apply_effect(body): 44 | for effect in effects: 45 | if remove_effects_on_apply: 46 | remove_child(effect) 47 | body.add_child(effect) 48 | else: 49 | body.add_child(effect.duplicate()) 50 | if remove_self_on_apply: 51 | queue_free() 52 | ) 53 | 54 | 55 | ## Returns true if the effect should be applied to a certain node 56 | func should_apply_effect(node: Node2D) -> bool: 57 | return true 58 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/nodes/effected_area3d.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name EffectedArea3D extends Area3D 3 | 4 | ## An extension to base class with automatism for GameplayAttributeMap 5 | ## 6 | ## Applies [GameplayEffect]s automatically when a collision is triggered and [method should_apply_effect] returns [code]true[/code]. 7 | 8 | 9 | @export_category("Gameplay Effect Handling") 10 | ## Removes the EffectedArea2D if [method should_apply_effect] returns [code]true[/code] 11 | @export var remove_self_on_apply := true 12 | ## Removes all child [GameplayEffect]s when colliding and when [method should_apply_effect] returns [code]true[/code] 13 | @export var remove_effects_on_apply := true 14 | 15 | 16 | ## Gets all child GameplayEffect nodes 17 | var effects: Array[GameplayEffect] = []: 18 | get: 19 | var _effects: Array[GameplayEffect] = [] 20 | 21 | for child in get_children(): 22 | if child is GameplayEffect: 23 | _effects.append(child) 24 | 25 | return _effects 26 | 27 | 28 | func _ready() -> void: 29 | if not Engine.is_editor_hint(): 30 | area_entered.connect(func (body: Area3D): 31 | if should_apply_effect(body): 32 | for effect in effects: 33 | if remove_effects_on_apply: 34 | remove_child(effect) 35 | body.add_child(effect) 36 | else: 37 | body.add_child(effect.duplicate()) 38 | 39 | if remove_self_on_apply: 40 | queue_free() 41 | ) 42 | 43 | body_entered.connect(func (body: Node3D): 44 | if should_apply_effect(body): 45 | for effect in effects: 46 | if remove_effects_on_apply: 47 | remove_child(effect) 48 | body.add_child(effect) 49 | else: 50 | body.add_child(effect.duplicate()) 51 | 52 | if remove_self_on_apply: 53 | queue_free() 54 | ) 55 | 56 | 57 | ## Returns true if the effect should be applied to a certain node 58 | func should_apply_effect(node: Node3D) -> bool: 59 | return true 60 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayEffect.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | GameplayEffect 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/objects/activation_event.gd: -------------------------------------------------------------------------------- 1 | ## Is the activation event object sent to an ability 2 | ## 3 | ## This class is used to pass down to an [Ability] all the informations needed for it's own execution. 4 | 5 | class_name ActivationEvent extends RefCounted 6 | 7 | ## It's the executing [AbilityContainer] which passed the [ActivationEvent] to an [Ability] 8 | var ability_container: AbilityContainer 9 | ## It's the [GameplayAttributeMap] bound to an [AbilityContainer] 10 | var attribute_map: GameplayAttributeMap 11 | ## Returns a [Node] as the owning character 12 | var character: Node: 13 | get: 14 | if has_attribute_map and attribute_map.owning_character != null and not attribute_map.owning_character.is_empty(): 15 | return attribute_map.get_node(attribute_map.owning_character) 16 | return null 17 | ## [code]true[/code] if [member ActivationEvent.ability_container] is not [code]null[/code], [code]false[/code] otherwise. 18 | var has_ability_container: bool: 19 | get: 20 | return ability_container != null 21 | ## [code]true[/code] if [member ActivationEvent.attribute_map] is not [code]null[/code], [code]false[/code] otherwise. 22 | var has_attribute_map: bool: 23 | get: 24 | return attribute_map != null 25 | ## Returns if the [ActivationEvent] has been created with some tags 26 | var has_tags: bool: 27 | get: 28 | return tags.size() > 0 29 | ## These are the tags set when the [Ability] has been activated using [method Ability.try_activate] 30 | var tags: Array[String] = [] 31 | 32 | 33 | func _init(_ability_container: AbilityContainer) -> void: 34 | ability_container = _ability_container 35 | attribute_map = _ability_container.gameplay_attribute_map 36 | tags = _ability_container.tags.duplicate() 37 | 38 | 39 | ## Gets an [AttributeSpec] attribute by it's name 40 | func get_attribute(attribute_name: String) -> AttributeSpec: 41 | if has_attribute_map: 42 | return attribute_map.get_attribute_by_name(attribute_name) 43 | return null 44 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/nodes/equipped_item_3d.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name EquippedItem3D extends Node3D 3 | 4 | 5 | ## Displays the current equipped [Item] from a specific [Equipment] node. 6 | 7 | 8 | ## Emitted when an [Item] is displayed. 9 | signal item_displayed(item: Item) 10 | ## Emitted when an [Item] is hidden. 11 | signal item_hidden(item: Item) 12 | 13 | 14 | @export_category("Equipment") 15 | ## The path to the [Equipment] node 16 | @export_node_path("Equipment") var equipment_path: NodePath = NodePath(): 17 | get: 18 | return equipment_path 19 | set(value): 20 | equipment_path = value 21 | update_configuration_warnings() 22 | 23 | @export_group("Tagging", "tags_") 24 | ## Displays an equipped [Item] only if both it has these the [member Item.tags] tags. 25 | @export var tags_to_display: Array[String] = [] 26 | 27 | 28 | var current: Node3D = null 29 | 30 | 31 | func _init() -> void: 32 | if Engine.is_editor_hint(): 33 | update_configuration_warnings() 34 | 35 | 36 | func _ready() -> void: 37 | assert(not equipment_path.is_empty(), "equipment_path cannot be empty") 38 | 39 | var equipment = get_node(equipment_path) 40 | 41 | equipment.equipped.connect(func (item: Item, _slot: EquipmentSlot): 42 | if tags_to_display.size() > 0: 43 | var has_tags = true 44 | for tag in tags_to_display: 45 | if not item.tags.has(tag): 46 | has_tags = false 47 | break 48 | if not has_tags: 49 | return 50 | 51 | if item.scene and item.scene.can_instantiate(): 52 | current = item.scene.instantiate() 53 | add_child(current) 54 | ) 55 | 56 | equipment.unequipped.connect(func (_item: Item, _slot: EquipmentSlot): 57 | if current: 58 | remove_child(current) 59 | current = null 60 | ) 61 | 62 | 63 | func _get_configuration_warnings() -> PackedStringArray: 64 | var errors: Array[String] = [] 65 | 66 | if equipment_path == null: 67 | errors.append("equipment_path cannot be null") 68 | 69 | if equipment_path != null and equipment_path.is_empty(): 70 | errors.append("equipment_path cannot be empty") 71 | 72 | return PackedStringArray(errors) 73 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_editor_row.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3] 2 | 3 | [ext_resource type="Script" path="res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_editor_row.gd" id="1_ceu2s"] 4 | 5 | [node name="AttributeEditorRow" type="VBoxContainer"] 6 | offset_right = 1152.0 7 | offset_bottom = 101.0 8 | grow_horizontal = 2 9 | grow_vertical = 2 10 | size_flags_horizontal = 3 11 | script = ExtResource("1_ceu2s") 12 | 13 | [node name="Label" type="Label" parent="."] 14 | layout_mode = 2 15 | theme_type_variation = &"HeaderSmall" 16 | vertical_alignment = 1 17 | 18 | [node name="LabelSeparator" type="HSeparator" parent="."] 19 | layout_mode = 2 20 | 21 | [node name="GridContainer" type="GridContainer" parent="."] 22 | layout_mode = 2 23 | columns = 3 24 | 25 | [node name="MaximumValue" type="VBoxContainer" parent="GridContainer"] 26 | layout_mode = 2 27 | size_flags_horizontal = 3 28 | 29 | [node name="Label" type="Label" parent="GridContainer/MaximumValue"] 30 | layout_mode = 2 31 | size_flags_horizontal = 3 32 | text = "Maximum" 33 | clip_text = true 34 | 35 | [node name="SpinBox" type="SpinBox" parent="GridContainer/MaximumValue"] 36 | layout_mode = 2 37 | size_flags_horizontal = 3 38 | allow_greater = true 39 | allow_lesser = true 40 | alignment = 2 41 | select_all_on_focus = true 42 | 43 | [node name="MinimumValue" type="VBoxContainer" parent="GridContainer"] 44 | layout_mode = 2 45 | size_flags_horizontal = 3 46 | 47 | [node name="Label" type="Label" parent="GridContainer/MinimumValue"] 48 | layout_mode = 2 49 | size_flags_horizontal = 3 50 | text = "Minimum" 51 | clip_text = true 52 | 53 | [node name="SpinBox" type="SpinBox" parent="GridContainer/MinimumValue"] 54 | layout_mode = 2 55 | size_flags_horizontal = 3 56 | allow_greater = true 57 | allow_lesser = true 58 | alignment = 2 59 | select_all_on_focus = true 60 | 61 | [node name="CurrentValue" type="VBoxContainer" parent="GridContainer"] 62 | layout_mode = 2 63 | size_flags_horizontal = 3 64 | 65 | [node name="Label" type="Label" parent="GridContainer/CurrentValue"] 66 | layout_mode = 2 67 | size_flags_horizontal = 3 68 | text = "Current" 69 | clip_text = true 70 | 71 | [node name="SpinBox" type="SpinBox" parent="GridContainer/CurrentValue"] 72 | layout_mode = 2 73 | size_flags_horizontal = 3 74 | allow_greater = true 75 | allow_lesser = true 76 | alignment = 2 77 | select_all_on_focus = true 78 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/resources/equipment_slot.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name EquipmentSlot extends Resource 3 | 4 | 5 | ## Represents an equipment slot. 6 | 7 | 8 | ## Emitted when an [Item] is equipped. 9 | ## [br]Note, this will always be emitted after the [signal EquipmentSlot.unequipped_item] signal if a previous [Item] was equipped. 10 | signal item_equipped(item: Item, slot: EquipmentSlot) 11 | ## Emitted if the [Item] passed to [method EquipmentSlot.equip] method is not acceptable. 12 | signal item_refused_to_equip(item: Item, slot: EquipmentSlot) 13 | ## Emitted when an [Item] is unequipped. 14 | signal item_unequipped(item: Item, slot: EquipmentSlot) 15 | 16 | 17 | @export_category("Slot") 18 | ## The name given to this slot. 19 | ## [br]It is useful to be queried by [method Equipment.activate_by_name] method. 20 | @export var name: String = "" 21 | ## A white-list [Array] of accepted [Item]s. 22 | ## [br]Note: if this array is empty, all [Item]s will be eligible to be equipped, so beware. 23 | @export var accepted_items: Array[Item] = [] 24 | ## The current equipped [Item] 25 | @export var equipped: Item = null 26 | 27 | 28 | ## [br]Is [code]true[/code] if an [Item] is equipped, [code]false[/code] otherwise. 29 | var has_equipped_item: bool: 30 | get: 31 | return equipped != null 32 | ## [br]Is [code]true[/code] if no [Item] are equipped, [code]false[/code] otherwise. 33 | var is_free: bool: 34 | get: 35 | return equipped == null 36 | 37 | 38 | func _init(_accepted_items: Array[Item] = [], _equipped_item: Item = null) -> void: 39 | accepted_items = _accepted_items 40 | 41 | if can_equip(_equipped_item): 42 | equipped = _equipped_item 43 | 44 | 45 | ## Determines if the slot can accept an [Item] 46 | func can_equip(item: Item) -> bool: 47 | if item == null: 48 | return false 49 | 50 | if accepted_items.size() == 0: 51 | return true 52 | 53 | for accepted in accepted_items: 54 | if accepted.name == item.name: 55 | return true 56 | 57 | return false 58 | 59 | 60 | ## Equips an [Item] 61 | func equip(item: Item) -> void: 62 | if item == null: 63 | return 64 | 65 | if not can_equip(item): 66 | item_refused_to_equip.emit(item, self) 67 | return 68 | 69 | if has_equipped_item: 70 | item_unequipped.emit(equipped, self) 71 | equipped = null 72 | 73 | equipped = item 74 | item_equipped.emit(item, self) 75 | 76 | 77 | ## Unequips an [Item] if any. 78 | ## [br]If there is an equipped item, the signal [signal EquipmentSlot.item_unequipped] will be emitted. 79 | func unequip() -> void: 80 | if has_equipped_item: 81 | item_unequipped.emit(equipped, self) 82 | equipped = null 83 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_editor.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorProperty 3 | 4 | var resource_input := EditorResourcePicker.new() 5 | var vbox_container := VBoxContainer.new() 6 | var scroll_container := VBoxContainer.new() 7 | var row_scene = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_editor_row.tscn") 8 | 9 | var updating := false 10 | 11 | 12 | func _init() -> void: 13 | resource_input.base_type = "AttributeTable" 14 | 15 | vbox_container.add_child(resource_input) 16 | vbox_container.add_child(scroll_container) 17 | 18 | add_child(vbox_container) 19 | 20 | resource_input.resource_changed.connect(func (res): 21 | get_edited_object().table = res 22 | if res != null: 23 | _draw_attributes_rows(res) 24 | else: 25 | _empty_attributes() 26 | _empty_scroll_container() 27 | ) 28 | resource_input.resource_selected.connect(func (res, inspect): 29 | get_edited_object().table = res 30 | if res != null: 31 | _draw_attributes_rows(res) 32 | else: 33 | _empty_attributes() 34 | _empty_scroll_container() 35 | ) 36 | 37 | property_changed.connect(func (property: StringName, value: Variant, field: StringName, changing: bool): 38 | if property == "attributes" and value == null: 39 | var target = get_edited_object() 40 | target.attributes = [] 41 | target.table = null 42 | resource_input.edited_resource = null 43 | _empty_scroll_container() 44 | ) 45 | 46 | 47 | func _draw_attributes_rows(resource: AttributeTable) -> void: 48 | _empty_scroll_container() 49 | 50 | if resource == null: 51 | return 52 | 53 | var index = 0 54 | 55 | for attribute in resource.attributes: 56 | scroll_container.add_child(_make_row(attribute, index)) 57 | index += 1 58 | 59 | 60 | func _empty_attributes() -> void: 61 | get_edited_object().attributes = [] 62 | 63 | 64 | func _empty_scroll_container() -> void: 65 | for child in scroll_container.get_children(): 66 | scroll_container.remove_child(child) 67 | 68 | 69 | func _make_row(attribute_name: String, index: int) -> Control: 70 | var gameplay_attributes = get_edited_object() as GameplayAttributeMap 71 | var spec = gameplay_attributes._get_attribute_at(index) 72 | var row = row_scene.instantiate() 73 | 74 | spec.attribute_name = attribute_name 75 | 76 | row.attribute_updated.connect(func (key, value): 77 | gameplay_attributes._update_attribute(index, key, value) 78 | ) 79 | 80 | row.set_values(spec) 81 | 82 | return row 83 | 84 | 85 | func _update_property() -> void: 86 | var target = get_edited_object() 87 | _draw_attributes_rows(target.table) 88 | resource_input.edited_resource = target.table 89 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ability 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/plugin.gd: -------------------------------------------------------------------------------- 1 | extends EditorPlugin 2 | 3 | 4 | const equipment_script = preload("./nodes/equipment.gd") 5 | const equipped_item_3d = preload("./nodes/equipped_item_3d.gd") 6 | const inventory_script = preload("./nodes/inventory.gd") 7 | const item_script = preload("./resources/item.gd") 8 | const item_activation_event_script = preload("./objects/item_activation_event.gd") 9 | const pickable_item_2d = preload("./nodes/pickable_item_2d.gd") 10 | const pickable_item_3d = preload("./nodes/pickable_item_3d.gd") 11 | const drop_group_script = preload("./resources/drop_group.gd") 12 | const drop_table_script = preload("./resources/drop_table.gd") 13 | const drop_script = preload("./nodes/drop.gd") 14 | const drop_2d_script = preload("./nodes/drop_2d.gd") 15 | const drop_3d_script = preload("./nodes/drop_3d.gd") 16 | const radial_menu_script = preload("./nodes/radial_menu_container.gd") 17 | 18 | 19 | func _enter_tree() -> void: 20 | add_custom_type("Item", "Resource", item_script, preload("./assets/ItemIcon.png")) 21 | add_custom_type("Drop", "Node", drop_script, preload("./assets/DropIcon.png")) 22 | add_custom_type("Drop2D", "Node2D", drop_2d_script, preload("./assets/DropNode2D.png")) 23 | add_custom_type("Drop3D", "Node3D", drop_3d_script, preload("./assets/DropNode3D.png")) 24 | add_custom_type("DropGroup", "Resource", drop_group_script, preload("./assets/DropGroupIcon.png")) 25 | add_custom_type("DropTable", "Resource", drop_table_script, preload("./assets/DropTableIcon.png")) 26 | add_custom_type("ItemActivationEvent", "RefCounted", item_activation_event_script, null) 27 | add_custom_type("Inventory", "Node", inventory_script, preload("./assets/InventoryIcon.png")) 28 | add_custom_type("Equipment", "Node", equipment_script, preload("./assets/EquipmentIcon.png")) 29 | add_custom_type("EquippedItem3D", "Node3D", equipped_item_3d, preload("./assets/Equipped3DIcon.png")) 30 | add_custom_type("PickableArea2D", "Area2D", pickable_item_2d, null) 31 | add_custom_type("PickableArea3D", "Area3D", pickable_item_3d, null) 32 | add_custom_type("RadialMenuContainer", "Container", radial_menu_script, preload("./assets/RadialMenuIcon.png")) 33 | 34 | 35 | func _exit_tree() -> void: 36 | remove_custom_type("RadialMenuContainer") 37 | remove_custom_type("PickableArea3D") 38 | remove_custom_type("PickableArea2D") 39 | remove_custom_type("EquippedItem3D") 40 | remove_custom_type("Equipment") 41 | remove_custom_type("Inventory") 42 | remove_custom_type("ItemActivationEvent") 43 | remove_custom_type("DropTable") 44 | remove_custom_type("DropGroup") 45 | remove_custom_type("Drop") 46 | remove_custom_type("Drop3D") 47 | remove_custom_type("Drop2D") 48 | remove_custom_type("Item") 49 | 50 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/debug/abilities_debugger.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | @export_category("Debug UI") 4 | @export_node_path("AbilityContainer") var ability_container_path := NodePath() 5 | @export var persist_messages_for := 15.0 6 | 7 | 8 | @onready var ability_container: AbilityContainer 9 | @onready var labels: VBoxContainer = $Labels 10 | @onready var timer: Timer = $Timer 11 | 12 | 13 | func _ready() -> void: 14 | ability_container = get_node(ability_container_path) as AbilityContainer 15 | 16 | timer.timeout.connect(func (): 17 | var children = labels.get_children() 18 | 19 | if children: 20 | labels.remove_child(children[0]) 21 | ) 22 | 23 | assert(ability_container != null, "ability_container is null") 24 | 25 | ability_container.ability_activated.connect(_handle_activated) 26 | ability_container.ability_blocked.connect(_handle_blocked) 27 | ability_container.ability_cancelled.connect(_handle_cancelled) 28 | ability_container.ability_ended.connect(_handle_ended) 29 | ability_container.ability_granted.connect(_handle_granted) 30 | ability_container.ability_revoked.connect(_handle_revoked) 31 | ability_container.cooldown_started.connect(_handle_cooldown_started) 32 | ability_container.cooldown_ended.connect(_handle_cooldown_ended) 33 | 34 | 35 | func _handle_activated(ability: Ability, e: ActivationEvent) -> void: 36 | _make_label("activated", ability, e) 37 | 38 | 39 | func _handle_blocked(ability: Ability, e: ActivationEvent) -> void: 40 | _make_label("blocked", ability, e) 41 | 42 | 43 | func _handle_cancelled(ability: Ability, e: ActivationEvent) -> void: 44 | _make_label("cancelled", ability, e) 45 | 46 | 47 | func _handle_cooldown_ended(ability: Ability) -> void: 48 | _make_label("cooldown_ended", ability, ActivationEvent.new(ability_container)) 49 | 50 | 51 | func _handle_cooldown_started(ability: Ability) -> void: 52 | _make_label("cooldown_started", ability, ActivationEvent.new(ability_container)) 53 | 54 | 55 | func _handle_ended(ability: Ability, e: ActivationEvent) -> void: 56 | _make_label("ended", ability, e) 57 | 58 | 59 | func _handle_granted(ability: Ability) -> void: 60 | _make_label("granted", ability, ActivationEvent.new(ability_container)) 61 | 62 | 63 | func _handle_revoked(ability: Ability) -> void: 64 | _make_label("revoked", ability, ActivationEvent.new(ability_container)) 65 | 66 | 67 | func _make_label(event: String, ability: Ability, e: ActivationEvent) -> void: 68 | var label = Label.new() 69 | var timer = Timer.new() 70 | 71 | timer.one_shot = true 72 | timer.wait_time = persist_messages_for 73 | 74 | label.text = "{0} is {1} with tags {2}".format({ 75 | "0": ability.ui_name, 76 | "1": event, 77 | "2": str(e.tags) 78 | }) 79 | 80 | labels.add_child(label) 81 | 82 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/resources/interaction.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png") 3 | class_name Interaction extends Resource 4 | 5 | ## Represents an interaction 6 | 7 | @export_category("Interaction") 8 | 9 | @export_group("Tags added on", "tags_added_on_") 10 | ## Tags added when an interaction starts 11 | @export var tags_added_on_start: Array[String] = [] 12 | ## Tags added when an interaction is blocked 13 | @export var tags_added_on_block: Array[String] = [] 14 | ## Tags added when an interaction is cancelled 15 | @export var tags_added_on_cancel: Array[String] = [] 16 | ## Tags added when an interaction ends 17 | @export var tags_added_on_end: Array[String] = [] 18 | 19 | @export_group("Tags required to", "tags_required_to_") 20 | ## Tags required to start an interaction 21 | @export var tags_required_to_start: Array[String] = [] 22 | ## Tags required to block an interaction 23 | @export var tags_required_to_block: Array[String] = [] 24 | ## Tags required to cancel an interaction 25 | @export var tags_required_to_cancel: Array[String] = [] 26 | ## Tags required to end an interaction 27 | @export var tags_required_to_end: Array[String] = [] 28 | 29 | @export_group("Tags removed on", "tags_removed_on_") 30 | ## Tags removed when an interaction starts 31 | @export var tags_removed_on_start: Array[String] = [] 32 | ## Tags removed when an interaction is blocked 33 | @export var tags_removed_on_block: Array[String] = [] 34 | ## Tags removed when an interaction is cancelled 35 | @export var tags_removed_on_cancel: Array[String] = [] 36 | ## Tags removed when an interaction ends 37 | @export var tags_removed_on_end: Array[String] = [] 38 | 39 | 40 | ## Invoked right before an interaction ends. 41 | ## [br]It's a virtual method, so it can be overridden in a script. 42 | func on_before_interaction_end(manager: InteractionManager, interacted_node: Node) -> void: 43 | pass 44 | 45 | 46 | ## Invoked right before an interaction starts. 47 | ## [br]It's a virtual method, so it can be overridden in a script. 48 | func on_before_interaction_start(manager: InteractionManager, interacted_node: Node) -> void: 49 | pass 50 | 51 | 52 | ## Invoked each time an input is received when the interaction is active. 53 | ## [br]It's a virtual method, so it can be overridden in a script. 54 | func handle_input(input: InputEvent, manager: InteractionManager, interacted_node: Node) -> void: 55 | pass 56 | 57 | 58 | ## Invoked inside the physics process if the interaction is active. 59 | ## [br]It's a virtual method, so it can be overridden in a script. 60 | func handle_physics_process(delta: float, manager: InteractionManager, interacted_node: Node) -> void: 61 | pass 62 | 63 | 64 | ## Invoked inside the process if the interaction is active. 65 | ## [br]It's a virtual method, so it can be overridden in a script. 66 | func handle_process(delta: float, manager: InteractionManager, interacted_node: Node) -> void: 67 | pass 68 | 69 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/extended_character_nodes/nodes/2d/point_and_click_2d.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name PointAndClick2D extends Node2D 3 | 4 | 5 | signal new_position_marked(position: Vector2) 6 | 7 | 8 | @export_category("PointAndClick2D") 9 | ## Activates the [PointAndClick2D]. 10 | @export var active: bool = true 11 | ## The movement speed 12 | @export var movement_speed: float = 100.0 13 | ## Is the [CharacterBody2D] to move. 14 | @export var character_2d: CharacterBody2D = null 15 | ## Is the [CharacterBody2D] child [NavigationAgent2D]. 16 | ## Is used to determine the path in the world. 17 | @export var navigation_agent: NavigationAgent2D = null 18 | ## If set to [code]true[/code] the [code]PointAndClick2D[/code] will move the [CharacterBody2D] automatically. 19 | @export var automatic_movement: bool = true 20 | 21 | 22 | ## Returns the mouse position in a 2D world. 23 | var mouse_position: Vector2: 24 | get: 25 | return character_2d.get_global_mouse_position() 26 | 27 | 28 | ## Handles movement 29 | func _handle_velocity_computed(_velocity: Vector2) -> void: 30 | character_2d.velocity = _velocity 31 | character_2d.move_and_slide() 32 | 33 | 34 | ## The physics process function 35 | func _physics_process(delta: float) -> void: 36 | if not Engine.is_editor_hint() and automatic_movement: 37 | move_to_clicked_position() 38 | 39 | 40 | ## The ready function 41 | func _ready() -> void: 42 | if not Engine.is_editor_hint(): 43 | navigation_agent.velocity_computed.connect(_handle_velocity_computed) 44 | 45 | 46 | ## Returns [code]true[/code] if the [CharacterBody2D] can move. 47 | func can_move() -> bool: 48 | if Engine.is_editor_hint(): 49 | return false 50 | 51 | if navigation_agent == null: 52 | return false 53 | 54 | if character_2d == null: 55 | return false 56 | 57 | return active 58 | 59 | 60 | ## Returns [code]true[/code] if the [code]navigation_agent[/code] is not [code]null[/code] and the [code]navigation_agent[/code] has finished navigating. 61 | func is_navigation_finished() -> bool: 62 | if navigation_agent != null: 63 | return navigation_agent.is_navigation_finished() 64 | return false 65 | 66 | 67 | ## Moves the [code]character_2d[/code] to the clicked position with the given speed. 68 | func move_to_clicked_position() -> void: 69 | if can_move(): 70 | var _velocity: Vector2 = (navigation_agent.get_next_path_position() - global_position).normalized() * movement_speed 71 | 72 | if navigation_agent.avoidance_enabled: 73 | navigation_agent.set_velocity(_velocity) 74 | else: 75 | _handle_velocity_computed(_velocity) 76 | 77 | 78 | ## Sets the [code]navigation_agent[/code] target position to current click position. 79 | func set_new_movement_position() -> void: 80 | if can_move(): 81 | navigation_agent.target_position = mouse_position 82 | new_position_marked.emit(navigation_agent.target_position) 83 | 84 | 85 | ## Stops the [code]navigation_agent[/code] movement. 86 | func stop_movement() -> void: 87 | if can_move(): 88 | navigation_agent.target_position = character_2d.position 89 | 90 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/nodes/drop.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name Drop extends Node 3 | 4 | ## It is the node responsible for dropping an array of [Items]. 5 | ## 6 | 7 | ## Emitted when some [Item]s have been dropped. 8 | signal items_dropped(items: Array[Item], drop_owner: Node) 9 | 10 | 11 | @export_category("Drop") 12 | ## The table used to drop items 13 | @export var drop_table: DropTable = null 14 | ## The path to the owning node. This will determine if the drop will be performed in a 2D or 3D world. 15 | ## [br]Note that the drop will occur at the same position of the owning node. 16 | @export_node_path("Node2D", "Node3D") var owning_node_path := NodePath() 17 | 18 | 19 | ## It could be either a [Node2D] or [Node3D] 20 | var owning_node: Variant: 21 | get: 22 | if owning_node_path != null and not owning_node_path.is_empty(): 23 | return get_node(owning_node_path) 24 | return null 25 | 26 | 27 | ## Called when some [Item]s have been dropped. 28 | func _drop(items: Array[Item]) -> void: 29 | var instances: Array[Node] = [] 30 | var _owning_node = owning_node 31 | 32 | if _owning_node == null: 33 | return 34 | 35 | for item in items: 36 | if item.scene and item.scene.can_instantiate(): 37 | var instance = item.scene.instantiate() 38 | var nearest_drop = find_nearest_drop() 39 | 40 | instance.position = _owning_node.position 41 | 42 | if nearest_drop: 43 | nearest_drop.add_child(instance) 44 | nearest_drop.item_dropped.emit(item, instance) 45 | 46 | 47 | ## It gets the droppable [Item]s from the [DropTable]. 48 | ## [br]It accepts an optional argument [code]drop_modifier[/code] which increases (or decreases if negative) drop chances for each [DropGroup] pool. 49 | ## [br]The greater the modifier, the higher the chances to drop an [Item] from the [member DropTable.pools]. 50 | func drop_items(drop_modifier: float = 0.0) -> void: 51 | var items = drop_table.drop(drop_modifier) 52 | 53 | if items.size() > 0: 54 | # Who knows what happens in this method. Let's await it. 55 | await _drop(items) 56 | 57 | items_dropped.emit(items) 58 | 59 | 60 | ## Finds the nearest [Drop2D] or [Drop3D] node. 61 | ## [br]This could be useful in a world-partitioned scenario (like open world rpgs). 62 | func find_nearest_drop() -> Node: 63 | var owner_node = owning_node 64 | 65 | if owner_node == null: 66 | return null 67 | 68 | # gets all dropzones 69 | var dropzones = get_tree().get_nodes_in_group("ggs.drop-node").duplicate() 70 | var position = owner_node.position 71 | var is_2d = (owner_node as Node2D) != null 72 | var is_3d = (owner_node as Node3D) != null 73 | 74 | if dropzones.size() == 0: 75 | return null 76 | 77 | 78 | # the sorting function, we will take the nearest [Drop2D] or [Drop3D] node. 79 | dropzones.sort_custom(func (a: Variant, b: Variant) -> bool: 80 | if a is Node2D and b is Node2D and is_2d: 81 | return b.position.distance_to(position) > a.position.distance_to(position) 82 | elif a is Node3D and b is Node3D and is_3d: 83 | return b.position.distance_to(position) > a.position.distance_to(position) 84 | 85 | return false 86 | ) 87 | 88 | return dropzones[0] 89 | 90 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/objects/attribute_spec.gd: -------------------------------------------------------------------------------- 1 | class_name AttributeSpec extends RefCounted 2 | 3 | ## Represents an attribute as a class 4 | 5 | ## Emitted when the instance has changed one of it's values 6 | signal changed(current: AttributeSpec) 7 | ## Emitted when the instance has changed it's minimum value 8 | signal minimum_value_changed(from: float, to: float) 9 | ## Emitted when the instance has changed it's maximum value 10 | signal maximum_value_changed(from: float, to: float) 11 | 12 | ## used internally to avoid the initial signals propagation 13 | var __setup := false 14 | ## It's the attribute name 15 | var attribute_name := "" 16 | ## It's the buff amount 17 | var buffing_value := 0.0 18 | ## It's the current attribute's value 19 | var current_value := 0.0: 20 | get: 21 | return current_value 22 | set(value): 23 | var previous_value = current_value 24 | 25 | if previous_value == value: 26 | return 27 | 28 | if maximum_value > minimum_value: 29 | current_value = clampf(value, minimum_value, maximum_value) 30 | elif maximum_value == minimum_value and maximum_value == 0.0: 31 | current_value = value 32 | else: 33 | if maximum_value == 0.0 and minimum_value > 0.0: 34 | current_value = clampf(value, value + 1, minimum_value) 35 | else: 36 | current_value = clampf(value, maximum_value, minimum_value) 37 | if not __setup: 38 | changed.emit(self) 39 | ## It's the maximum attribute's value. Keep 0 to mark it as infinite 40 | var maximum_value := 0.0: 41 | get: 42 | return maximum_value 43 | set(value): 44 | var previous_value = maximum_value 45 | maximum_value = value 46 | 47 | if not __setup: 48 | maximum_value_changed.emit(previous_value, value) 49 | ## It's the maximum attribute's value. 50 | var minimum_value := 0.0: 51 | get: 52 | return minimum_value 53 | set(value): 54 | var previous_value = minimum_value 55 | minimum_value = value 56 | 57 | if not __setup: 58 | minimum_value_changed.emit(previous_value, value) 59 | ## The calculated attribute value with the [member AttributeSpec.buffing_value]. 60 | var current_buffed_value: float = 0.0: 61 | get: 62 | return current_value + buffing_value 63 | 64 | 65 | func apply_attribute_effect(attribute_effect: AttributeEffect) -> void: 66 | var value = attribute_effect.get_current_value() 67 | 68 | if attribute_effect.applies_as == 1: 69 | buffing_value += value 70 | else: 71 | if buffing_value > 0.0 and value < 0.0: 72 | var diff = buffing_value + value 73 | 74 | buffing_value = clampf(diff, 0, buffing_value) 75 | current_value += diff 76 | else: 77 | current_value += value 78 | 79 | 80 | ## Creates an instance of [AttributeSpec] starting from an [AttributeResource] 81 | static func from_attribute(attribute: AttributeResource) -> AttributeSpec: 82 | var instance = AttributeSpec.new() 83 | instance.__setup = true 84 | instance.attribute_name = attribute.attribute_name 85 | instance.maximum_value = attribute.maximum_value 86 | instance.minimum_value = attribute.minimum_value 87 | instance.current_value = attribute.current_value 88 | instance.__setup = false 89 | return instance 90 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/turn_based/nodes/TurnBasedGame.gd: -------------------------------------------------------------------------------- 1 | class_name TurnBasedGame extends Node 2 | 3 | 4 | ## Emitted when a turn changes. 5 | signal turn_changed(manager: TurnBasedGame) 6 | ## Emitted when the turn based game starts. 7 | signal turn_game_started() 8 | ## Emitted when the turn based game stops. 9 | signal turn_game_stopped() 10 | ## Emitted when a subscriber is added. 11 | signal subscriber_added(subscriber: TurnSubscriber) 12 | ## Emitted when a subscriber is removed. 13 | signal subscriber_removed(subscriber: TurnSubscriber) 14 | 15 | 16 | ## The current turn. 17 | var current_turn: int = 0 18 | ## The current turn subscriber. 19 | var current_turn_subscriber: TurnSubscriber: 20 | get: 21 | if subscribers.size() == 0: 22 | return null 23 | 24 | return subscribers[current_turn] 25 | ## The turn subscribers. 26 | var subscribers: Array[TurnSubscriber] = [] 27 | 28 | 29 | func _ready() -> void: 30 | add_to_group("ggs.turnbased") 31 | 32 | 33 | func _sort_subscribers() -> void: 34 | subscribers.sort_custom(func (a, b): 35 | return a.priority < b.priority 36 | ) 37 | 38 | 39 | ## Called when a [TurnSubscriber] is subscribed. 40 | ## [br]This is a virtual method 41 | func _subscriber_added(sub: TurnSubscriber) -> void: 42 | pass 43 | 44 | 45 | ## Called when a [TurnSubscriber] is unsubscribed. 46 | ## [br]This is a virtual method 47 | func _subscriber_removed(sub: TurnSubscriber) -> void: 48 | pass 49 | 50 | 51 | ## Adds a [TurnSubscriber] 52 | func add_subscriber(sub: TurnSubscriber) -> bool: 53 | if subscribers.has(sub): 54 | return false 55 | 56 | subscribers.append(sub) 57 | subscriber_added.emit(sub) 58 | 59 | _sort_subscribers() 60 | 61 | return true 62 | 63 | 64 | ## Ends the current turn 65 | func end_turn_sequence() -> void: 66 | if subscribers.size() == 0: 67 | return 68 | 69 | subscribers[current_turn].end_turn() 70 | 71 | current_turn = 0 72 | 73 | turn_game_stopped.emit() 74 | 75 | 76 | ## Calls next turn 77 | func next_turn() -> void: 78 | var subscribers_count := subscribers.size() 79 | 80 | if current_turn < subscribers_count: 81 | subscribers[current_turn].turn_ended.emit() 82 | subscribers[current_turn]._turn_ended() 83 | 84 | current_turn += 1 85 | 86 | if current_turn >= subscribers_count: 87 | current_turn = 0 88 | 89 | subscribers[current_turn].turn_started.emit() 90 | subscribers[current_turn]._turn_started() 91 | 92 | 93 | ## Removes a [TurnSubscriber] 94 | func remove_subscriber(sub: TurnSubscriber) -> bool: 95 | if not subscribers.has(sub): 96 | return false 97 | 98 | var sub_turn_index = subscribers.find(sub) 99 | 100 | subscribers.remove_at(sub_turn_index) 101 | subscriber_removed.emit(sub) 102 | 103 | _sort_subscribers() 104 | 105 | return true 106 | 107 | 108 | ## Starts the turn based game. 109 | func start_turn_sequence() -> void: 110 | if subscribers.size() == 0: 111 | return 112 | 113 | current_turn = 0 114 | 115 | subscribers[current_turn]._turn_started() 116 | subscribers[current_turn].turn_started.emit() 117 | 118 | turn_game_started.emit() 119 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/inspector/attribute_effect_editor.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorProperty 3 | 4 | var add_row_button := Button.new() 5 | var scroll_container := VBoxContainer.new() 6 | var resource_input := EditorResourcePicker.new() 7 | var row_editor_scene = preload("res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_effect_editor_row.tscn") 8 | var vbox_container := VBoxContainer.new() 9 | 10 | 11 | func _draw_effects_row(table: AttributeTable, res: AttributeEffect, index: int) -> Control: 12 | var instance = row_editor_scene.instantiate() 13 | 14 | instance.set_values(table, res) 15 | 16 | instance.removed.connect(func (): 17 | var target = get_edited_object() as GameplayEffect 18 | target.attributes_affected.erase(res) 19 | _update_ui() 20 | ) 21 | 22 | res.changed.connect(func (): 23 | var prop = get_edited_property() 24 | var target = get_edited_object() 25 | 26 | target[prop][index] = res; 27 | ) 28 | 29 | return instance 30 | 31 | 32 | func _draw_effects_rows() -> void: 33 | var target = get_edited_object() as GameplayEffect 34 | var index = 0 35 | 36 | _empty_scroll_container() 37 | 38 | if target and target.attributes_affected: 39 | for eff in target.attributes_affected: 40 | scroll_container.add_child(_draw_effects_row(target.table, eff, index)) 41 | index += 1 42 | 43 | 44 | func _handle_add_attribute_effect() -> void: 45 | var target = get_edited_object() as GameplayEffect 46 | 47 | target.attributes_affected.append(AttributeEffect.new()) 48 | 49 | _update_ui() 50 | 51 | 52 | func _init() -> void: 53 | add_row_button.text = "Add effect" 54 | add_row_button.hide() 55 | 56 | resource_input.base_type = "AttributeTable" 57 | 58 | vbox_container.add_child(resource_input) 59 | vbox_container.add_child(scroll_container) 60 | vbox_container.add_child(add_row_button) 61 | 62 | add_row_button.pressed.connect(_handle_add_attribute_effect) 63 | 64 | resource_input.resource_changed.connect(func (res): 65 | _handle_attribute_table_changed(res) 66 | ) 67 | 68 | resource_input.resource_selected.connect(func (res, inspector): 69 | _handle_attribute_table_changed(res) 70 | ) 71 | 72 | add_child(vbox_container) 73 | 74 | _update_ui() 75 | 76 | 77 | func _empty_scroll_container() -> void: 78 | for child in scroll_container.get_children(): 79 | scroll_container.remove_child(child) 80 | 81 | 82 | func _handle_attribute_table_changed(res: AttributeTable) -> void: 83 | get_edited_object().table = res 84 | 85 | if res: 86 | add_row_button.show() 87 | else: 88 | add_row_button.hide() 89 | 90 | 91 | func _update_property() -> void: 92 | var target = get_edited_object() as GameplayEffect 93 | 94 | resource_input.edited_resource = target.table 95 | 96 | _update_ui() 97 | 98 | 99 | func _update_ui() -> void: 100 | var target = get_edited_object() 101 | 102 | _empty_scroll_container() 103 | _draw_effects_rows() 104 | 105 | if target and target.table: 106 | add_row_button.show() 107 | else: 108 | add_row_button.hide() 109 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/extended_character_nodes/nodes/3d/point_and_click_3d.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name PointAndClick3D extends Node3D 3 | 4 | 5 | ## Emitted when the player clicks a new position 6 | signal new_position_marked(position: Vector3) 7 | 8 | 9 | @export_category("PointAndClick3D") 10 | ## Activates the [PointAndClick3D]. 11 | @export var active: bool = true 12 | ## The movement speed 13 | @export var movement_speed: float = 100.0 14 | ## Is the [CharacterBody3D] to move. 15 | @export var character_3d: CharacterBody3D = null 16 | ## Is the [CharacterBody3D] child [NavigationAgent3D]. 17 | ## Is used to determine the path in the world. 18 | @export var navigation_agent: NavigationAgent3D = null 19 | ## If set to [code]true[/code] the [PointAndClick3D] will move the [CharacterBody3D] automatically. 20 | @export var automatic_movement: bool = true 21 | 22 | @export_flags_3d_physics var clickable_layer: int = 1 23 | 24 | 25 | ## Gets the current mouse position in the world. 26 | var mouse_position: Vector3: 27 | get: 28 | if character_3d == null: 29 | return Vector3.ZERO 30 | 31 | var camera = get_viewport().get_camera_3d() 32 | var mouse_position = get_viewport().get_mouse_position() 33 | var direct_space_state = get_world_3d().direct_space_state 34 | var pos = camera.project_position(mouse_position, 100.0) 35 | var raycast_origin = camera.project_ray_origin(mouse_position) 36 | var raycast_end = (camera.project_ray_normal(mouse_position) + raycast_origin) * 2000.0 37 | # var query = PhysicsRayQueryParameters3D.new().create(raycast_origin, raycast_end, clickable_layer, [character_3d]) 38 | var query = PhysicsRayQueryParameters3D.new().create(raycast_origin, pos, clickable_layer, [character_3d]) 39 | var raycast_array = direct_space_state.intersect_ray(query) 40 | var target_position = raycast_array.get("position", Vector3.ZERO) 41 | 42 | if target_position != Vector3.ZERO: 43 | return target_position 44 | 45 | return Vector3.ZERO 46 | 47 | 48 | func _handle_velocity_computed(_velocity: Vector3) -> void: 49 | character_3d.velocity = _velocity 50 | character_3d.move_and_slide() 51 | 52 | 53 | ## The physics process function 54 | func _physics_process(delta: float) -> void: 55 | if not Engine.is_editor_hint() and automatic_movement: 56 | move_to_clicked_position() 57 | 58 | 59 | ## The ready function 60 | func _ready() -> void: 61 | if not Engine.is_editor_hint(): 62 | navigation_agent.velocity_computed.connect(_handle_velocity_computed) 63 | 64 | 65 | ## Returns [code]true[/code] if the navigation path's final position has been reached. 66 | func is_navigation_finished() -> bool: 67 | if navigation_agent != null: 68 | return navigation_agent.is_navigation_finished() 69 | return false 70 | 71 | 72 | ## Moves the [CharacterBody3D] to the clicked position. 73 | func move_to_clicked_position() -> void: 74 | if Engine.is_editor_hint(): 75 | return 76 | 77 | if navigation_agent == null: 78 | return 79 | 80 | if navigation_agent.is_navigation_finished(): 81 | return 82 | 83 | if not active: 84 | return 85 | 86 | var _velocity: Vector3 = (navigation_agent.get_next_path_position() - global_position).normalized() * movement_speed 87 | 88 | if navigation_agent.avoidance_enabled: 89 | navigation_agent.set_velocity(_velocity) 90 | else: 91 | _handle_velocity_computed(_velocity) 92 | 93 | 94 | ## Sets the [code]navigation_agent[/code] target position to current click position. 95 | func set_new_movement_position() -> void: 96 | if Engine.is_editor_hint(): 97 | return 98 | 99 | if navigation_agent == null: 100 | return 101 | 102 | var target_position = mouse_position 103 | 104 | if target_position != Vector3.ZERO: 105 | navigation_agent.target_position = target_position 106 | new_position_marked.emit(target_position) 107 | 108 | 109 | ## Stops the [code]navigation_agent[/code] movement. 110 | func stop_movement() -> void: 111 | navigation_agent.target_position = character_3d.position 112 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/resources/item.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/inventory_system/assets/ItemIcon.png") 2 | @tool 3 | class_name Item extends Resource 4 | 5 | 6 | ## Represents an item in the world 7 | 8 | 9 | @export_category("Item") 10 | ## Name given to this item. 11 | ## [br]Use it just for your internal purpose, you should use a dictionary of name->i18n values for multilanguage purpose. 12 | @export var name: StringName = "" 13 | ## Determines if an [Item] can stack or not. 14 | @export var can_stack: bool = false 15 | ## If [member Item.can_stack] is [code]true[/code], it will decrease automatically it's own stack on activation. 16 | @export var decrease_stack_on_use: bool = false 17 | ## The rendered (2D or 3D) item into the game world, or in front of the camera. 18 | @export var scene: PackedScene = null 19 | ## Tags used to categorize this [Item]. 20 | @export var tags: Array[String] = [] 21 | 22 | @export_group("Stacking", "quantity_") 23 | ## Current quantity. 24 | @export var quantity_current: int = 1 25 | ## If [member Item.can_stack] is set to [code]true[/code]. 26 | ## [br]Set it to [code]0[/code] for infinite stacking (e.g. currency and similar). 27 | @export var quantity_max: int = 1 28 | 29 | @export_group("Tags", "tags_") 30 | ## Tags added to [Inventory] and [Equipment] on [Item] activation. 31 | @export var tags_added_on_activation: Array[String] = [] 32 | ## Tags added to [Inventory] on [Item] add. 33 | ## [br]Note, if some [Item]s are added to an [Inventory] through the editor, these tags will be added inside 34 | ## the [method Inventory._ready] method. 35 | @export var tags_added_on_add: Array[String] = [] 36 | ## Tags added to [Equipment] when the [Item] is equipped. 37 | @export var tags_added_on_equip: Array[String] = [] 38 | ## Tags added to [Inventory] and [Equipment] on [Item] remove. 39 | @export var tags_added_on_remove: Array[String] = [] 40 | ## Tags added to [Equipment] owner when the [Item] is unequipped. 41 | @export var tags_added_on_unequip: Array[String] = [] 42 | ## Requires an [Inventory] or [Equipment] to have all these tags before activating this [Item]. 43 | @export var tags_required_to_activate: Array[String] = [] 44 | ## Requires an [Inventory] to have all these tags before adding this [Item]. 45 | @export var tags_required_to_add: Array[String] = [] 46 | ## Requires an [Equipment] to have all these tags before equipping this [Item]. 47 | @export var tags_required_to_equip: Array[String] = [] 48 | ## Requires an [Inventory] to have all these tags before removing this [Item]. 49 | @export var tags_required_to_remove: Array[String] = [] 50 | ## Requires an [Equipment] to have all these tags before unequipping this [Item]. 51 | @export var tags_required_to_unequip: Array[String] = [] 52 | ## Tags removed from [Inventory] and [Equipment] on [Item] activation. 53 | @export var tags_removed_on_activation: Array[String] = [] 54 | ## Tags removed from [Inventory] on [Item] add. 55 | @export var tags_removed_on_add: Array[String] = [] 56 | ## Tags removed from the [Equipment] when the [Item] is equipped. 57 | @export var tags_removed_on_equip: Array[String] = [] 58 | ## Tags removed from [Inventory] and [Equipment] on [Item] remove. 59 | @export var tags_removed_on_remove: Array[String] = [] 60 | ## Tags removed from the [Equipment] when the [Item] is unequipped. 61 | @export var tags_removed_on_unequip: Array[String] = [] 62 | 63 | 64 | ## A virtual method called when the item is activated. Override it to determine how the item behaves on activation. 65 | func _activate(_activation_event: ItemActivationEvent) -> void: 66 | pass 67 | 68 | 69 | ## Returns if the item has the right tags to be activated. Override it to determine if the item can be added to an [Inventory]. 70 | func _can_activate(_activation_event: ItemActivationEvent) -> bool: 71 | return true 72 | 73 | 74 | ## A virtual method called when this item is equipped. 75 | func _equip(_equipment: Equipment, _equipment_slot: EquipmentSlot) -> void: 76 | pass 77 | 78 | 79 | ## A virtual method called when this item is unequipped. 80 | func _unequip(_equipment: Equipment, _equipment_slot: EquipmentSlot) -> void: 81 | pass 82 | 83 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/resources/attribute_effect.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/AttributeEffect.svg") 2 | 3 | class_name AttributeEffect extends Resource 4 | 5 | ## An AttributeEffect is a resource which stores all effects mutation informations 6 | ## 7 | ## It is consumed once by a GameplayAttributeMap if it's lifetime is "oneshot", otherwise 8 | ## it will be consumed each [member AttributeEffect.apply_every_second] seconds for a maximum of [member AttributeEffect.max_applications] times 9 | 10 | 11 | enum { 12 | ## The effect will be applied once 13 | LIFETIME_ONE_SHOT = 0, 14 | ## The effect will be applied each tot seconds 15 | LIFETIME_TIME_BASED = 1, 16 | } 17 | 18 | @export_category("Effect life-time") 19 | ## The attribute effect life-time:[br] 20 | ## LIFETIME_ONE_SHOT [b]0[/b] is equal to a oneshot effect, aka applied once.[br] 21 | ## LIFETIME_TIME_BASED [b]1[/b] is equal to a time-based effect, applied each [member AttributeEffect.apply_every_second] seconds for a maximum of [member AttributeEffect.max_applications] times 22 | @export_enum("One-Shot", "Time-Based") var life_time = 0: 23 | get: 24 | return life_time 25 | set(value): 26 | life_time = value 27 | emit_changed() 28 | ## If [member life_time] is [b]1[/b], then the effect is applied each seconds as specified by this parameter 29 | @export var apply_every_second := 1.0: 30 | get: 31 | return apply_every_second 32 | set(value): 33 | apply_every_second = value 34 | emit_changed() 35 | ## If [member life_time] is [b]1[/b], then the effect is applied each seconds as specified by [member apply_every_second] for a maximum of times specified by this parameter 36 | @export var max_applications := 1: 37 | get: 38 | return max_applications 39 | set(value): 40 | max_applications = value 41 | emit_changed() 42 | 43 | @export_category("Attribute modifier") 44 | @export_enum("Value modification", "Value buff") var applies_as = 0: 45 | get: 46 | return applies_as 47 | set(value): 48 | applies_as = value 49 | emit_changed() 50 | ## The attribute name to mutate 51 | @export var attribute_name := "": 52 | get: 53 | return attribute_name 54 | set(value): 55 | attribute_name = value 56 | emit_changed() 57 | ## The minimum value. 58 | ## [br]Can be negative 59 | @export var minimum_value := 0.0: 60 | get: 61 | return minimum_value 62 | set(value): 63 | minimum_value = value 64 | emit_changed() 65 | ## The minimum value. 66 | ## [br]Can be negative 67 | @export var maximum_value := 0.0: 68 | get: 69 | return maximum_value 70 | set(value): 71 | maximum_value = value 72 | emit_changed() 73 | @export_group("Pipeline") 74 | ## If it is set, it will call [method AttributeEffectCondition.should_apply] 75 | @export var condition: Resource = null: 76 | get: 77 | return condition 78 | set(value): 79 | condition = value 80 | emit_changed() 81 | 82 | 83 | ## Gets the computed current value.[br] 84 | ## 85 | ## If both [member minimum_value] and [member maximum_value] are different from [code]0.0[/code] the resulting [code]float[/code] is a value between 86 | ## [br] - [member minimum_value] and [member maximum_value] if minimum_value is less than maximum 87 | ## [br] - [member maximum_value] and [member minimum_value] if minimum_value is greater than maximum 88 | func get_current_value() -> float: 89 | if minimum_value < maximum_value: 90 | return randf_range(minimum_value, maximum_value) 91 | elif minimum_value > maximum_value: 92 | return randf_range(maximum_value, minimum_value) 93 | else: 94 | return minimum_value 95 | 96 | 97 | ## Checks if the current attribute effect can be applied 98 | func should_apply(gameplay_effect: GameplayEffect, gameplay_attribute_map: GameplayAttributeMap) -> bool: 99 | if condition == null: 100 | return true 101 | 102 | if condition.has_method("should_apply"): 103 | return condition.should_apply(self, gameplay_effect, gameplay_attribute_map) 104 | 105 | printerr("condition has not a method should_apply(attribute_effect: AttributeEffect, effect: GameplayEffect, gameplay_attributes: GameplayAttributeMap) -> bool") 106 | 107 | return true 108 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/plugin.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends EditorPlugin 3 | 4 | const ATTRIBUTE_SPEC_NAME = "AttributeSpec" 5 | const ABILITY_CONTAINER_NAME = "AbilityContainer" 6 | const ACTIVATION_EVENT_NAME = "ActivationEvent" 7 | const ATTRIBUTE_RESOURCE_NAME = "AttributeResource" 8 | const ATTRIBUTE_TABLE_RESOURCE_NAME = "AttributeTableResource" 9 | const ATTRIBUTE_EFFECT_RESOURCE_NAME = "AttributeEffectResource" 10 | const ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME = "AttributeEffectCondition" 11 | const GAMEPLAY_EFFECT_NAME = "GameplayEffect" 12 | const GAMEPLAY_ATTRIBUTE_MAP_NAME = "GameplayAttributeMap" 13 | const EFFECTED_AREA2D = "EffectedArea2D" 14 | const EFFECTED_AREA3D = "EffectedArea3D" 15 | const STOP_EFFECT_IF0_RESOURCE_NAME = "StopEffectIfAttributeIs0" 16 | 17 | const attribute_spec_script = preload("./objects/attribute_spec.gd") 18 | const ability_container_resource = preload("./nodes/ability_container.gd") 19 | const activation_event_script = preload("./objects/activation_event.gd") 20 | const attributes_table_resource = preload("./resources/attribute_table.gd") 21 | const attribute_resource = preload("./resources/attribute.gd") 22 | const attribute_effect_resource = preload("./resources/attribute_effect.gd") 23 | const attribute_effect_condition_resource = preload("./resources/attribute_effect_condition.gd") 24 | const effected_area2d = preload("./nodes/effected_area2d.gd") 25 | const effected_area3d = preload("./nodes/effected_area3d.gd") 26 | const gameplay_effect = preload("./nodes/gameplay_effect.gd") 27 | const gameplay_attribute_map = preload("./nodes/gameplay_attribute_map.gd") 28 | const stop_effect_if0_resource = preload("./resources/stop_effect_if_0.gd") 29 | 30 | const attribute_inspector_plugin_script = preload("./inspector/gameplay_attribute_map_inspector_plugin.gd") 31 | const effect_inspector_plugin_script = preload("./inspector/gameplay_effect_inspector_plugin.gd") 32 | 33 | 34 | var attribute_inspector_plugin: EditorInspectorPlugin 35 | var effect_inspector_plugin: EditorInspectorPlugin 36 | 37 | 38 | func _enter_tree(): 39 | add_custom_type(ACTIVATION_EVENT_NAME, "RefCounted", activation_event_script, null) 40 | add_custom_type(ATTRIBUTE_SPEC_NAME, "RefCounted", attribute_spec_script, null) 41 | 42 | add_custom_type(ATTRIBUTE_RESOURCE_NAME, "Resource", attribute_resource, preload("./assets/Attribute@0.15x.png")) 43 | add_custom_type(ATTRIBUTE_TABLE_RESOURCE_NAME, "Resource", attributes_table_resource, preload("./assets/AttributeTable@0.15x.png")) 44 | add_custom_type(ATTRIBUTE_EFFECT_RESOURCE_NAME, "Resource", attribute_effect_resource, preload("./assets/GameplayEffect@0.15x.png")) 45 | add_custom_type(ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME, "Resource", attribute_effect_condition_resource, null) 46 | add_custom_type(GAMEPLAY_ATTRIBUTE_MAP_NAME, "Node", gameplay_attribute_map, preload("./assets/GameplayAttributeMap@0.15x.png")) 47 | add_custom_type(GAMEPLAY_EFFECT_NAME, "Node", gameplay_effect, preload("./assets/GameplayEffect@0.15x.png")) 48 | add_custom_type(EFFECTED_AREA2D, "Area2D", effected_area2d, null) 49 | add_custom_type(EFFECTED_AREA3D, "Area3D", effected_area3d, null) 50 | add_custom_type(STOP_EFFECT_IF0_RESOURCE_NAME, ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME, stop_effect_if0_resource, null) 51 | 52 | add_custom_type(ABILITY_CONTAINER_NAME, "Node", ability_container_resource, preload("./assets/AbilityContainer@0.15x.png")) 53 | 54 | attribute_inspector_plugin = attribute_inspector_plugin_script.new() 55 | effect_inspector_plugin = effect_inspector_plugin_script.new() 56 | 57 | add_inspector_plugin(attribute_inspector_plugin) 58 | add_inspector_plugin(effect_inspector_plugin) 59 | 60 | 61 | func _exit_tree(): 62 | remove_custom_type(ATTRIBUTE_RESOURCE_NAME) 63 | remove_custom_type(ATTRIBUTE_TABLE_RESOURCE_NAME) 64 | remove_custom_type(ATTRIBUTE_EFFECT_RESOURCE_NAME) 65 | remove_custom_type(ATTRIBUTE_EFFECT_CONDITION_RESOURCE_NAME) 66 | remove_custom_type(GAMEPLAY_ATTRIBUTE_MAP_NAME) 67 | remove_custom_type(GAMEPLAY_EFFECT_NAME) 68 | remove_custom_type(EFFECTED_AREA2D) 69 | remove_custom_type(EFFECTED_AREA3D) 70 | remove_custom_type(STOP_EFFECT_IF0_RESOURCE_NAME) 71 | 72 | remove_custom_type(ABILITY_CONTAINER_NAME) 73 | 74 | remove_custom_type(ATTRIBUTE_SPEC_NAME) 75 | remove_custom_type(ACTIVATION_EVENT_NAME) 76 | 77 | remove_inspector_plugin(attribute_inspector_plugin) 78 | remove_inspector_plugin(effect_inspector_plugin) 79 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_effect_editor_row.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://caqvpv434u0tf"] 2 | 3 | [ext_resource type="Script" path="res://addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_effect_editor_row.gd" id="1_ntmni"] 4 | [ext_resource type="Texture2D" uid="uid://qdrl51ys1mv7" path="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffectOneShot@0.15x.png" id="2_twl3n"] 5 | [ext_resource type="Texture2D" uid="uid://bbd35kb6frop4" path="res://addons/godot_gameplay_systems/attributes_and_abilities/assets/TimedEffect@0.15x.png" id="3_iu2ar"] 6 | 7 | [node name="AttributeEffectRow" type="GridContainer"] 8 | offset_right = 262.0 9 | offset_bottom = 140.0 10 | script = ExtResource("1_ntmni") 11 | 12 | [node name="AppliesAs" type="BoxContainer" parent="."] 13 | layout_mode = 2 14 | 15 | [node name="OptionButton" type="OptionButton" parent="AppliesAs"] 16 | layout_mode = 2 17 | size_flags_horizontal = 3 18 | item_count = 2 19 | selected = 0 20 | popup/item_0/text = "Alters value directly" 21 | popup/item_0/id = 0 22 | popup/item_1/text = "Applies buff" 23 | popup/item_1/id = 1 24 | 25 | [node name="AttributeName" type="BoxContainer" parent="."] 26 | layout_mode = 2 27 | 28 | [node name="OptionButton" type="OptionButton" parent="AttributeName"] 29 | layout_mode = 2 30 | size_flags_horizontal = 3 31 | 32 | [node name="RemoveButton" type="Button" parent="AttributeName"] 33 | layout_mode = 2 34 | text = "Remove" 35 | 36 | [node name="MinimumValue" type="BoxContainer" parent="."] 37 | layout_mode = 2 38 | 39 | [node name="Label" type="Label" parent="MinimumValue"] 40 | layout_mode = 2 41 | size_flags_horizontal = 3 42 | text = "Minimum value applied" 43 | clip_text = true 44 | 45 | [node name="SpinBox" type="SpinBox" parent="MinimumValue"] 46 | layout_mode = 2 47 | size_flags_horizontal = 3 48 | allow_greater = true 49 | allow_lesser = true 50 | alignment = 2 51 | update_on_text_changed = true 52 | select_all_on_focus = true 53 | 54 | [node name="MaximumValue" type="BoxContainer" parent="."] 55 | layout_mode = 2 56 | 57 | [node name="Label" type="Label" parent="MaximumValue"] 58 | layout_mode = 2 59 | size_flags_horizontal = 3 60 | text = "Maximum value applied" 61 | clip_text = true 62 | 63 | [node name="SpinBox" type="SpinBox" parent="MaximumValue"] 64 | layout_mode = 2 65 | size_flags_horizontal = 3 66 | allow_greater = true 67 | allow_lesser = true 68 | alignment = 2 69 | update_on_text_changed = true 70 | select_all_on_focus = true 71 | 72 | [node name="LifeTime" type="BoxContainer" parent="."] 73 | layout_mode = 2 74 | 75 | [node name="Label" type="Label" parent="LifeTime"] 76 | layout_mode = 2 77 | size_flags_horizontal = 3 78 | text = "Life time type" 79 | 80 | [node name="OptionButton" type="OptionButton" parent="LifeTime"] 81 | layout_mode = 2 82 | size_flags_horizontal = 3 83 | alignment = 1 84 | item_count = 2 85 | selected = 0 86 | popup/item_0/text = "One Shot" 87 | popup/item_0/icon = ExtResource("2_twl3n") 88 | popup/item_0/id = 0 89 | popup/item_1/text = "Time-Based" 90 | popup/item_1/icon = ExtResource("3_iu2ar") 91 | popup/item_1/id = 1 92 | 93 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 94 | visible = false 95 | layout_mode = 2 96 | 97 | [node name="Lifetime Setup" type="Label" parent="VBoxContainer"] 98 | custom_minimum_size = Vector2(2.08165e-12, 50) 99 | layout_mode = 2 100 | text = "Life time settings" 101 | horizontal_alignment = 1 102 | vertical_alignment = 1 103 | 104 | [node name="TimerSetupContainer" type="VBoxContainer" parent="VBoxContainer"] 105 | layout_mode = 2 106 | size_flags_horizontal = 3 107 | 108 | [node name="TimeoutLabel" type="Label" parent="VBoxContainer/TimerSetupContainer"] 109 | layout_mode = 2 110 | size_flags_horizontal = 3 111 | text = "Seconds" 112 | 113 | [node name="TimeoutSpinBox" type="SpinBox" parent="VBoxContainer/TimerSetupContainer"] 114 | layout_mode = 2 115 | size_flags_horizontal = 3 116 | allow_greater = true 117 | allow_lesser = true 118 | alignment = 2 119 | update_on_text_changed = true 120 | select_all_on_focus = true 121 | 122 | [node name="VSeparator" type="VSeparator" parent="VBoxContainer/TimerSetupContainer"] 123 | layout_mode = 2 124 | size_flags_horizontal = 3 125 | 126 | [node name="ApplicationCountLabel" type="Label" parent="VBoxContainer/TimerSetupContainer"] 127 | layout_mode = 2 128 | size_flags_horizontal = 3 129 | text = "Apply times (0 = infinite)" 130 | 131 | [node name="ApplicationCountSpinBox" type="SpinBox" parent="VBoxContainer/TimerSetupContainer"] 132 | layout_mode = 2 133 | size_flags_horizontal = 3 134 | allow_greater = true 135 | allow_lesser = true 136 | alignment = 2 137 | update_on_text_changed = true 138 | select_all_on_focus = true 139 | 140 | [node name="HSeparator" type="HSeparator" parent="."] 141 | custom_minimum_size = Vector2(2.08165e-12, 20) 142 | layout_mode = 2 143 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/slideshow/slide_show.gd: -------------------------------------------------------------------------------- 1 | class_name SlideShow extends Node2D 2 | 3 | 4 | ## The initial slideshow in a videogame 5 | ## 6 | ## This is made easy 7 | 8 | 9 | enum { 10 | SKIP_PREV = -1, 11 | SKIP_NEXT = +1, 12 | } 13 | 14 | ## Emitted when the slideshow is finished 15 | signal finished() 16 | ## Emitted when a slide is skipped 17 | signal slide_skipped(skip_direction: int) 18 | 19 | @export_category("Presentation settings") 20 | ## Starts the presentation automatically when ready 21 | @export var autoplay: bool = true 22 | ## How much long the slide is shown. It does not take in the [member SlideShow.slide_fade_duration] fadein/fadeout time. 23 | @export_range(1.0, 10.0, 0.1) var slide_duration: float = 6.0 24 | @export_range(0.0, 3.0) var slide_fade_duration: float = 1.0 25 | 26 | ## Current slide index. 27 | var current_slide: int = 0 28 | ## Is [code]true[/code] if there is a previous slide, [code]false[/code] otherwise. 29 | var has_prev: bool: 30 | get: 31 | return current_slide > 0 and slides.size() > 0 32 | ## Is [code]true[/code] if there is a next slide, [code]false[/code] otherwise. 33 | var has_next: bool: 34 | get: 35 | return current_slide < slides.size() 36 | ## If [code]true[/code] the slide is playing, [code]false[/code] otherwise. 37 | var playing: bool = true: 38 | get: 39 | return playing 40 | set(value): 41 | playing = value 42 | 43 | if value and autoplay: 44 | _handle_next_slide() 45 | var slides: Array[Node2D]: 46 | get: 47 | var _s = [] as Array[Node2D] 48 | 49 | for child in get_children(): 50 | if child is Node2D: 51 | _s.append(child) 52 | 53 | return _s 54 | 55 | 56 | func _handle_slide_in(slide: Node2D) -> void: 57 | if slide.has_method("_slide_in"): 58 | slide.call("_slide_in") 59 | 60 | 61 | func _handle_slide_out(slide: Node2D) -> void: 62 | if slide.has_method("_slide_out"): 63 | slide.call("_slide_out") 64 | 65 | 66 | ## Forcefully 67 | func _forcefully_fade_current() -> Tween: 68 | var tween = create_tween() 69 | var slide = slides[current_slide] as Node2D 70 | 71 | tween.tween_property(slide, "modulate:a", 0.0, slide_fade_duration) 72 | 73 | return tween 74 | 75 | 76 | ## Handles next slide. Called internally, use [method SlideShow.skip_to_prev], [method SlideShow.skip_to_next], [method SlideShow.skip_to_nth] or [method SlideShow.skip_all] 77 | func _handle_next_slide(direction: int = SKIP_NEXT) -> void: 78 | if current_slide >= slides.size(): 79 | finished.emit() 80 | else: 81 | var tween = create_tween() 82 | var slide = slides[current_slide] as Node2D 83 | 84 | if slide == null: 85 | printerr("This should NEVER happen, what have you done?") 86 | _handle_next_slide() 87 | 88 | _handle_slide_in(slide) 89 | 90 | tween.tween_property(slide, "modulate:a", 1.0, slide_fade_duration) 91 | tween.tween_interval(slide_duration - (slide_fade_duration * 2)) 92 | tween.tween_property(slide, "modulate:a", 0.0, slide_fade_duration) 93 | 94 | tween.finished.connect(func (): 95 | _handle_slide_out(slide) 96 | current_slide += direction 97 | _handle_next_slide() 98 | ) 99 | 100 | ## Ready fn 101 | func _ready() -> void: 102 | playing = autoplay 103 | 104 | for slide in slides: 105 | slide.modulate.a = 0.0 106 | 107 | 108 | ## Sets [member SlideShow.playing] to [code]true[/code] 109 | func play() -> void: 110 | current_slide = 0 111 | playing = true 112 | 113 | 114 | ## Skips all slides and the [signal SlideShow.finished] is emitted. 115 | ## [br] 116 | ## GG mate, we worked hard for this. 117 | func skip_all() -> void: 118 | skip_slide_to_nth(get_child_count() + 1) 119 | 120 | 121 | ## Skips to the next slide if any, otherwise the slideshow ends and the [signal SlideShow.finished] is emitted. 122 | func skip_slide_to_next() -> void: 123 | skip_slide_to_nth(current_slide + 1) 124 | 125 | 126 | ## Skips to a nth slide. If out of bound, the slideshow ends and the [signal SlideShow.finished] is emitted. 127 | func skip_slide_to_nth(slide_index: int) -> void: 128 | var direction = SKIP_NEXT if slide_index > current_slide else SKIP_PREV 129 | var inbound = slide_index >= 0 and slide_index <= slides.size() 130 | 131 | if not inbound: 132 | playing = false 133 | finished.emit() 134 | return 135 | 136 | if current_slide >= slides.size(): 137 | finished.emit() 138 | else: 139 | var tween = create_tween() 140 | var slide = slides[current_slide] as Node2D 141 | 142 | slide_skipped.emit(direction) 143 | 144 | ## Forcefully fades out current slide. You asked for it, do not complain plis. 145 | tween.tween_property(slide, "modulate:a", 0.0, slide_fade_duration) 146 | 147 | tween.finished.connect(func (): 148 | current_slide += direction 149 | _handle_slide_out(slide) 150 | _handle_next_slide(direction) 151 | ) 152 | 153 | 154 | ## Skips to the previous slide if any, otherwise the slideshow ends and the [signal SlideShow.finished] is emitted. 155 | func skip_slide_to_prev() -> void: 156 | skip_slide_to_nth(current_slide - 1) 157 | 158 | 159 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/inspector/components/attribute_effect_editor_row.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends GridContainer 3 | 4 | 5 | signal removed() 6 | 7 | 8 | @export_category("Effect") 9 | @export var attributes_table: AttributeTable = null: 10 | get: 11 | return attributes_table 12 | set(value): 13 | attributes_table = value 14 | _redraw() 15 | @export var attribute_effect: AttributeEffect = null: 16 | get: 17 | return attribute_effect 18 | set(value): 19 | attribute_effect = value 20 | _redraw() 21 | 22 | @onready var application_count_spinbox: SpinBox = $VBoxContainer/TimerSetupContainer/ApplicationCountSpinBox 23 | @onready var applies_as: OptionButton = $AppliesAs/OptionButton 24 | @onready var attribute_option_button: OptionButton = $AttributeName/OptionButton 25 | @onready var life_time_option_button: OptionButton = $LifeTime/OptionButton 26 | @onready var minimum_value_spinbox: SpinBox = $MinimumValue/SpinBox 27 | @onready var maximum_value_spinbox: SpinBox = $MaximumValue/SpinBox 28 | @onready var timer_setup_container: VBoxContainer = $VBoxContainer 29 | @onready var timer_spinbox: SpinBox = $VBoxContainer/TimerSetupContainer/TimeoutSpinBox 30 | @onready var remove_button: Button = $AttributeName/RemoveButton 31 | 32 | 33 | var condition_resource_input := EditorResourcePicker.new() 34 | 35 | 36 | func _init() -> void: 37 | pass 38 | 39 | 40 | func _inherit_from_resource() -> void: 41 | if attribute_effect != null and attributes_table != null: 42 | _populate_attribute_name_list() 43 | _select_attribute_name() 44 | applies_as.selected = attribute_effect.applies_as 45 | 46 | if applies_as.selected < 0: 47 | applies_as.selected = 0 48 | 49 | attribute_option_button.text = attribute_effect.attribute_name 50 | life_time_option_button.selected = attribute_effect.life_time 51 | minimum_value_spinbox.value = attribute_effect.minimum_value 52 | maximum_value_spinbox.value = attribute_effect.maximum_value 53 | timer_spinbox.value = attribute_effect.apply_every_second 54 | application_count_spinbox.value = attribute_effect.max_applications 55 | condition_resource_input.edited_resource = attribute_effect.condition 56 | _set_lifetime(attribute_effect.life_time) 57 | 58 | 59 | func _populate_attribute_name_list() -> void: 60 | if attribute_option_button: 61 | attribute_option_button.clear() 62 | 63 | if attributes_table: 64 | for attribute in attributes_table.attributes: 65 | attribute_option_button.add_item(attribute) 66 | 67 | 68 | func _ready() -> void: 69 | var condition_label = Label.new() 70 | 71 | condition_label.text = "Condition" 72 | condition_resource_input.base_type = "AttributeEffectCondition" 73 | 74 | add_child(condition_label) 75 | add_child(condition_resource_input) 76 | 77 | timer_setup_container.visible = false 78 | 79 | applies_as.item_selected.connect(func (index): 80 | attribute_effect.applies_as = index 81 | ) 82 | 83 | application_count_spinbox.value_changed.connect(func (value): 84 | attribute_effect.max_applications = value 85 | ) 86 | 87 | attribute_option_button.item_selected.connect(func (index): 88 | if attributes_table and attribute_effect: 89 | if attributes_table.attributes.size() > index: 90 | attribute_effect.attribute_name = attributes_table.attributes[index] 91 | ) 92 | 93 | condition_resource_input.resource_changed.connect(func (resource): 94 | if resource: 95 | attribute_effect.condition = resource 96 | else: 97 | attribute_effect.condition = null 98 | ) 99 | condition_resource_input.resource_selected.connect(func (resource, _i): 100 | if resource: 101 | attribute_effect.condition = resource 102 | else: 103 | attribute_effect.condition = null 104 | ) 105 | 106 | life_time_option_button.item_selected.connect(func (id): 107 | if id is int: 108 | attribute_effect.life_time = id 109 | _set_lifetime(id) 110 | ) 111 | 112 | minimum_value_spinbox.value_changed.connect(func (value): 113 | attribute_effect.minimum_value = value 114 | ) 115 | 116 | maximum_value_spinbox.value_changed.connect(func (value): 117 | attribute_effect.maximum_value = value 118 | ) 119 | 120 | remove_button.pressed.connect(func (): 121 | removed.emit() 122 | ) 123 | 124 | timer_spinbox.value_changed.connect(func (value): 125 | attribute_effect.apply_every_second = value 126 | ) 127 | 128 | _redraw() 129 | 130 | 131 | func _redraw() -> void: 132 | _populate_attribute_name_list() 133 | _inherit_from_resource() 134 | _select_attribute_name() 135 | 136 | 137 | func _select_attribute_name() -> void: 138 | if attributes_table and attribute_effect: 139 | var index = attributes_table.attributes.find(attribute_effect.attribute_name) 140 | attribute_option_button.selected = clampi(index, 0, attribute_option_button.item_count) 141 | 142 | 143 | func _set_lifetime(value: int) -> void: 144 | timer_setup_container.visible = value == 1 145 | 146 | if not timer_setup_container.visible: 147 | application_count_spinbox.value = 0 148 | timer_spinbox.value = 0.0 149 | 150 | 151 | func set_values(table: AttributeTable, effect: AttributeEffect) -> void: 152 | await ready 153 | attributes_table = table 154 | attribute_effect = effect 155 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/camera_shake/nodes/camera_shake.gd: -------------------------------------------------------------------------------- 1 | class_name CameraShake extends Node 2 | 3 | 4 | ## Emitted when the shake ends 5 | signal shake_ended() 6 | ## Emitted when the shake starts 7 | signal shake_started() 8 | ## Emitted when a shake occurs 9 | signal shaken(remaining_times: int) 10 | 11 | 12 | enum CameraContext { 13 | CAMERA_2D, 14 | CAMERA_3D, 15 | } 16 | 17 | 18 | @export_category("Camera shake") 19 | ## The path to the camera to shake 20 | @export_node_path("Camera2D", "Camera3D") var camera_path: NodePath = NodePath() 21 | 22 | @export_group("Minimum values", "min_") 23 | ## The minimum strength appliable to the shake 24 | @export var min_strength: float = 0.0 25 | ## How many seconds the camera will be shaked (min) 26 | @export var min_duration: float = 0.0 27 | ## How many times per second the camera will be shaked (min) 28 | @export var min_frequency: int = 0 29 | 30 | @export_group("Maximum values", "max_") 31 | ## The maximum strength appliable to the shake 32 | @export var max_strength: float = 50.0 33 | ## How many seconds the camera will be shaked (max) 34 | @export var max_duration: float = 5.0 35 | ## How many times per second the camera will be shaked (max) 36 | @export var max_frequency: int = 50 37 | 38 | ## The camera 2d/3d node 39 | var camera: Node 40 | ## The camera context 41 | var camera_context: CameraContext 42 | ## Current applied duration 43 | var duration: float = 0.0: 44 | get: 45 | return duration 46 | set(value): 47 | duration = clampf(value, min_duration, max_duration) 48 | ## Current applied frequency 49 | var frequency: int = 0: 50 | get: 51 | return frequency 52 | set(value): 53 | frequency = clampf(value, min_frequency, max_frequency) 54 | ## Previous used [Tween]. Is [code]null[/code] if the [member CameraShake.camera] never shook or the previous [Tween] finished it's own job. 55 | var previous_tween: Tween 56 | ## Current applied strength 57 | var strength: float = 0.0: 58 | get: 59 | return strength 60 | set(value): 61 | strength = clampf(value, min_strength, max_strength) 62 | ## Time remaining before the shake effect ends 63 | var time_remaining: float = 0.0: 64 | get: 65 | return duration / float(frequency) 66 | ## Returns the number of tweens to operate 67 | var tweens_range: Array: 68 | get: 69 | return range(0, duration * frequency) 70 | 71 | ## Applies the shake 72 | func _apply_shake() -> void: 73 | if camera_context == CameraContext.CAMERA_2D: 74 | _apply_shake_2d() 75 | elif camera_context == CameraContext.CAMERA_3D: 76 | _apply_shake_3d() 77 | 78 | 79 | ## Applies the shake using 2D context 80 | func _apply_shake_2d() -> void: 81 | var _camera = camera as Camera2D 82 | 83 | assert(_camera != null, "Camera (2D) should not be null at this point, check your code before shaking the camera again") 84 | 85 | if previous_tween: 86 | previous_tween.stop() 87 | 88 | previous_tween = create_tween() 89 | previous_tween.tween_property(_camera, "offset", Vector2(0.0, 0.0), time_remaining) 90 | 91 | print("duration: ", duration) 92 | print("time_remaining: ", time_remaining) 93 | print("duration * frequency: ", duration * frequency) 94 | print("tweens_range: ", tweens_range) 95 | 96 | for _n in tweens_range: 97 | previous_tween.tween_property(_camera, "offset", Vector2(_get_shake_value(), _get_shake_value()), time_remaining) 98 | 99 | previous_tween.tween_property(_camera, "offset", Vector2(0.0, 0.0), time_remaining) 100 | previous_tween.play() 101 | 102 | previous_tween.step_finished.connect(func (_x): 103 | previous_tween = null 104 | ) 105 | 106 | 107 | ## Applies the shake using 3D context 108 | func _apply_shake_3d() -> void: 109 | var _camera = camera as Camera3D 110 | 111 | assert(_camera != null, "Camera (3D) should not be null at this point, check your code before shaking the camera again") 112 | 113 | if previous_tween != null: 114 | previous_tween.stop() 115 | 116 | previous_tween = create_tween() 117 | previous_tween.tween_property(_camera, "h_offset", 0.0, time_remaining) 118 | previous_tween.tween_property(_camera, "v_offset", 0.0, time_remaining) 119 | 120 | print("tweens_range: ", tweens_range) 121 | print("time_remaining: ", time_remaining) 122 | print("duration: ", duration) 123 | print("frequency: ", frequency) 124 | 125 | for _n in tweens_range: 126 | previous_tween.tween_property(_camera, "h_offset", _get_shake_value(), time_remaining) 127 | previous_tween.tween_property(_camera, "v_offset", _get_shake_value(), time_remaining) 128 | 129 | previous_tween.tween_property(_camera, "h_offset", 0.0, time_remaining) 130 | previous_tween.tween_property(_camera, "v_offset", 0.0, time_remaining) 131 | previous_tween.play() 132 | 133 | 134 | func _get_shake_value(rerolls_remaining: int = 5) -> float: 135 | var shake_value = randf_range(-1.0, 1.0) * strength 136 | 137 | # Avoiding stack overflows 138 | if max_strength == 0.0 and shake_value == 0.0: 139 | return 0.0 140 | 141 | # Rerolls 142 | if shake_value == 0.0 and rerolls_remaining > 0: 143 | return _get_shake_value(rerolls_remaining - 1) 144 | 145 | return shake_value 146 | 147 | 148 | func _ready() -> void: 149 | if camera_path.is_empty(): 150 | if owner is Camera2D or owner is Camera3D: 151 | camera = owner 152 | else: 153 | camera = get_node(camera_path) 154 | 155 | assert(camera != null, "Camera is null. Set camera_path correctly.") 156 | 157 | ## Try to guess if the camera is a Camera2D 158 | if camera is Camera2D: 159 | camera_context = CameraContext.CAMERA_2D 160 | ## Try to guess if the camera is a Camera3D 161 | elif camera is Camera3D: 162 | camera_context = CameraContext.CAMERA_3D 163 | 164 | 165 | ## Resets shake 2D 166 | func _reset_from_shake_2d() -> void: 167 | camera.offset = Vector2.ZERO 168 | 169 | 170 | ## Resets shake 2D 171 | func _reset_from_shake_3d() -> void: 172 | camera.h_offset = 0.0 173 | camera.v_offset = 0.0 174 | 175 | 176 | ## Shakes current camera by the given strength, duration, and frequency. 177 | ## [br][code]strength[/code] defaults to [code]1.0[/code] 178 | ## [br][code]duration[/code] defaults to [code]1.0[/code] 179 | ## [br][code]frequency[/code] defaults to [code]5[/code] 180 | func shake(strength: float = 1.0, duration: float = 1.0, frequency: int = 5) -> void: 181 | self.strength = strength 182 | self.duration = duration 183 | self.frequency = frequency 184 | 185 | _apply_shake() 186 | 187 | 188 | ## Resets from shake 189 | func reset_from_shake() -> void: 190 | if camera is Camera2D: 191 | _reset_from_shake_2d() 192 | elif camera is Camera3D: 193 | _reset_from_shake_3d() 194 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/assets/AbilityContainer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | AbilityContainer 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/nodes/gameplay_attribute_map.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/GameplayAttributeMap.svg") 2 | @tool 3 | class_name GameplayAttributeMap extends Node 4 | 5 | ## A GameplayAttributeMap is the core node which makes the magic for you.[br] 6 | ## 7 | ## A GameplayAttributeMap stores and handles a set of attributes and effects for your gameplay mechanics. 8 | 9 | 10 | ## Emitted when an attribute changes. 11 | signal attribute_changed(attribute: AttributeSpec) 12 | ## Emitted each time an effect is applied to a specific AttributeSpec. 13 | signal attribute_effect_applied(attribute_effect: AttributeEffect, attribute: AttributeSpec) 14 | ## Emitted after an effect is has been applied for the last time to a specific AttributeSpec. 15 | signal attribute_effect_removed(attribute_effect: AttributeEffect, attribute: AttributeSpec) 16 | ## Emitted once a GameplayEffect has been applied. 17 | ## [br] 18 | ## The signal is called once, even if a time-based AttributeEffect is still going on. 19 | signal effect_applied(effect: GameplayEffect) 20 | 21 | 22 | @export_category("Owner") 23 | ## Is the path to the owning character 24 | @export_node_path var owning_character := NodePath() 25 | 26 | 27 | @export_category("Attributes") 28 | ## Is the array of Attribute resources generated by the inspector plugin. 29 | @export var attributes: Array[AttributeResource] = [] 30 | ## Is the AttributeTable selected resource. 31 | @export var table: AttributeTable = null 32 | 33 | 34 | ## A dictionary whose keys are attributes names and values are [AttributeSpec] instances. 35 | var _attributes_dict: Dictionary = {} 36 | ## A dictionary whose keys are [Timer] id and values are integers representing how many times these timers got called. 37 | ## [br]Once a timer is stopped, those key/value pair should be erased. 38 | var _timeouts_count_dict: Dictionary = {} 39 | ## Is the list of child GameplayEffect nodes 40 | var effects: Array[GameplayEffect] = []: 41 | get: 42 | var _effects: Array[GameplayEffect] = [] 43 | 44 | for child in get_children(): 45 | if child is GameplayEffect: 46 | _effects.append(child) 47 | 48 | return _effects 49 | 50 | 51 | func _add_attribute_spec(spec: AttributeResource) -> void: 52 | if Engine.is_editor_hint(): 53 | attributes.append(spec) 54 | 55 | 56 | func _apply_initial_effects() -> void: 57 | for effect in effects: 58 | apply_effect(effect) 59 | 60 | 61 | func _handle_character_child_entered_tree(node: Node) -> void: 62 | if node is GameplayEffect: 63 | add_child(node) 64 | 65 | 66 | func _get_attribute_at(index: int) -> AttributeResource: 67 | if Engine.is_editor_hint(): 68 | if attributes.size() > index: 69 | return attributes[index] 70 | 71 | attributes.append(AttributeResource.new()) 72 | return attributes[index] 73 | else: 74 | return null 75 | 76 | 77 | func _ready() -> void: 78 | if not Engine.is_editor_hint(): 79 | _setup_attributes() 80 | 81 | if owning_character != null and not owning_character.is_empty(): 82 | var character = get_node(owning_character) 83 | 84 | if character: 85 | character.child_entered_tree.connect(func (child): 86 | if child is GameplayEffect: 87 | apply_effect(child) 88 | ) 89 | 90 | _apply_initial_effects() 91 | 92 | 93 | func _setup_attributes() -> void: 94 | _attributes_dict = {} 95 | 96 | for attribute in attributes: 97 | var previous = get_attribute_by_name(attribute.attribute_name) 98 | 99 | if previous: 100 | previous.free() 101 | 102 | var spec = AttributeSpec.from_attribute(attribute) 103 | 104 | spec.changed.connect(func (attribute): 105 | attribute_changed.emit(attribute) 106 | ) 107 | 108 | _attributes_dict[spec.attribute_name] = spec 109 | 110 | 111 | func _setup_owning_character() -> void: 112 | if owning_character == null or owning_character.is_empty(): 113 | return 114 | 115 | var owning_character = get_node(owning_character) 116 | 117 | if owning_character: 118 | owning_character.child_entered_tree.connect(func (child): 119 | if child is GameplayEffect: 120 | apply_effect(child) 121 | ) 122 | 123 | 124 | func _update_attribute(index: int, key: String, value: float) -> void: 125 | if Engine.is_editor_hint(): 126 | if attributes.size() >= index: 127 | if key in attributes[index]: 128 | attributes[index][key] = value 129 | 130 | 131 | ## Applies an effect on current GameplayAttributeMap 132 | func apply_effect(effect: GameplayEffect) -> void: 133 | var _effect = effect.duplicate() 134 | 135 | effect.queue_free() 136 | 137 | if multiplayer and not multiplayer.is_server(): 138 | return 139 | 140 | if effect == null: 141 | return 142 | 143 | for attribute_affected in effect.attributes_affected: 144 | if not attribute_affected.attribute_name in _attributes_dict: 145 | return 146 | 147 | if attribute_affected.life_time == AttributeEffect.LIFETIME_ONE_SHOT: 148 | var spec = _attributes_dict[attribute_affected.attribute_name] 149 | 150 | if not attribute_affected.should_apply(_effect, self): 151 | continue 152 | 153 | _attributes_dict[attribute_affected.attribute_name].apply_attribute_effect(attribute_affected) 154 | 155 | attribute_effect_applied.emit(attribute_affected, spec) 156 | attribute_effect_removed.emit(attribute_affected, spec) 157 | elif attribute_affected.life_time == AttributeEffect.LIFETIME_TIME_BASED: 158 | var timer = Timer.new() 159 | var timer_id = timer.get_instance_id() 160 | 161 | _timeouts_count_dict[timer_id] = 0 162 | 163 | timer.autostart = true 164 | timer.wait_time = attribute_affected.apply_every_second 165 | 166 | timer.timeout.connect(func (): 167 | var spec = _attributes_dict[attribute_affected.attribute_name] 168 | 169 | if not attribute_affected.should_apply(_effect, self): 170 | return 171 | 172 | if attribute_affected.max_applications != 0 and attribute_affected.max_applications == _timeouts_count_dict[timer_id]: 173 | attribute_effect_removed.emit(attribute_affected, spec) 174 | timer.stop() 175 | _timeouts_count_dict.erase(timer_id) 176 | remove_child(timer) 177 | else: 178 | _attributes_dict[attribute_affected.attribute_name].apply_attribute_effect(attribute_affected) 179 | 180 | attribute_effect_applied.emit(attribute_affected, spec) 181 | 182 | if attribute_affected.max_applications != 0: 183 | _timeouts_count_dict[timer_id] += 1 184 | ) 185 | 186 | add_child(timer) 187 | 188 | effect_applied.emit(_effect) 189 | 190 | 191 | ## Gets an instance of AttributeSpec by it's attribute_name 192 | func get_attribute_by_name(attribute_name: String) -> AttributeSpec: 193 | if _attributes_dict.has(attribute_name): 194 | return _attributes_dict.get(attribute_name) 195 | 196 | return null 197 | 198 | 199 | ## Gets all attributes as a dictionary 200 | ## [br]The dictionary keys are the attribute names and the values are the current buffed value of the attribute. 201 | func get_attributes_dict() -> Dictionary: 202 | var keys = _attributes_dict.keys() 203 | var out = {} as Dictionary 204 | 205 | for key in keys: 206 | var attr = get_attribute_by_name(key) 207 | out[key] = attr.current_buffed_value 208 | return out 209 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/attributes_and_abilities/resources/ability.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/attributes_and_abilities/assets/Ability.svg") 2 | class_name Ability extends Resource 3 | 4 | ## Represents an ability or skill 5 | 6 | ## Emitted the the ability has been activated. 7 | signal activated(Ability, ActivationEvent) 8 | ## Emitted the the ability has been blocked in [method Ability.try_activate]. 9 | signal blocked(Ability, ActivationEvent) 10 | ## Emitted the the ability has been cancelled. 11 | signal cancelled(Ability, ActivationEvent) 12 | ## Emitted the the ability has been ended. 13 | signal ended(Ability, ActivationEvent) 14 | 15 | @export_group("User interface", "ui_") 16 | ## Is the icon shown in your user interface. 17 | @export var ui_icon: Texture2D = null 18 | ## Is the name shown in your user interface. 19 | @export var ui_name: String = "" 20 | 21 | @export_group("Cooldown", "cooldown_") 22 | ## Sets a cooldown duration before the ability can be used again. 23 | @export var cooldown_duration := 0.0 24 | ## Sets the cooldown as continuos, so it will keep on working every [member Ability.cooldown_duration] seconds. 25 | @export var cooldown_continuos := false 26 | 27 | @export_group("Ability Granting", "grant_") 28 | ## Automatically grants an ability when added to a [AbilityContainer]. 29 | @export var grant_automatic := true 30 | ## Adds these tags to the owning [AbilityContainer] when the ability is granted. 31 | ## [br]Useful for skill trees or skill progression systems. 32 | @export var grant_tags: Array[String] = [] 33 | ## Tags required for granting an ability 34 | ## [br]These tags will be checked to ensure that the [Ability] can be granted or not. 35 | @export var grant_tags_required: Array[String] = [] 36 | 37 | @export_group("Tags", "tags_") 38 | ## Tags added once ability has been activated. 39 | ## [br]Use [member Ability.tags_to_remove_on_activation] to remove some of these tags after the activation. 40 | @export var tags_activation: Array[String] = [] 41 | ## Tags required for activation. 42 | ## [br]The ability cannot be activated if the [AbilityContainer] does not have all the tags provided here. 43 | @export var tags_activation_required: Array[String] = [] 44 | ## Blocks execution if ore or more tags are contained by [AbilityContainer] 45 | ## [br]Use these tags to block the activation of an ability. 46 | @export var tags_block: Array[String] = [] 47 | ## Tags required for cancellation. 48 | ## [br]Use these tags to determine if an ability can be cancelled or not. 49 | @export var tags_cancellation_required: Array[String] = [] 50 | ## Tags added when cooldown is started. 51 | ## [br]Use [member Ability.tags_to_remove_on_cooldown_start] to remove some tags from this array when a cooldown starts. 52 | @export var tags_cooldown_start: Array[String] = [] 53 | ## Tags added when cooldown is ended. 54 | ## [br]Use [member Ability.tags_to_remove_on_cooldown_end] to remove some tags from this array when a cooldown ends. 55 | @export var tags_cooldown_end: Array[String] = [] 56 | ## Tags which will block the end of an [Ability]. 57 | @export var tags_end_blocking: Array[String] = [] 58 | ## Tags which will be removed on activation. 59 | @export var tags_to_remove_on_activation: Array[String] = [] 60 | ## Tags which will be removed on block. 61 | @export var tags_to_remove_on_block: Array[String] = [] 62 | ## Tags which will be removed on cancellation. 63 | @export var tags_to_remove_on_cancellation: Array[String] = [] 64 | ## Tags which will be removed when a cooldown start. 65 | @export var tags_to_remove_on_cooldown_start: Array[String] = [] 66 | ## Tags which will be removed when a cooldown end. 67 | @export var tags_to_remove_on_cooldown_end: Array[String] = [] 68 | ## Tags which will be removed when an ability ends. 69 | @export var tags_to_remove_on_end: Array[String] = [] 70 | 71 | 72 | ## Is [code]true[/code] if [member Ability.cooldown_duration] is greater than [code]0.0[/code], [code]false[/code] otherwise. 73 | var has_cooldown: bool: 74 | get: 75 | return cooldown_duration > 0.0 76 | 77 | 78 | ## Activates the effect. This will forcefully activate it even if some criteria do not match. 79 | ## You should use [method Ability.try_activate] instead for a proper (and safer) flow. 80 | func activate(activation_event: ActivationEvent) -> void: 81 | activated.emit(self, activation_event) 82 | 83 | 84 | ## Return [code]true[/code] if the ability can be activated, [code]false[/code] otherwise. 85 | ## [br]Always return: [code]true[/code] if [member Ability.tags_activation_required] is empty. 86 | func can_activate(activation_event: ActivationEvent) -> bool: 87 | if tags_activation_required.size() > 0: 88 | return has_all_tags(tags_activation_required, activation_event.tags) 89 | 90 | return true 91 | 92 | 93 | ## Returns [code]true[/code] if the ability can be blocked, [code]false[/code] otherwise. 94 | ## [br]Always return: [code]true[/code] if [member Ability.tags_block] is empty. 95 | func can_block(activation_event: ActivationEvent) -> bool: 96 | if tags_block.size() > 0: 97 | return has_some_tags(tags_block, activation_event.tags) 98 | 99 | return false 100 | 101 | 102 | ## Return [code]true[/code] if the ability can be cancelled, [code]false[/code] otherwise. 103 | ## [br]Always return: [code]true[/code] if [member Ability.tags_cancellation_required] is empty. 104 | func can_cancel(activation_event: ActivationEvent) -> bool: 105 | if tags_cancellation_required.size() > 0: 106 | return has_some_tags(tags_cancellation_required, activation_event.tags) 107 | 108 | return false 109 | 110 | 111 | ## Return [code]true[/code] if the ability can be ended, [code]false[/code] otherwise. 112 | ## [br]Always return: [code]true[/code] if [member Ability.tags_end_blocking] is empty. 113 | func can_end(activation_event: ActivationEvent) -> bool: 114 | if tags_end_blocking.size() > 0: 115 | return !has_some_tags(tags_end_blocking, activation_event.tags) 116 | 117 | return true 118 | 119 | 120 | ## Cancels an ability forcefully. Remember to call [method Ability.can_cancel] first. 121 | ## [br]This will forcefully activate it even if some criteria do not match. 122 | ## [br]You should use [method Ability.try_activate] instead for a proper (and safer) flow. 123 | func cancel(activation_event: ActivationEvent) -> void: 124 | if can_cancel(activation_event): 125 | cancelled.emit(self, activation_event) 126 | 127 | 128 | ## Ends the ability forcefully. Remember to call [method Ability.can_end] first. 129 | ## [br]This will forcefully activate it even if some criteria do not match. 130 | ## [br]You should use [method Ability.try_activate] instead for a proper (and safer) flow. 131 | func end_ability(activation_event: ActivationEvent) -> void: 132 | if can_end(activation_event): 133 | ended.emit(self, activation_event) 134 | 135 | 136 | ## Checks if the parameter [code]tags[/code] has all tags included in [code]tags_to_check[/code]. 137 | ## [br]It checks if [code]tags[/code] has all [code]tags_to_check[/code]. 138 | func has_all_tags(tags: Array[String], tags_to_check: Array[String]) -> bool: 139 | for t in tags: 140 | if not tags_to_check.has(t): 141 | return false 142 | 143 | return true 144 | 145 | ## Checks if the parameter [code]tags[/code] has some tags included in [code]tags_to_check[/code]. 146 | ## [br]It checks if [code]tags[/code] has all [code]tags_to_check[/code]. 147 | func has_some_tags(tags: Array[String], tags_to_check: Array[String]) -> bool: 148 | for t in tags: 149 | if tags_to_check.has(t): 150 | return true 151 | 152 | return false 153 | 154 | 155 | ## Tries to activate an ability, then tries to cancel it and then tries to end it. 156 | func try_activate(activation_event: ActivationEvent) -> void: 157 | if can_block(activation_event): 158 | blocked.emit(self, activation_event) 159 | return 160 | 161 | if can_activate(activation_event): 162 | activate(activation_event) 163 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Godot-gameplay-attributes" 14 | run/main_scene="res://examples/examples.tscn" 15 | config/features=PackedStringArray("4.2", "Forward Plus") 16 | config/icon="res://icon.svg" 17 | 18 | [autoload] 19 | 20 | TurnManager="*res://addons/godot_gameplay_systems/turn_based/./autoloads/turn_manager.gd" 21 | 22 | [display] 23 | 24 | window/stretch/aspect="keep_width" 25 | 26 | [dotnet] 27 | 28 | project/assembly_name="Godot-gameplay-attributes" 29 | 30 | [editor] 31 | 32 | version_control/plugin_name="GitPlugin" 33 | version_control/autoload_on_startup=true 34 | 35 | [editor_plugins] 36 | 37 | enabled=PackedStringArray("res://addons/godot_gameplay_systems/plugin.cfg", "res://addons/gut/plugin.cfg") 38 | 39 | [input] 40 | 41 | resurrect={ 42 | "deadzone": 0.5, 43 | "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":82,"key_label":0,"unicode":114,"echo":false,"script":null) 44 | ] 45 | } 46 | fireball={ 47 | "deadzone": 0.5, 48 | "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":32,"key_label":0,"unicode":32,"echo":false,"script":null) 49 | ] 50 | } 51 | close_example={ 52 | "deadzone": 0.5, 53 | "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":4194305,"key_label":0,"unicode":0,"echo":false,"script":null) 54 | ] 55 | } 56 | fps_move_forward={ 57 | "deadzone": 0.5, 58 | "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,"echo":false,"script":null) 59 | ] 60 | } 61 | fps_move_backward={ 62 | "deadzone": 0.5, 63 | "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,"echo":false,"script":null) 64 | ] 65 | } 66 | fps_strafe_left={ 67 | "deadzone": 0.5, 68 | "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,"echo":false,"script":null) 69 | ] 70 | } 71 | fps_strafe_right={ 72 | "deadzone": 0.5, 73 | "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,"echo":false,"script":null) 74 | ] 75 | } 76 | fps_shoot={ 77 | "deadzone": 0.5, 78 | "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(461, 38),"global_position":Vector2(469, 122),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) 79 | ] 80 | } 81 | fps_jump={ 82 | "deadzone": 0.5, 83 | "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":32,"key_label":0,"unicode":32,"echo":false,"script":null) 84 | ] 85 | } 86 | fps_weapon_1={ 87 | "deadzone": 0.5, 88 | "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":49,"key_label":0,"unicode":49,"echo":false,"script":null) 89 | ] 90 | } 91 | fps_weapon_2={ 92 | "deadzone": 0.5, 93 | "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":50,"key_label":0,"unicode":50,"echo":false,"script":null) 94 | ] 95 | } 96 | diablo_like_inventory={ 97 | "deadzone": 0.5, 98 | "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":67,"key_label":0,"unicode":99,"echo":false,"script":null) 99 | , 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":73,"key_label":0,"unicode":105,"echo":false,"script":null) 100 | ] 101 | } 102 | diablo_like_ability_1={ 103 | "deadzone": 0.5, 104 | "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":49,"key_label":0,"unicode":49,"echo":false,"script":null) 105 | ] 106 | } 107 | diablo_like_ability_2={ 108 | "deadzone": 0.5, 109 | "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":50,"key_label":0,"unicode":50,"echo":false,"script":null) 110 | ] 111 | } 112 | diablo_like_ability_3={ 113 | "deadzone": 0.5, 114 | "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":51,"key_label":0,"unicode":51,"echo":false,"script":null) 115 | ] 116 | } 117 | diablo_like_ability_4={ 118 | "deadzone": 0.5, 119 | "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":52,"key_label":0,"unicode":52,"echo":false,"script":null) 120 | ] 121 | } 122 | diablo_like_move_to={ 123 | "deadzone": 0.5, 124 | "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":1,"canceled":false,"pressed":false,"double_click":false,"script":null) 125 | ] 126 | } 127 | diablo_like_add_gold={ 128 | "deadzone": 0.5, 129 | "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":71,"key_label":0,"unicode":103,"echo":false,"script":null) 130 | ] 131 | } 132 | fps_interact={ 133 | "deadzone": 0.5, 134 | "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":70,"key_label":0,"unicode":102,"echo":false,"script":null) 135 | ] 136 | } 137 | fps_drop={ 138 | "deadzone": 0.5, 139 | "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":88,"key_label":0,"unicode":120,"echo":false,"script":null) 140 | ] 141 | } 142 | 143 | [layer_names] 144 | 145 | 2d_physics/layer_1="terrain" 146 | 3d_physics/layer_1="terrain" 147 | 2d_physics/layer_2="player" 148 | 3d_physics/layer_2="player" 149 | 2d_physics/layer_3="hazard" 150 | 3d_physics/layer_3="enemies" 151 | 2d_physics/layer_4="collectible" 152 | 3d_physics/layer_4="items" 153 | 2d_physics/layer_5="projectile" 154 | 3d_physics/layer_5="interactables" 155 | 3d_physics/layer_6="bullet" 156 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/interactables/nodes/interaction_manager.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/interactables/assets/InteractionIcon.png") 2 | @tool 3 | class_name InteractionManager extends Node 4 | 5 | 6 | ## Handles interactions. 7 | 8 | 9 | ## Emitted when a bound raycast detects and interactable. 10 | signal interactable_focus_in(interactable: Node) 11 | ## Emitted when a bound raycast loses a focus to an interactable. 12 | signal interactable_focus_out(interactable: Node) 13 | ## Emitted when an interaction is blocked. 14 | signal interaction_blocked(interactable: Node, interaction: Interaction) 15 | ## Emitted when an interaction is cancelled. 16 | signal interaction_cancelled(interactable: Node, interaction: Interaction) 17 | ## Emitted when an interaction is ended. 18 | signal interaction_ended(interactable: Node, interaction: Interaction) 19 | ## Emitted when an interaction is started. 20 | signal interaction_started(interactable: Node, interaction: Interaction) 21 | ## Emitted when an interaction is refused to block. 22 | signal interaction_block_refused(interactable: Node, interaction: Interaction) 23 | ## Emitted when an interaction is refused to cancel. 24 | signal interaction_cancel_refused(interactable: Node, interaction: Interaction) 25 | ## Emitted when an interaction is refused to end. 26 | signal interaction_end_refused(interactable: Node, interaction: Interaction) 27 | ## Emitted when an interaction is refused to start. 28 | signal interaction_start_refused(interactable: Node, interaction: Interaction) 29 | ## Emitted when tags have changed. 30 | signal tags_changed() 31 | 32 | @export_category("Interaction") 33 | ## The path to the interaction owner. 34 | @export_node_path("Node2D", "Node3D") var interacting_owner_path := NodePath() 35 | ## The path to the interaction raycast node. 36 | @export_node_path("InteractionRayCast2D", "InteractionRayCast3D") var interaction_raycast_path := NodePath() 37 | 38 | @export_group("Tags", "tags_") 39 | ## The tags associated to this interaction manager. 40 | @export var tags: Array[String] = [] 41 | ## Tags which will blocks the interaction completely. 42 | @export var tags_blocking_interaction: Array[String] = [] 43 | ## Tags required to trigger an interaction. 44 | @export var tags_required_to_interact: Array[String] = [] 45 | 46 | 47 | ## Checks if it can interact. 48 | var can_interact: bool: 49 | get: 50 | if tags_blocking_interaction.size() == 0 and tags_required_to_interact.size() == 0: 51 | return true 52 | 53 | if tags_blocking_interaction.size() > 0: 54 | for tag in tags_blocking_interaction: 55 | if tags.has(tag): 56 | return false 57 | 58 | if tags_required_to_interact.size() > 0: 59 | for tag in tags_required_to_interact: 60 | if not tags.has(tag): 61 | return false 62 | 63 | return focused_interactable != null 64 | ## Is the interacting owner. Usually a [CharacterBody2D] or [CharacterBody3D]. 65 | var interacting_owner: Node: 66 | get: 67 | if interacting_owner != null: 68 | return interacting_owner 69 | 70 | interacting_owner = get_node(interacting_owner_path) 71 | return interacting_owner 72 | set(value): 73 | interacting_owner = value 74 | ## Is an interaction raycast. Used to determine if the owner can see and reach the interactable. 75 | var interaction_raycast: Node: 76 | get: 77 | if interaction_raycast != null: 78 | return interaction_raycast 79 | 80 | interaction_raycast = get_node(interaction_raycast_path) 81 | return interaction_raycast 82 | set(value): 83 | interaction_raycast = value 84 | ## Is [code]true[/code] if the [InteractionManager] 85 | var is_interacting: bool: 86 | get: 87 | if current_interactable == null: 88 | return false 89 | 90 | if current_interactable.interaction == null: 91 | return false 92 | 93 | if current_interactable.interaction.tags_added_on_start.size() > 0: 94 | return has_tags(current_interactable.interaction.tags_added_on_start) 95 | 96 | return true 97 | ## It's the current interactable [Node]. This variable is stored when you start an interaction and cleared when you end an interaction. 98 | var current_interactable: Node = null 99 | ## It's the current focused interactable [Node]. 100 | var focused_interactable: Node = null 101 | ## It's the current focused interactable [Node]'s [Interaction]. 102 | var interaction: Interaction: 103 | get: 104 | if current_interactable == null: 105 | return null 106 | 107 | return current_interactable.interaction if is_interacting else null 108 | 109 | 110 | func _handle_focus_in(node: Node) -> void: 111 | interactable_focus_in.emit(node) 112 | focused_interactable = node 113 | 114 | 115 | func _handle_focus_out(node: Node) -> void: 116 | interactable_focus_out.emit(node) 117 | focused_interactable = null 118 | 119 | 120 | ## Overrides virtual and handles input events 121 | func _input(input: InputEvent) -> void: 122 | if is_interacting and interaction_raycast != null: 123 | current_interactable.interaction.handle_input(input, self, current_interactable) 124 | 125 | 126 | ## Overrides virtual and handles process 127 | func _process(delta: float) -> void: 128 | if is_interacting and interaction_raycast != null: 129 | current_interactable.interaction.handle_process(delta, self, current_interactable) 130 | 131 | 132 | ## Overrides virtual and handles physics process 133 | func _physics_process(delta: float) -> void: 134 | if is_interacting and interaction_raycast != null: 135 | current_interactable.interaction.handle_physics_process(delta, self, current_interactable) 136 | 137 | 138 | func _ready() -> void: 139 | if interaction_raycast != null: 140 | interaction_raycast.interactable_detected.connect(_handle_focus_in) 141 | interaction_raycast.interactable_focus_in.connect(_handle_focus_in) 142 | interaction_raycast.interactable_focus_out.connect(_handle_focus_out) 143 | 144 | 145 | ## Adds a tag. 146 | func add_tag(tag: String) -> void: 147 | if not tags.has(tag): 148 | tags.append(tag) 149 | tags_changed.emit() 150 | 151 | 152 | ## Adds many tags at once. 153 | ## [br]Note: this will emit the [signal InteractionManager.tags_changed] only once. 154 | func add_tags(tags: Array[String]) -> void: 155 | var updated = false 156 | 157 | for tag in tags: 158 | if not self.tags.has(tag): 159 | self.tags.append(tag) 160 | updated = true 161 | 162 | if updated: 163 | tags_changed.emit() 164 | 165 | 166 | ## Returns [code]true[/code] if can ends interaction, [code]false[/code] otherwise. 167 | func can_end_interaction() -> bool: 168 | if can_interact and interaction != null: 169 | if interaction.tags_required_to_block.size() > 0: 170 | return not has_tags(interaction.tags_required_to_block) and has_tags(interaction.tags_required_to_end) 171 | else: 172 | return has_tags(interaction.tags_required_to_end) 173 | return true 174 | 175 | 176 | ## Returns [code]true[/code] if can starts interaction, [code]false[/code] otherwise. 177 | func can_start_interaction() -> bool: 178 | return can_interact and focused_interactable.interaction != null 179 | 180 | 181 | ## Ends an interaction with the currently focused node. 182 | ## [br]Note: this will emit the [signal InteractionManager.tags_changed] only once. 183 | ## [br]Note: this will emit the [signal InteractionManager.interaction_ended] only once. 184 | func end_interaction() -> void: 185 | if can_end_interaction(): 186 | if current_interactable.has_method("_on_interaction_ended"): 187 | current_interactable.call("_on_interaction_ended", self) 188 | 189 | current_interactable.interaction.on_before_interaction_end(self, current_interactable) 190 | add_tags(current_interactable.interaction.tags_added_on_end) 191 | remove_tags(current_interactable.interaction.tags_removed_on_end) 192 | interaction_ended.emit(current_interactable) 193 | current_interactable = null 194 | 195 | 196 | ## Starts an interaction with the currently focused node. 197 | ## [br]Note: this will emit the [signal InteractionManager.tags_changed] only once. 198 | ## [br]Note: this will emit the [signal InteractionManager.interaction_started] only once. 199 | func start_interaction() -> void: 200 | if can_start_interaction(): 201 | focused_interactable.interaction.on_before_interaction_start(self, focused_interactable) 202 | add_tags(focused_interactable.interaction.tags_added_on_start) 203 | remove_tags(focused_interactable.interaction.tags_removed_on_start) 204 | interaction_started.emit(focused_interactable) 205 | current_interactable = focused_interactable 206 | 207 | if current_interactable.has_method("_on_interaction_started"): 208 | current_interactable.call("_on_interaction_started", self) 209 | 210 | 211 | ## Checks if has a tag. 212 | func has_tag(tag: String) -> bool: 213 | return tags.has(tag) 214 | 215 | 216 | ## Checks if has tags. 217 | ## [br]If the param [code]match_all[/code] is [code]true[/code], then this [InteractionManager] must have all the passed tags. 218 | ## [br]If the param [code]match_all[/code] is [code]false[/code], then this function returns [code]true[/code] if this [InteractionManager] has one or many tags passed. 219 | func has_tags(tags: Array[String], match_all: bool = true) -> bool: 220 | for tag in tags: 221 | if not self.tags.has(tag) and match_all: 222 | return false 223 | elif self.tags.has(tag) and not match_all: 224 | return true 225 | return true 226 | 227 | 228 | ## Removes a tag. Returns [code]true[/code] if the tag is removed, [code]false[/code] otherwise. 229 | func remove_tag(tag: String) -> bool: 230 | var index = tags.find(tag) 231 | 232 | if index < 0: 233 | return false 234 | 235 | tags.remove_at(index) 236 | tags_changed.emit() 237 | 238 | return true 239 | 240 | 241 | ## Removes a tag. Returns the count of the removed elements. 242 | func remove_tags(tags: Array[String]) -> int: 243 | var count = 0 244 | 245 | for tag in tags: 246 | var index = self.tags.find(tag) 247 | 248 | if index >= 0: 249 | self.tags.remove_at(index) 250 | count += 1 251 | 252 | if count > 0: 253 | tags_changed.emit() 254 | 255 | return count 256 | -------------------------------------------------------------------------------- /addons/godot_gameplay_systems/inventory_system/nodes/inventory.gd: -------------------------------------------------------------------------------- 1 | @icon("res://addons/godot_gameplay_systems/inventory_system/assets/InventoryIcon.png") 2 | @tool 3 | class_name Inventory extends Node 4 | 5 | 6 | ## Represents an inventory. 7 | ## 8 | ## Note: an Inventory should be unique for a character. 9 | 10 | 11 | enum LifeCycle { 12 | ## An [Item] has been activated. 13 | Activated = 0, 14 | ## An [Item] has been added to the inventory. 15 | Added = 1, 16 | ## An [Item] has been removed from an inventory. 17 | Removed = 2, 18 | } 19 | 20 | 21 | ## Emitted when an [Item] is properly activated with a specific activation type. 22 | signal item_activated(item: Item, activation_type: int) 23 | ## Emitted when an [Item] has been added to this inventory. 24 | signal item_added(item: Item) 25 | ## Emitted when an [Item] has been activated and it's own stack has been consumed entirely. 26 | signal item_depleted(item: Item) 27 | ## Emitted when an [Item] has been removed from this inventory. 28 | signal item_removed(item: Item) 29 | ## Emitted when the [Inventory] refuses to add an [Item] because of lacking tags. 30 | signal refused_to_add(item: Item) 31 | ## Emitted when the [Inventory] refuses to remove an [Item] because of lacking tags. 32 | signal refused_to_remove(item: Item) 33 | 34 | 35 | @export_category("Inventory") 36 | ## Is the path to the owner (aka the character or entity which owns the inventory). 37 | @export_node_path("Node2D", "Node3D") var owner_path: NodePath = NodePath() 38 | ## Is the path to the related [Equipment] node. 39 | ## [br]This is not necessary unless you want to make this [Inventory] to talk with an [Equipment] automatically. 40 | ## [br]For example: a classic Doom/Quake-like game, will not have an inventory system, 41 | ## all weapons are set at startup and can be used only if some tags are set. 42 | @export_node_path("Equipment") var equipment_path: NodePath = NodePath() 43 | ## Is the array of items. 44 | @export var items: Array[Item] = [] 45 | ## Tags associated to this [Inventory] 46 | @export var tags: Array[String] = [] 47 | 48 | ## Related [Equipment] node if any. 49 | var equipment: Equipment 50 | 51 | 52 | ## Internal method. Handles all life cycle events managing the tagging system. 53 | func _handle_lifecycle_tags(life_cycle: LifeCycle, item: Item) -> void: 54 | assert(item != null, "Item cannot be null") 55 | 56 | match life_cycle: 57 | LifeCycle.Activated: 58 | add_tags(item.tags_added_on_activation) 59 | remove_tags(item.tags_removed_on_activation) 60 | LifeCycle.Added: 61 | add_tags(item.tags_added_on_add) 62 | remove_tags(item.tags_removed_on_add) 63 | LifeCycle.Removed: 64 | add_tags(item.tags_added_on_remove) 65 | remove_tags(item.tags_removed_on_remove) 66 | 67 | 68 | ## Ready function. Adds all starting [member Item.tags_added_on_add] when the node is ready. 69 | func _ready() -> void: 70 | setup() 71 | 72 | 73 | 74 | ## Activates an [Item] with an optional [code]activation_type[/code]. 75 | ## Activation can occur only if [method Item._can_activate] returns [code]true[/code]. 76 | func activate(item: Item, activation_type: int = 0) -> void: 77 | var activation_event = ItemActivationEvent.new(self, activation_type) 78 | 79 | if item._can_activate(activation_event): 80 | item._activate(activation_event) 81 | item_activated.emit(item, activation_type) 82 | _handle_lifecycle_tags(LifeCycle.Activated, item) 83 | 84 | if item.can_stack and item.decrease_stack_on_use: 85 | item.quantity_current = clampi(item.quantity_current - 1, 0, item.quantity_current) 86 | 87 | if item.quantity_current == 0: 88 | var index = items.find(item) 89 | 90 | if index >= 0: 91 | items.remove_at(index) 92 | item_depleted.emit(item) 93 | 94 | 95 | ## Adds an item to the inventory and connects it's signals 96 | ## [br][Item] is duplicated when added. If an [Item] has [member Item.can_stack], it will stack [member Item.quantity_current] until [member Item.quantity_max] is reached. 97 | ## [br]If the quantity exceed the maximum, another [Item] will be added to the inventory with the difference in stacks. 98 | ## [br]Returns the copied [Item]. 99 | ## [br]Note: always check it the [Item] is [code]null[/code] or not, since it could not be added if some tags requirements are not met. 100 | func add_item(item: Item) -> Item: 101 | if not can_add(item): 102 | refused_to_add.emit(item) 103 | return null 104 | 105 | # Finds the first item which is already in the inventory, is stackable and it's own stack is not at the maximum quantity. 106 | var found = find_by(func (x: Item): return item.name == x.name and x.can_stack and x.quantity_current < x.quantity_max) 107 | 108 | # Check if there's another [Item] in the inventory and if is stackable. 109 | if found and found.can_stack: 110 | var new_quantity = found.quantity_current + item.quantity_current 111 | 112 | # It's not stackable to an infinite value 113 | if found.quantity_max != 0: 114 | found.quantity_current = clampi(new_quantity, 0, found.quantity_max) 115 | 116 | if new_quantity > found.quantity_max: 117 | var duplicated_item = found.duplicate() as Item 118 | 119 | duplicated_item.quantity_current = new_quantity - found.quantity_max 120 | 121 | return add_item(duplicated_item) 122 | # Stacking it to infinite value 123 | else: 124 | found.quantity_current = new_quantity 125 | item_added.emit(found) 126 | return found 127 | 128 | var item_to_return = item.duplicate() 129 | items.append(item_to_return) 130 | 131 | item_added.emit(item_to_return) 132 | 133 | _handle_lifecycle_tags(LifeCycle.Added, item_to_return) 134 | 135 | return item_to_return 136 | 137 | 138 | ## Adds many items. 139 | func add_items(_items: Array[Item]) -> Array[Item]: 140 | var out: Array[Item] = [] 141 | 142 | for i in _items: 143 | out.append(add_item(i)) 144 | 145 | return out 146 | 147 | 148 | ## Adds a tag to [member Inventory.tags]. 149 | ## [br]Duplicated tags are discarded 150 | func add_tag(tag: String) -> void: 151 | if tag == null: 152 | return 153 | 154 | if not tags.has(tag): 155 | tags.append(tag) 156 | 157 | 158 | ## Adds many tags to [member Inventory.tags]. 159 | ## [br]Duplicated tags are discarded 160 | func add_tags(_tags: Array[String]) -> void: 161 | if _tags.size() == 0: 162 | return 163 | 164 | for t in _tags: 165 | if not tags.has(t): 166 | tags.append(t) 167 | 168 | 169 | ## Checks if an item can be activated with an optional activation id. 170 | func can_activate(item: Item, activation_type: int = 0) -> bool: 171 | if item.tags_required_to_activate.size() > 0: 172 | for tag in item.tags_required_to_activate: 173 | if not tags.has(tag): 174 | return false 175 | 176 | return item._can_activate(ItemActivationEvent.new(self, activation_type)) 177 | 178 | 179 | ## Checks if an [Item] can be added to this [Inventory] checking if all [member Item.tags_required_to_add] exists in this [Inventory] 180 | func can_add(item: Item) -> bool: 181 | if item.tags_required_to_add.size() == 0: 182 | return true 183 | 184 | for tag in item.tags_required_to_add: 185 | if not tags.has(tag): 186 | return false 187 | 188 | return true 189 | 190 | 191 | ## Checks if an [Item] can be added from this [Inventory] checking if all [member Item.tags_required_to_remove] exists in this [Inventory] 192 | func can_remove(item: Item) -> bool: 193 | if item.tags_required_to_remove.size() == 0: 194 | return true 195 | 196 | for tag in item.tags_required_to_remove: 197 | if not tags.has(tag): 198 | return false 199 | 200 | return true 201 | 202 | ## Counts all items. 203 | ## [br]If [code]count_stacks[/code] is passed to [code]true[/code], the count will returns also count all stacked quantities. 204 | func count_items(count_stacks: bool = false) -> int: 205 | if count_stacks: 206 | var count = 0 207 | 208 | for item in items: 209 | count += item.quantity_current 210 | 211 | return count 212 | else: 213 | return items.size() 214 | 215 | 216 | ## Counts all items by a given predicate. 217 | ## [br]If [code]count_stacks[/code] is passed to [code]true[/code], the count will returns also count all stacked quantities. 218 | func count_items_by(predicate: Callable, count_stacks: bool = false) -> int: 219 | var num := 0 220 | 221 | for i in items: 222 | if predicate.call(i): 223 | if count_stacks: 224 | num += i.quantity_current 225 | else: 226 | num += 1 227 | 228 | return num 229 | 230 | 231 | ## Filters [member Inventory.items] by the [Callable] predicate. 232 | ## [br]It returns all [Item]s which satisfy the predicate. 233 | func filter_by(predicate: Callable) -> Array[Item]: 234 | var out: Array[Item] = [] 235 | 236 | for i in items: 237 | if predicate.call(i): 238 | out.append(i) 239 | 240 | return out 241 | 242 | 243 | ## Find first [Item] which satisfies the [Callable] predicate and returns it. 244 | ## [br]If none are found, then returns [code]null[/code] 245 | func find_by(predicate: Callable) -> Item: 246 | for i in items: 247 | if predicate.call(i): 248 | return i 249 | return null 250 | 251 | 252 | ## Instantiate the [member Item.scene] if any, otherwise returns [code]null[/code]. 253 | ## [br]If it can be istantiated, it will try to call a [code]_from_item(item: Item, inventory: Inventory) -> void[/code] method bound to the packed scene. 254 | func instantiate_item(item: Item) -> Node: 255 | if item.scene != null and item.scene.can_instantiate(): 256 | var instance = item.scene.instantiate() 257 | 258 | if instance.has_method("_from_item"): 259 | instance.call("_from_item", item, self) 260 | 261 | return instance 262 | 263 | return null 264 | 265 | 266 | ## Removes an item from the inventory 267 | ## [br]If [code]bypass_tag_check[/code] is set to [code]true[/code] then the check for [member Item.tags_required_to_remove] will not be performed. 268 | func remove_item(item: Item, bypass_tag_check: bool = false) -> void: 269 | if not can_remove(item) and not bypass_tag_check: 270 | refused_to_remove.emit(item) 271 | return 272 | 273 | var index = items.find(item) 274 | 275 | if index >= 0: 276 | items.remove_at(index) 277 | item_removed.emit(item) 278 | _handle_lifecycle_tags(LifeCycle.Removed, item) 279 | 280 | 281 | ## Removes many items from an inventory 282 | ## [br]If [code]bypass_tag_check[/code] is set to [code]true[/code] then the check for [member Item.tags_required_to_remove] will not be performed. 283 | func remove_items(_items: Array[Item], bypass_tag_check: bool = false) -> void: 284 | for i in _items: 285 | remove_item(i) 286 | 287 | 288 | ## Removes items using a [Callable] predicate. If the predicate is satisfied by the [Item], the [Item] is going to be removed entirely. 289 | ## [br]If [code]bypass_tag_check[/code] is set to [code]true[/code] then the check for [member Item.tags_required_to_remove] will not be performed. 290 | func remove_items_by(predicate: Callable, bypass_tag_check: bool = false) -> void: 291 | for i in items: 292 | if predicate.call(i): 293 | remove_item(i, bypass_tag_check) 294 | 295 | 296 | ## Removes a tag. 297 | func remove_tag(tag: String) -> void: 298 | if tags.has(tag): 299 | tags.erase(tag) 300 | 301 | 302 | ## Removes many tags. 303 | func remove_tags(_tags: Array[String]) -> void: 304 | for t in _tags: 305 | if tags.has(t): 306 | tags.erase(t) 307 | 308 | 309 | ## Programmatically setups an [Inventory] 310 | func setup() -> void: 311 | if not equipment_path.is_empty(): 312 | equipment = get_node(equipment_path) as Equipment 313 | 314 | if not Engine.is_editor_hint() and not owner_path.is_empty(): 315 | var _owner = get_node(owner_path) 316 | _owner.set_meta("ggsInventory", self) 317 | 318 | for i in items: 319 | add_tags(i.tags_added_on_add) 320 | --------------------------------------------------------------------------------