Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom task logic via extending InteractTask #300

Merged
merged 32 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2ad950e
groundwork for networking + extending InteractTask
TheSecondReal0 Feb 12, 2021
094eb3b
Merge branch 'task-resource-despaghetti' into custom-task-logic
TheSecondReal0 Feb 12, 2021
12e0703
more work to allow custom interacttask scripts
TheSecondReal0 Feb 12, 2021
8ba4b5a
more virtual functions + comments
TheSecondReal0 Feb 12, 2021
343cde8
comment improvement
TheSecondReal0 Feb 12, 2021
b48705f
added transition logic and more comments
TheSecondReal0 Feb 13, 2021
b423739
updated task manager to work with resource transition logic
TheSecondReal0 Feb 13, 2021
ea0a2ee
began porting clockset task
TheSecondReal0 Feb 13, 2021
ea27f56
finished porting clockset task
TheSecondReal0 Feb 13, 2021
a6f6b07
improved networking
TheSecondReal0 Feb 16, 2021
7384d2e
reorganized interacttask.gd
TheSecondReal0 Feb 16, 2021
598a1d9
TaskManager overhaul
TheSecondReal0 Feb 19, 2021
a3957f3
updated clockset task and fleshed out some stuff
TheSecondReal0 Feb 19, 2021
385d531
cleaned up a bit
TheSecondReal0 Feb 19, 2021
4b3c8da
removed complete_task() from UI classes
TheSecondReal0 Feb 19, 2021
fca6bda
removed random numbers from InteractTask
TheSecondReal0 Feb 19, 2021
ee00d85
Revert "removed random numbers from InteractTask"
TheSecondReal0 Feb 19, 2021
9942ddc
removed random numbers from TaskInteract without updating resources
TheSecondReal0 Feb 19, 2021
2be12bd
updated resources to reflect random numbers removal
TheSecondReal0 Feb 19, 2021
3d0b83d
got resources figured out
TheSecondReal0 Feb 19, 2021
8f40ff2
added some comments
TheSecondReal0 Feb 19, 2021
8cbcf94
fixed bugs
TheSecondReal0 Feb 19, 2021
a1e1dfb
actually fixed the desync bug
TheSecondReal0 Feb 19, 2021
922036f
fixed crash that occurred with non-global tasks
TheSecondReal0 Feb 20, 2021
bee22dd
fixed weird bug where task would be completed over and over again
TheSecondReal0 Feb 20, 2021
2a508dc
working on personal task support
TheSecondReal0 Feb 22, 2021
ffe2c9d
general polishing
TheSecondReal0 Feb 22, 2021
66c2605
another check to prevent trying to complete a task twice
TheSecondReal0 Feb 23, 2021
9084397
delete binary
TheSecondReal0 Feb 23, 2021
5eb802c
improved task syncing
TheSecondReal0 Feb 24, 2021
d79c7d8
clockset map text updates more often
TheSecondReal0 Feb 24, 2021
161f71c
possible clockset times now include all intended values
TheSecondReal0 Feb 24, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/addons/opensusinteraction/opensusinteraction.gd
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ func _enter_tree():
#add custom resources
add_custom_type("Interact", "Resource", interact_resource_script, object_icon)
add_custom_type("InteractMap", "Resource", interactmap_resource_script, object_icon)
add_custom_type("InteractTask", "Resource", task_resource_script, object_icon)
#add_custom_type("InteractTask", "Resource", task_resource_script, object_icon)
add_custom_type("InteractUI", "Resource", interactui_resource_script, object_icon)

func _exit_tree():
remove_custom_type("Interact")
remove_custom_type("InteractMap")
remove_custom_type("InteractTask")
#remove_custom_type("InteractTask")
remove_custom_type("InteractUI")
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ resource_local_to_scene = true
resource_name = "InteractTask"
script = ExtResource( 5 )
task_text = ""
random_numbers = 0
task_id = -1
ui_resource = SubResource( 2 )
outputs/toggle_map_interactions = false
outputs/output_map_interactions = [ ]
is_task_global = false

[resource]
resource_local_to_scene = true
Expand Down
140 changes: 128 additions & 12 deletions src/addons/opensusinteraction/resources/interacttask/interacttask.gd
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
tool
extends Resource

#class_name InteractTask
class_name InteractTask

export(String) var task_text

Expand Down Expand Up @@ -31,9 +31,7 @@ var ui_res: Resource = base_ui_resource.duplicate()
#node this task is attached to
var attached_to: Node

