Skip to content

Commit cfffa04

Browse files
committed
ScriptingEngine extended to allow non-Card objects to integrate with it
1 parent c3e7972 commit cfffa04

30 files changed

+219
-132
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ New **mandatory** CardConfig property added: `EXPLANATIONS` (Dictionary). It sto
1616

1717
New CFConst poperty added: `VIEWPORT_FOCUS_ZOOM_TYPE`. Set to either "resize" or "scale" (default = "resize"). If set to scale, will magnify the card during viewport focus using godot scaling. If set to resize, will resize the card's viewport dupe's dimentions. This prevent blurry text, but needs more setup in the card's front script.
1818

19+
Game has been adjusted to allow the ScriptingEngine to work non-Card classes. To enable this the following changes have been made which might affect your game if you've extended the Card or ScriptingEngine classes
20+
21+
* Card `card_name` property has been renamed to `canonical_name`
22+
* Card `retrieve_card_scripts` function has been renamed to `retrieve_scripts`
23+
* ScriptingEngine and ScriptObject `owner_card` variable has been renamed to `owner` and is not expected that it be a Card class
24+
1925
### Bugs
2026

2127
* Fixed crash when window x-axis resized to minimum
@@ -28,6 +34,7 @@ New CFConst poperty added: `VIEWPORT_FOCUS_ZOOM_TYPE`. Set to either "resize" or
2834
* Deckbuilder will now display the total or filtered count of cards shown
2935
* Added new class: GameStats, which can be used to submit stats to your own instance of [CGF-Stats](https://github.com/db0/CGF-Stats)
3036
* Can now show extra info below card focus viewport, such as keyword explanations or illustrator info
37+
* Can now extend deckbuilder DBListCardObject and
3138

3239
#### ScriptingEngine
3340

INSTALL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ While everything in there can be modified, some files need to exist in one form
2424
* You **need** CustomScripts.gd, even if it custom_script() function does not match anything.
2525
* You **need** CFConst, but you should customize its constants to fit your needs. It is in custom so that you do not lose your changes during an upgrade.
2626
* You **need** at least 1 Card Back, 1 Card Front and 1 CardManipulationButton scene, to link to your card templates.
27+
* You **need** at least 1 Info Panel Scene. You can re-use CGFInfoPanel.tcsn
2728

2829
All other files, especially those starting with "CGF" can be deleted, or you can keep them around for reference.
2930

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ Please see the [Install Guide](INSTALL.md)
124124

125125
One of the most powerful features of this framework, is the possibility to easily script each individual card's abilities from start to finish, so that a player only needs to double-click on a card and your code will handle the proper execution. This allows games to very easily create complete rules enforcement of all card abilities, from the simplest to the most tricky ones.
126126

127-
Please see the [ScriptDefinitions](https://github.com/db0/godot-card-gaming/wiki/ScriptDefinitions) documentation for more details
127+
Please see the [ScriptingEngine](SCRIPTING_ENGINE] and [ScriptDefinitions](https://github.com/db0/godot-card-gaming/wiki/ScriptDefinitions) documentation for more details.
128128

129129
The game comes with some sample scripted cards which can be found under `res://src/custom/cards/sets`.
130130

SCRIPTING_ENGINE.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Godot Card Game Scripting Engine
2+
3+
The ScriptingEngine (ScEng) of the Card Game Framework is a way to allow complex interactions and manipulations of the board to be encoded in objects in the game through a simple json dictionary.
4+
5+
Please check the [basic information in the wiki](https://github.com/db0/godot-card-gaming/wiki/ScriptDefinitions) first.
6+
7+
## Connecting new objects to the ScriptingEngine
8+
9+
The ScriptingEngine has been created in such a way so as to allow even non-card objects to make use of its capabilities.
10+
11+
To achieve that, you need to prepare you object in the following way:
12+
13+
1. Add the object to the "scriptables" group. Objects in this group are checked whenever looking for effects that modify automatic intereactions with the board
14+
1. Add a "canonical_name" variable to your object's script and set it so that it has a human-readable value which refers to that object. This is used in dialogues to the player and for filtering effects
15+
1. Add an `execute_scripts()` method to the object. This will be called automatically due to signals and should be called with manual intereactions. It should behave similar to the `execute_scripts()`
16+
in the Card object, checking filters, checking confirmations and finally invoking the ScriptingEngine with a list of tasks. The best approach would be to copy `execute_scripts()` and modify it.
17+
1. Add a `get_state_exec()` method to the object. This should return a string of one of the various states the object can be in, which are used to filter the correct scripts for execution.
18+
1. Add a `retrieve_scripts()` method to the object. This should retrieve all of the object's scripts, from wherever they are stored.

src/core/AlterantEngine.gd

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ func _ready() -> void:
2929
# Sets the owner of this Scripting Engine
3030
func _init(
3131
trigger_card: Card,
32-
alterant_card: Card,
32+
alterant_object,
3333
scripts_queue: Array,
3434
task_details: Dictionary) -> void:
3535
calculate_next_alteration(
3636
trigger_card,
37-
alterant_card,
37+
alterant_object,
3838
scripts_queue.duplicate(),
3939
task_details)
4040

@@ -44,7 +44,7 @@ func _init(
4444
# against the relevant filters and per_ requests.
4545
func calculate_next_alteration(
4646
trigger_card: Card,
47-
alterant_card: Card,
47+
alterant_object,
4848
scripts_queue: Array,
4949
task_details: Dictionary) -> void:
5050
if scripts_queue.empty():
@@ -54,7 +54,7 @@ func calculate_next_alteration(
5454
var script := ScriptAlter.new(
5555
scripts_queue.pop_front(),
5656
trigger_card,
57-
alterant_card,
57+
alterant_object,
5858
task_details)
5959
if not script.is_primed:
6060
yield(script,"primed")
@@ -64,7 +64,7 @@ func calculate_next_alteration(
6464
# with one less item in our scripts_queue.
6565
calculate_next_alteration(
6666
trigger_card,
67-
alterant_card,
67+
alterant_object,
6868
scripts_queue,
6969
task_details)
7070

@@ -79,7 +79,7 @@ func calculate_alteration(script: ScriptAlter) -> void:
7979
elif SP.VALUE_PER in str(alteration_requested):
8080
var per_msg = perMessage.new(
8181
script.get_property(SP.KEY_ALTERATION),
82-
script.owner_card,
82+
script.owner,
8383
script.get_property(script.get_property(SP.KEY_ALTERATION)),
8484
script.trigger_card)
8585
alteration = per_msg.found_things

src/core/CFControl.gd

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ func instance_card(card_name: String) -> Card:
176176
+ card_definitions[card_name][CardConfig.SCENE_PROPERTY] + ".tscn")
177177
var card = template.instance()
178178
# We set the card_name variable so that it's able to be used later
179-
card.card_name = card_name
179+
card.canonical_name = card_name
180180
return(card)
181181

182182

@@ -308,5 +308,10 @@ class SignalPropagator:
308308
# It also fails to execute if I use any other flag than GROUP_CALL_UNIQUE
309309
for card in cfc.get_tree().get_nodes_in_group("cards"):
310310
card.execute_scripts(trigger_card,trigger,details)
311+
# If we need other objects than cards to trigger scripts via signals
312+
# add them to the 'scriptables' group ang ensure they have
313+
# an "execute_scripts" function
314+
for card in cfc.get_tree().get_nodes_in_group("scriptables"):
315+
card.execute_scripts(trigger_card,trigger,details)
311316
# cfc.get_tree().call_group_flags(SceneTree.GROUP_CALL_UNIQUE ,"cards",
312317
# "execute_scripts",trigger_card,trigger,details)

src/core/CFScriptUtils.gd

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ extends Reference
1515
# of each alterant, and each value is the modification done by that
1616
# specific alterant.
1717
static func get_altered_value(
18-
_owner_card,
18+
_owner,
1919
task_name: String,
2020
task_properties: Dictionary,
2121
value: int) -> Dictionary:
2222
# The key for the alterant cache is the hash of the
2323
# values passed to this function.
2424
# This is the only way to compare dicts.
2525
var alterant_cache_key = {
26-
"_owner_card": _owner_card,
26+
"_owner_card": _owner,
2727
"task_name": task_name,
2828
"task_properties": task_properties,
2929
"value": value
@@ -36,17 +36,20 @@ static func get_altered_value(
3636
else:
3737
var value_alteration := 0
3838
var alterants_details := {}
39-
for card in cfc.get_tree().get_nodes_in_group("cards"):
40-
var card_scripts = card.retrieve_card_scripts(SP.KEY_ALTERANTS)
39+
var scriptables_array : Array =\
40+
cfc.get_tree().get_nodes_in_group("cards")\
41+
+ cfc.get_tree().get_nodes_in_group("scriptables")
42+
for obj in scriptables_array:
43+
var scripts = obj.retrieve_scripts(SP.KEY_ALTERANTS)
4144
# We select which scripts to run from the card, based on it state
42-
var state_scripts = card_scripts.get(card.get_state_exec(), [])
45+
var state_scripts = scripts.get(obj.get_state_exec(), [])
4346
# To avoid unnecessary operations
4447
# we evoke the AlterantEngine only if we have something to execute
4548
var task_details = _generate_trigger_details(task_name, task_properties)
4649
if len(state_scripts):
4750
var alteng = cfc.alterant_engine.new(
48-
_owner_card,
49-
card,
51+
_owner,
52+
obj,
5053
state_scripts,
5154
task_details)
5255
if not alteng.all_alterations_completed:
@@ -57,7 +60,7 @@ static func get_altered_value(
5760
# Each key in the alterants_details dictionary is the
5861
# card object of the alterant. The value is the alteration
5962
# this alterant has added to the total.
60-
alterants_details[card] = alteng.alteration
63+
alterants_details[obj] = alteng.alteration
6164
# As a general rule, we don't want a positive number to turn
6265
# negative (and the other way around) due to an alteration
6366
# For example: if a card says, "decrease all costs by 3",

src/core/CardTemplate.gd

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export var mandatory_grid_name : String
123123
# If not set, will be set to the value of the Name label in the front.
124124
# if that is also not set, will be set.
125125
# to the human-readable value of the "name" node property.
126-
export var card_name : String setget set_card_name, get_card_name
126+
var canonical_name : String setget set_card_name, get_card_name
127127
# Contains the scene which has the Card Back design to use for this card type
128128
# It needs to be scene which uses a CardBack class script.
129129
export(PackedScene) var card_back_design : PackedScene
@@ -190,7 +190,7 @@ var _placement_slot : BoardPlacementSlot = null
190190
#
191191
# Each key is a ScriptingEngine reference, and each value is a dictionary
192192
# With the following keys:
193-
# * requesting_card: The card object which has requested this temp modifier
193+
# * requesting_object: The object which has requested this temp modifier
194194
# * modifer: A dictionary with all the modifications requested by this card
195195
# Each key is a (numerical) property name,
196196
# and value is the temp modifier requested for this property.
@@ -236,7 +236,7 @@ onready var highlight = $Control/Highlight
236236
func _ready() -> void:
237237
set_card_size(card_size)
238238
_init_card_layout()
239-
# The below call ensures out card_name variable is set.
239+
# The below call ensures out canonical_name variable is set.
240240
# Normally the setup() function should be used to set it,
241241
# but setting the name here as well ensures that a card can be also put on
242242
# The board without calling setup() and then use its hardcoded labels
@@ -267,9 +267,9 @@ func _init_card_layout() -> void:
267267

268268

269269
# Ensures that the canonical card name is set in all fields which use it.
270-
# var card_name, "Name" label and self.name should use the same string.
270+
# var canonical_name, "Name" label and self.name should use the same string.
271271
func _init_card_name() -> void:
272-
if not card_name:
272+
if not canonical_name:
273273
# If the variable has not been set on start
274274
# But the Name label has been set, we set our name to that instead
275275
if card_front.card_labels["Name"].text != "":
@@ -286,7 +286,7 @@ func _init_card_name() -> void:
286286
else:
287287
# If the variable has been set, we ensure label and node name
288288
# are matching
289-
set_card_name(card_name)
289+
set_card_name(canonical_name)
290290

291291

292292
# Called every frame. 'delta' is the elapsed time since the previous frame.
@@ -469,12 +469,12 @@ func _on_Card_mouse_exited() -> void:
469469
# This function handles filling up the card's labels according to its
470470
# card definition dictionary entry.
471471
func setup() -> void:
472-
# card_name needs to be setup before we call this function
473-
set_card_name(card_name)
472+
# canonical_name needs to be setup before we call this function
473+
set_card_name(canonical_name)
474474
# The properties of the card should be already stored in cfc
475475
var read_properties: Dictionary
476476
if state != CardState.VIEWPORT_FOCUS:
477-
read_properties = cfc.card_definitions.get(card_name, {})
477+
read_properties = cfc.card_definitions.get(canonical_name, {})
478478
else:
479479
read_properties = properties.duplicate()
480480
for property in read_properties.keys():
@@ -644,7 +644,7 @@ func get_property_and_alterants(property: String,
644644
# Each value in the modifier_details dictionary is another dictionary
645645
# Where the key is the card object which has added this modifier
646646
# And the value is the modifier this specific card added to the total
647-
temp_modifiers.modifier_details[modifiers_dict.requesting_card] =\
647+
temp_modifiers.modifier_details[modifiers_dict.requesting_object] =\
648648
modifiers_dict.modifier.get(property,0)
649649
property_value += temp_modifiers.value_modification
650650
# We use this flag to avoid an alteranty which alters get_property
@@ -830,34 +830,34 @@ func get_is_viewed() -> bool:
830830
return is_viewed
831831

832832

833-
# Setter for card_name
833+
# Setter for canonical_name
834834
# Also changes the card label and the node name
835835
func set_card_name(value : String) -> void:
836836
# if the card_front.card_labels variable is not set it means ready() has not
837837
# run yet, so we just store the card name for later.
838838
if not card_front:
839-
card_name = value
839+
canonical_name = value
840840
else:
841841
# We set all areas of the card to match the canonical name.
842842
var name_label = card_front.card_labels["Name"]
843843
card_front.set_label_text(name_label,value)
844844
name = value
845-
card_name = value
845+
canonical_name = value
846846
properties["Name"] = value
847847

848848

849-
# Getter for card_name
849+
# Getter for canonical_name
850850
func get_card_name() -> String:
851-
return card_name
851+
return canonical_name
852852

853853

854-
# Overwrites the built-in set name, so that it also sets card_name
854+
# Overwrites the built-in set name, so that it also sets canonical_name
855855
#
856-
# It's preferrable to set card_name instead.
856+
# It's preferrable to set canonical_name instead.
857857
func set_name(value : String) -> void:
858858
.set_name(value)
859859
card_front.card_labels["Name"].text = value
860-
card_name = value
860+
canonical_name = value
861861

862862
# Setter for card_rotation.
863863
#
@@ -1235,7 +1235,7 @@ func execute_scripts(
12351235
if cfc.game_paused:
12361236
return
12371237
common_pre_execution_scripts(trigger)
1238-
var card_scripts = retrieve_card_scripts(trigger)
1238+
var card_scripts = retrieve_scripts(trigger)
12391239
# I use this spot to add a breakpoint when testing script behaviour
12401240
# especially on filters
12411241
if _debugger_hook:
@@ -1259,7 +1259,7 @@ func execute_scripts(
12591259
# Then you'd include an "is_optional_board" key at the same level as "board"
12601260
var confirm_return = CFUtils.confirm(
12611261
card_scripts,
1262-
card_name,
1262+
canonical_name,
12631263
trigger,
12641264
state_exec)
12651265
if confirm_return is GDScriptFunctionState: # Still working.
@@ -1273,7 +1273,7 @@ func execute_scripts(
12731273
# it means it's a multiple choice between two scripts
12741274
if typeof(state_scripts) == TYPE_DICTIONARY:
12751275
var choices_menu = _CARD_CHOICES_SCENE.instance()
1276-
choices_menu.prep(self.card_name,state_scripts)
1276+
choices_menu.prep(canonical_name,state_scripts)
12771277
# We have to wait until the player has finished selecting an option
12781278
yield(choices_menu,"id_pressed")
12791279
# If the player just closed the pop-up without choosing
@@ -1330,7 +1330,7 @@ func execute_scripts(
13301330
#
13311331
# Returns a dictionary of card scripts for this specific card
13321332
# based on the current trigger.
1333-
func retrieve_card_scripts(trigger: String) -> Dictionary:
1333+
func retrieve_scripts(trigger: String) -> Dictionary:
13341334
var found_scripts: Dictionary
13351335
# If scripts have been defined directly in this Card object
13361336
# They take precedence over CardScriptDefinitions.gd
@@ -1343,7 +1343,7 @@ func retrieve_card_scripts(trigger: String) -> Dictionary:
13431343
# This retrieves all the script from the card, stored in cfc
13441344
# The seeks in them the specific trigger we're using in this
13451345
# execution
1346-
found_scripts = cfc.set_scripts.get(card_name,{}).get(trigger,{}).duplicate()
1346+
found_scripts = cfc.set_scripts.get(canonical_name,{}).get(trigger,{}).duplicate()
13471347
return(found_scripts)
13481348

13491349

@@ -1683,7 +1683,7 @@ func common_post_execution_scripts(trigger: String) -> void:
16831683
# scripts on the card during an attempt to drag it from hand.
16841684
func _has_targeting_cost_hand_script() -> bool:
16851685
var ret := false
1686-
var hand_scripts = retrieve_card_scripts("manual").get("hand",[])
1686+
var hand_scripts = retrieve_scripts("manual").get("hand",[])
16871687
# We don't check multiple choice cards
16881688
if hand_drag_starts_targeting and typeof(hand_scripts) == TYPE_ARRAY:
16891689
for task in hand_scripts:

0 commit comments

Comments
 (0)