A card game engine for TCGs, primarily Magic: The Gathering but with support for others
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Going through a tutorial to make a draggable card

+291
+5
client/project.godot
··· 18 18 19 19 window/size/viewport_width=1920 20 20 window/size/viewport_height=1080 21 + 22 + [layer_names] 23 + 24 + 2d_physics/layer_1="Card Target Selector" 25 + 2d_physics/layer_2="Card Drop Area"
+53
client/scenes/card.tscn
··· 1 + [gd_scene format=3 uid="uid://d1sxdvrtr2oij"] 2 + 3 + [sub_resource type="RectangleShape2D" id="RectangleShape2D_up0cc"] 4 + size = Vector2(144, 200) 5 + 6 + [node name="Card" type="Control" unique_id=261788604] 7 + custom_minimum_size = Vector2(144, 200) 8 + layout_mode = 3 9 + anchors_preset = 15 10 + anchor_right = 1.0 11 + anchor_bottom = 1.0 12 + offset_right = -1776.0 13 + offset_bottom = -880.0 14 + grow_horizontal = 2 15 + grow_vertical = 2 16 + 17 + [node name="Color" type="ColorRect" parent="." unique_id=128705537] 18 + layout_mode = 1 19 + anchors_preset = 15 20 + anchor_right = 1.0 21 + anchor_bottom = 1.0 22 + grow_horizontal = 2 23 + grow_vertical = 2 24 + color = Color(0.15443997, 0.44, 0.14959998, 0.47058824) 25 + 26 + [node name="State" type="Label" parent="." unique_id=2040209964] 27 + layout_mode = 1 28 + anchors_preset = 15 29 + anchor_right = 1.0 30 + anchor_bottom = 1.0 31 + grow_horizontal = 2 32 + grow_vertical = 2 33 + text = "state" 34 + horizontal_alignment = 1 35 + vertical_alignment = 1 36 + 37 + [node name="DropDetector" type="Area2D" parent="." unique_id=1830197364] 38 + collision_mask = 2 39 + monitorable = false 40 + 41 + [node name="CollisionShape2D" type="CollisionShape2D" parent="DropDetector" unique_id=1037157013] 42 + position = Vector2(72, 100) 43 + shape = SubResource("RectangleShape2D_up0cc") 44 + 45 + [node name="CardStateMachine" type="Node" parent="." unique_id=1584855640] 46 + 47 + [node name="CardBaseState" type="CardState" parent="CardStateMachine" unique_id=281811704] 48 + 49 + [node name="CardClickedState" type="CardState" parent="CardStateMachine" unique_id=718462206] 50 + 51 + [node name="CardDraggingState" type="CardState" parent="CardStateMachine" unique_id=1876014390] 52 + 53 + [node name="CardReleasedState" type="CardState" parent="CardStateMachine" unique_id=1021467954]
+38
client/scenes/main.tscn
··· 1 1 [gd_scene format=3 uid="uid://c8c7akavbxo35"] 2 2 3 + [ext_resource type="PackedScene" uid="uid://d1sxdvrtr2oij" path="res://scenes/card.tscn" id="1_o5qli"] 4 + 5 + [sub_resource type="RectangleShape2D" id="RectangleShape2D_sgp6g"] 6 + size = Vector2(1920, 720) 7 + 3 8 [node name="Main" type="Control" unique_id=1351053636] 4 9 layout_mode = 3 5 10 anchors_preset = 15 ··· 9 14 grow_vertical = 2 10 15 11 16 [node name="ManaGroveNode" type="ManaGroveNode" parent="." unique_id=2018257899] 17 + 18 + [node name="CardDropArea" type="Area2D" parent="." unique_id=142191192] 19 + collision_layer = 2 20 + 21 + [node name="CollisionShape2D" type="CollisionShape2D" parent="CardDropArea" unique_id=1056549176] 22 + position = Vector2(960, 360) 23 + shape = SubResource("RectangleShape2D_sgp6g") 24 + 25 + [node name="BattleUI" type="CanvasLayer" parent="." unique_id=1806992104] 26 + 27 + [node name="Hand" type="HBoxContainer" parent="BattleUI" unique_id=1792213696] 28 + anchors_preset = 7 29 + anchor_left = 0.5 30 + anchor_top = 1.0 31 + anchor_right = 0.5 32 + anchor_bottom = 1.0 33 + offset_left = -350.0 34 + offset_top = -200.0 35 + offset_right = 350.0 36 + grow_horizontal = 2 37 + grow_vertical = 0 38 + 39 + [node name="Card" parent="BattleUI/Hand" unique_id=261788604 instance=ExtResource("1_o5qli")] 40 + layout_mode = 2 41 + 42 + [node name="Card2" parent="BattleUI/Hand" unique_id=1225463640 instance=ExtResource("1_o5qli")] 43 + layout_mode = 2 44 + 45 + [node name="Card3" parent="BattleUI/Hand" unique_id=489102091 instance=ExtResource("1_o5qli")] 46 + layout_mode = 2 47 + 48 + [node name="Card4" parent="BattleUI/Hand" unique_id=1447825586 instance=ExtResource("1_o5qli")] 49 + layout_mode = 2
+2
gdext/src/lib.rs
··· 1 1 use godot::prelude::*; 2 2 use managrove_core::Game; 3 3 4 + mod ui; 5 + 4 6 #[derive(GodotClass)] 5 7 #[class(base=Node)] 6 8 struct ManaGroveNode {
+65
gdext/src/ui/card_state.rs
··· 1 + use godot::{prelude::*, classes::InputEvent}; 2 + use crate::ui::CardUI; 3 + 4 + #[derive(GodotClass)] 5 + #[class(base=Node)] 6 + pub struct CardState { 7 + base: Base<Node>, 8 + #[export] 9 + state: State, 10 + card_ui: Option<Gd<CardUI>> 11 + } 12 + 13 + #[godot_api] 14 + impl INode for CardState { 15 + fn init(base: Base<Node>) -> Self { 16 + Self { 17 + base, 18 + state: State::Base, 19 + card_ui: None 20 + } 21 + } 22 + } 23 + 24 + #[godot_api] 25 + impl CardState { 26 + #[signal] 27 + pub fn transition_requested(from: Gd<CardState>, to: State); 28 + } 29 + 30 + impl CardState { 31 + fn enter(&mut self) { 32 + 33 + } 34 + 35 + fn exit(&mut self) { 36 + 37 + } 38 + 39 + fn on_input(&mut self, _event: Gd<InputEvent>) { 40 + 41 + } 42 + 43 + fn on_gui_input(&mut self, _event: Gd<InputEvent>) { 44 + 45 + } 46 + 47 + fn on_mouse_entered(&mut self) { 48 + 49 + } 50 + 51 + fn on_mouse_exited(&mut self) { 52 + 53 + } 54 + } 55 + 56 + #[derive(Clone, Debug, GodotConvert, Var, Export, Default, Eq, PartialEq, Hash)] 57 + #[godot(via=GString)] 58 + pub enum State { 59 + #[default] 60 + Base, 61 + Clicked, 62 + Dragging, 63 + Aiming, 64 + Released 65 + }
+94
gdext/src/ui/card_state_machine.rs
··· 1 + use std::collections::HashMap; 2 + use godot::{prelude::*, classes::InputEvent}; 3 + use crate::ui::{CardUI, card_state::{CardState, State}}; 4 + 5 + #[derive(GodotClass)] 6 + #[class(base=Node)] 7 + pub struct CardStateMachine { 8 + base: Base<Node>, 9 + #[export] 10 + initial_state: Option<Gd<CardState>>, 11 + current_state: Option<Gd<CardState>>, 12 + states: HashMap<State, Gd<CardState>> 13 + } 14 + 15 + #[godot_api] 16 + impl INode for CardStateMachine { 17 + fn init(base: Base<Node>) -> Self { 18 + Self { 19 + base, 20 + initial_state: None, 21 + current_state: None, 22 + states: HashMap::new() 23 + } 24 + } 25 + } 26 + 27 + #[godot_api] 28 + impl CardStateMachine { 29 + fn init_obj(&mut self, card: Gd<CardUI>) { 30 + for child in self.to_gd().get_children().iter_shared() { 31 + if child.is_class("CardState") { 32 + let mut child_card_state = child.clone().try_cast::<CardState>().unwrap(); 33 + let key = child_card_state.get("state").try_to::<State>().unwrap(); 34 + self.states.insert(key, child.try_cast::<CardState>().unwrap()); 35 + let obj = self.to_gd(); 36 + child_card_state.signals().transition_requested().connect_other(&obj, Self::on_transition_requested); 37 + child_card_state.set("card_ui", &Variant::from(card.clone())); 38 + } 39 + } 40 + 41 + if let Some(initial_state) = &mut self.initial_state { 42 + initial_state.call("enter", &[]); 43 + self.current_state = Some(initial_state.clone()); 44 + } 45 + } 46 + 47 + fn on_input(&mut self, event: Gd<InputEvent>) { 48 + if let Some(current_state) = &mut self.current_state { 49 + current_state.call("on_input", &[Variant::from(event)]); 50 + } 51 + } 52 + 53 + fn on_gui_input(&mut self, event: Gd<InputEvent>) { 54 + if let Some(current_state) = &mut self.current_state { 55 + current_state.call("on_gui_input", &[Variant::from(event)]); 56 + } 57 + } 58 + 59 + fn on_mouse_entered(&mut self) { 60 + if let Some(current_state) = &mut self.current_state { 61 + current_state.call("on_mouse_entered", &[]); 62 + } 63 + } 64 + 65 + fn on_mouse_exited(&mut self) { 66 + if let Some(current_state) = &mut self.current_state { 67 + current_state.call("on_mouse_exited", &[]); 68 + } 69 + } 70 + } 71 + 72 + impl CardStateMachine { 73 + pub fn on_transition_requested(&mut self, from: Gd<CardState>, to: State) { 74 + if let Some(current_state) = &self.current_state { 75 + if from != *current_state { 76 + return; 77 + } 78 + } 79 + 80 + match self.states.get_mut(&to) { 81 + Some(new_state) => { 82 + if let Some(current_state) = &mut self.current_state { 83 + current_state.call("exit", &[]); 84 + } 85 + 86 + new_state.call("enter", &[]); 87 + self.current_state = Some(new_state.clone()); 88 + }, 89 + None => { 90 + return; 91 + } 92 + } 93 + } 94 + }
+34
gdext/src/ui/mod.rs
··· 1 + use godot::{classes::{ColorRect, Control, IControl, Label}, prelude::*}; 2 + 3 + mod card_state; 4 + mod card_state_machine; 5 + 6 + #[derive(GodotClass)] 7 + #[class(base=Control)] 8 + pub struct CardUI { 9 + base: Base<Control>, 10 + color: Option<Gd<ColorRect>>, 11 + state: Option<Gd<Label>> 12 + } 13 + 14 + #[godot_api] 15 + impl IControl for CardUI { 16 + fn init(base: Base<Control>) -> Self { 17 + Self { 18 + base, 19 + color: None, 20 + state: None 21 + } 22 + } 23 + 24 + fn ready(&mut self) { 25 + self.color = Some(self.to_gd().get_node_as("Color")); 26 + self.state = Some(self.to_gd().get_node_as("State")); 27 + } 28 + } 29 + 30 + #[godot_api] 31 + impl CardUI { 32 + #[signal] 33 + fn reparent_requested(which_card_ui: Gd<CardUI>); 34 + }