#assigned by a programmer when added to the scene
#needs to be unique
export var task_id: int = TaskManager.INVALID_TASK_ID
var task_id: int = TaskManager.INVALID_TASK_ID
var task_data: Dictionary = {}
var task_data_player: Dictionary = {}
var task_registered: bool = false
Expand Down Expand Up @@ -62,9 +60,19 @@ var custom_properties: Dictionary = {
# if you want the editor property name to be the same as the script variable name, you do not need to add it to custom_properties
var custom_properties_to_show: PoolStringArray = ["ui_resource", "outputs/toggle_map_interactions", "outputs/output_map_interactions", "is_task_global"]

func complete_task( player_id: int = TaskManager.GLOBAL_TASK_PLAYER_ID,
data: Dictionary = {}) -> bool:
signal transitioned(old_state, new_state, player_id)

func complete_task( player_id: int = TaskManager.GLOBAL_TASK_PLAYER_ID,
data: Dictionary = {}) -> bool:
# I'm adding a virtual function in case we add some base checks here, so you could
# add custom behavior while retaining the checks
# this is similar behavior to assign_player(), registered(), gen_task_data(), etc.
# if you want fully custom behavior, override this function instead
var virt_return = _complete_task(player_id, data)
# if virt_return is not a bool, it means the extending script either didn't override or
# doesn't want to break out of this function, so we shouldn't cancel the actions below
if virt_return is bool:
return virt_return
var temp_interact_data = task_data_player[player_id]
for key in data.keys():
temp_interact_data[key] = data[key]
Expand All @@ -73,10 +81,23 @@ func complete_task( player_id: int = TaskManager.GLOBAL_TASK_PLAYER_ID,
resource.interact(attached_to, temp_interact_data)
return true

# not defined to return a bool so complete_task() can know if this function was overridden
# or not
# while overriding, return any bool to break out of complete_task(), which will return
# said bool to whatever called complete_task(), most likely the task manager
func _complete_task(player_id: int, data: Dictionary):
pass

func assign_player(player_id: int = TaskManager.GLOBAL_TASK_PLAYER_ID):

if task_data_player.has(player_id):
return
# if nothing is explicitly returned, _assign_player() will return null and will not trigger this
# this allows an extending script to override interact behavior (while retaining the above checks)
# by declaring _assign_player() and returning false. If you want fully custom
# behavior, override this function instead
# used to add custom behavior when a player is assigned to this task
if _assign_player(player_id) == false:
return
task_data_player[player_id] = task_data.duplicate(true)
var task_text = task_data["task_text"]
var data = []
Expand All @@ -87,14 +108,54 @@ func assign_player(player_id: int = TaskManager.GLOBAL_TASK_PLAYER_ID):
#var data: Dictionary = TaskGenerators.call_generator(task_text)
task_data_player[player_id]["task_data"] = data

# overridden to add custom behavior for when a player is assigned to this task while
# retaining the checks implemented in assign_player()
# return false to break out of assign_player() early (before any actions are taken)
func _assign_player(player_id: int = TaskManager.GLOBAL_TASK_PLAYER_ID):
pass

func registered(new_task_id: int, new_task_data: Dictionary):
_registered(new_task_id, new_task_data)
for key in new_task_data.keys():
task_data[key] = new_task_data[key]
task_id = new_task_id
task_registered = true

func _registered(new_task_id: int, new_task_data: Dictionary):
pass

# while this function doesn't add any functionality, it does provide a constant
# function to call, and we could easily add checks later
func sync_task():
_sync_task()

# to be overridden by an extending script
func _sync_task():
pass

func task_rset(property: String, value):
TaskManager.task_rset(property, value, task_id)

func task_rset_id(id: int, property: String, value):
TaskManager.task_rset_id(id, property, value, task_id)

func receive_task_rset(property: String, value):
set(property, value)

# args must be in the form of an array because you can't create functions with variable
# arg amounts in gdscript
func task_rpc(function: String, args: Array):
TaskManager.task_rpc(function, args, task_id)

func task_rpc_id(id: int, function: String, args: Array):
TaskManager.task_rpc_id(id, function, args, task_id)

func receive_task_rpc(function: String, args: Array):
# using callv instead of call because it will translate the array into
# individual arguments
callv(function, args)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couldn't it be done so that task resources can only RPC the server, and can only accept RPCs from the server?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it could be done that way. I set it up like this so each task can fully customize their networking stuff 😃


func get_task_data(player_id: int = Network.get_my_id()) -> Dictionary:

if task_registered and is_task_global():
player_id = TaskManager.GLOBAL_TASK_PLAYER_ID

Expand All @@ -112,8 +173,8 @@ func get_task_data(player_id: int = Network.get_my_id()) -> Dictionary:

# generate initial data to send to the task manager, should not be called after it is registered
func gen_task_data() -> Dictionary:
if task_registered:
return task_data
# if task_registered:
# return task_data
var info: Dictionary = {}
info["task_text"] = task_text
# info["item_inputs"] = item_inputs
Expand All @@ -123,10 +184,19 @@ func gen_task_data() -> Dictionary:
info["resource"] = self
info["is_task_global"] = is_task_global
#info["ui_resource"] = ui_res
# so you can override _gen_task_data() and non-destructively add data
# if you want to remove data, you'd have to override this function
var virt_info: Dictionary = _gen_task_data()
for key in virt_info:
info[key] = virt_info[key]
for key in info.keys():
task_data[key] = info[key]
return info

# meant to be overridden in an extending script to allow adding custom data to task_info
func _gen_task_data() -> Dictionary:
return {}

func get_task_id() -> int:
return task_id

Expand All @@ -136,27 +206,73 @@ func get_task_state(player_id: int = TaskManager.GLOBAL_TASK_PLAYER_ID) -> int:
return TaskManager.task_state.HIDDEN
return task_data_player[player_id]["state"]

func set_task_state(player_id: int, new_state: int) -> bool:
func transition(new_state: int, player_id: int = TaskManager.GLOBAL_TASK_PLAYER_ID) -> bool:
# to add custom behavior/checks before the state is officially changed
# if _transition() returns false, interpret it to mean the extending script
# doesn't want to transition
# to remove behavior after this point, you must override this function (make sure
# to emit the "transitioned" signal)
var virt_return = _transition(new_state, player_id)
if virt_return is bool and virt_return == false:
return false
var old_state = task_data_player[player_id]["state"]
task_data_player[player_id]["state"] = new_state
emit_signal("transitioned", old_state, new_state, player_id)
return true

# override to add custom behavior/checks before the state is officially changed
# this is especially useful if you want to cancel the transition as it would be
# much harder to retroactively undo it
# return false to break out of transition() early (this will also cause transition()
# to return false to whatever called it, most likely task manager or itself)
func _transition(new_state: int, player_id: int):
pass

func is_task_global() -> bool:
return task_data["is_task_global"]

func interact(_from: Node = null, _interact_data: Dictionary = {}):
if attached_to == null and _from != null:
attached_to = _from
if attached_to == null:
push_error("InteractTask resource trying to be used with no defined node")
if not task_data_player.has(Network.get_my_id()) and not task_data_player.has(TaskManager.GLOBAL_TASK_PLAYER_ID):
return
# if nothing is explicitly returned, _interact() will return null and will not trigger this
# this check is so an extending script can override interact behavior (while retaining the
# above checks) by declaring _interact() and returning false. If you want fully custom
# behavior, override this function instead
# this could be used to cancel the interaction or to implement custom behavior,
# like triggering a map interaction instead of opening a UI
if _interact(_from, _interact_data) == false:
return
ui_res.interact(_from, get_task_data())

# meant to be overridden by an extending script to allow custom behavior when interacted with
func _interact(_from: Node = null, _interact_data: Dictionary = {}):
pass

func init_resource(_from: Node):
if attached_to == null and _from != null:
attached_to = _from
if attached_to == null:
push_error("InteractTask resource trying to be initiated with no defined node")
# if nothing is explicitly returned, _init_resource() will return null and will not trigger this
# this check is so an inheriting script can override initiation behavior while retaining the
# above checks. If you want fully custom behavior, override this function instead
# I can't think of any reason you wouldn't want to register with the task manager,
# but the ability to do so couldn't hurt
# calling the virtual function here also allows the extending script to add custom behavior
if _init_resource(_from) == false:
return
TaskManager.register_task(self)

# meant to be overridden by an extending script to allow custom behavior on resource init
func _init_resource(_from: Node):
pass

# not adding a virtual function for this because the same thing is accomplished by
# overriding _gen_interact_data()
func get_interact_data(_from: Node = null) -> Dictionary:
if attached_to == null and _from != null:
attached_to = _from
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ resource_name = "InteractTask"
script = ExtResource( 1 )
task_text = ""
random_numbers = 0
task_id = -1
ui_resource = SubResource( 1 )
outputs/toggle_map_interactions = false
outputs/output_map_interactions = [ ]
Expand Down
Loading