1/**************************************************************************/
2/* skeleton_modification_stack_2d.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "skeleton_modification_stack_2d.h"
32#include "scene/2d/skeleton_2d.h"
33
34void SkeletonModificationStack2D::_get_property_list(List<PropertyInfo> *p_list) const {
35 for (int i = 0; i < modifications.size(); i++) {
36 p_list->push_back(
37 PropertyInfo(Variant::OBJECT, "modifications/" + itos(i),
38 PROPERTY_HINT_RESOURCE_TYPE,
39 "SkeletonModification2D",
40 PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_ALWAYS_DUPLICATE));
41 }
42}
43
44bool SkeletonModificationStack2D::_set(const StringName &p_path, const Variant &p_value) {
45 String path = p_path;
46
47 if (path.begins_with("modifications/")) {
48 int mod_idx = path.get_slicec('/', 1).to_int();
49 set_modification(mod_idx, p_value);
50 return true;
51 }
52 return true;
53}
54
55bool SkeletonModificationStack2D::_get(const StringName &p_path, Variant &r_ret) const {
56 String path = p_path;
57
58 if (path.begins_with("modifications/")) {
59 int mod_idx = path.get_slicec('/', 1).to_int();
60 r_ret = get_modification(mod_idx);
61 return true;
62 }
63 return true;
64}
65
66void SkeletonModificationStack2D::setup() {
67 if (is_setup) {
68 return;
69 }
70
71 if (skeleton != nullptr) {
72 is_setup = true;
73 for (int i = 0; i < modifications.size(); i++) {
74 if (!modifications[i].is_valid()) {
75 continue;
76 }
77 modifications.get(i)->_setup_modification(this);
78 }
79
80#ifdef TOOLS_ENABLED
81 set_editor_gizmos_dirty(true);
82#endif // TOOLS_ENABLED
83
84 } else {
85 WARN_PRINT("Cannot setup SkeletonModificationStack2D: no Skeleton2D set!");
86 }
87}
88
89void SkeletonModificationStack2D::execute(float p_delta, int p_execution_mode) {
90 ERR_FAIL_COND_MSG(!is_setup || skeleton == nullptr || is_queued_for_deletion(),
91 "Modification stack is not properly setup and therefore cannot execute!");
92
93 if (!skeleton->is_inside_tree()) {
94 ERR_PRINT_ONCE("Skeleton is not inside SceneTree! Cannot execute modification!");
95 return;
96 }
97
98 if (!enabled) {
99 return;
100 }
101
102 for (int i = 0; i < modifications.size(); i++) {
103 if (!modifications[i].is_valid()) {
104 continue;
105 }
106
107 if (modifications[i]->get_execution_mode() == p_execution_mode) {
108 modifications.get(i)->_execute(p_delta);
109 }
110 }
111}
112
113void SkeletonModificationStack2D::draw_editor_gizmos() {
114 if (!is_setup) {
115 return;
116 }
117
118 if (editor_gizmo_dirty) {
119 for (int i = 0; i < modifications.size(); i++) {
120 if (!modifications[i].is_valid()) {
121 continue;
122 }
123
124 if (modifications[i]->editor_draw_gizmo) {
125 modifications.get(i)->_draw_editor_gizmo();
126 }
127 }
128 skeleton->draw_set_transform(Vector2(0, 0));
129 editor_gizmo_dirty = false;
130 }
131}
132
133void SkeletonModificationStack2D::set_editor_gizmos_dirty(bool p_dirty) {
134 if (!is_setup) {
135 return;
136 }
137
138 if (!editor_gizmo_dirty && p_dirty) {
139 editor_gizmo_dirty = p_dirty;
140 if (skeleton) {
141 skeleton->queue_redraw();
142 }
143 } else {
144 editor_gizmo_dirty = p_dirty;
145 }
146}
147
148void SkeletonModificationStack2D::enable_all_modifications(bool p_enabled) {
149 for (int i = 0; i < modifications.size(); i++) {
150 if (!modifications[i].is_valid()) {
151 continue;
152 }
153 modifications.get(i)->set_enabled(p_enabled);
154 }
155}
156
157Ref<SkeletonModification2D> SkeletonModificationStack2D::get_modification(int p_mod_idx) const {
158 ERR_FAIL_INDEX_V(p_mod_idx, modifications.size(), nullptr);
159 return modifications[p_mod_idx];
160}
161
162void SkeletonModificationStack2D::add_modification(Ref<SkeletonModification2D> p_mod) {
163 ERR_FAIL_COND(!p_mod.is_valid());
164
165 p_mod->_setup_modification(this);
166 modifications.push_back(p_mod);
167
168#ifdef TOOLS_ENABLED
169 set_editor_gizmos_dirty(true);
170#endif // TOOLS_ENABLED
171}
172
173void SkeletonModificationStack2D::delete_modification(int p_mod_idx) {
174 ERR_FAIL_INDEX(p_mod_idx, modifications.size());
175 modifications.remove_at(p_mod_idx);
176
177#ifdef TOOLS_ENABLED
178 set_editor_gizmos_dirty(true);
179#endif // TOOLS_ENABLED
180}
181
182void SkeletonModificationStack2D::set_modification(int p_mod_idx, Ref<SkeletonModification2D> p_mod) {
183 ERR_FAIL_INDEX(p_mod_idx, modifications.size());
184
185 if (p_mod.is_null()) {
186 modifications.write[p_mod_idx] = Ref<SkeletonModification2D>();
187 } else {
188 modifications.write[p_mod_idx] = p_mod;
189 p_mod->_setup_modification(this);
190 }
191
192#ifdef TOOLS_ENABLED
193 set_editor_gizmos_dirty(true);
194#endif // TOOLS_ENABLED
195}
196
197void SkeletonModificationStack2D::set_modification_count(int p_count) {
198 ERR_FAIL_COND_MSG(p_count < 0, "Modification count cannot be less than zero.");
199 modifications.resize(p_count);
200 notify_property_list_changed();
201
202#ifdef TOOLS_ENABLED
203 set_editor_gizmos_dirty(true);
204#endif // TOOLS_ENABLED
205}
206
207int SkeletonModificationStack2D::get_modification_count() const {
208 return modifications.size();
209}
210
211void SkeletonModificationStack2D::set_skeleton(Skeleton2D *p_skeleton) {
212 skeleton = p_skeleton;
213}
214
215Skeleton2D *SkeletonModificationStack2D::get_skeleton() const {
216 return skeleton;
217}
218
219bool SkeletonModificationStack2D::get_is_setup() const {
220 return is_setup;
221}
222
223void SkeletonModificationStack2D::set_enabled(bool p_enabled) {
224 enabled = p_enabled;
225}
226
227bool SkeletonModificationStack2D::get_enabled() const {
228 return enabled;
229}
230
231void SkeletonModificationStack2D::set_strength(float p_strength) {
232 ERR_FAIL_COND_MSG(p_strength < 0, "Strength cannot be less than zero!");
233 ERR_FAIL_COND_MSG(p_strength > 1, "Strength cannot be more than one!");
234 strength = p_strength;
235}
236
237float SkeletonModificationStack2D::get_strength() const {
238 return strength;
239}
240
241void SkeletonModificationStack2D::_bind_methods() {
242 ClassDB::bind_method(D_METHOD("setup"), &SkeletonModificationStack2D::setup);
243 ClassDB::bind_method(D_METHOD("execute", "delta", "execution_mode"), &SkeletonModificationStack2D::execute);
244
245 ClassDB::bind_method(D_METHOD("enable_all_modifications", "enabled"), &SkeletonModificationStack2D::enable_all_modifications);
246 ClassDB::bind_method(D_METHOD("get_modification", "mod_idx"), &SkeletonModificationStack2D::get_modification);
247 ClassDB::bind_method(D_METHOD("add_modification", "modification"), &SkeletonModificationStack2D::add_modification);
248 ClassDB::bind_method(D_METHOD("delete_modification", "mod_idx"), &SkeletonModificationStack2D::delete_modification);
249 ClassDB::bind_method(D_METHOD("set_modification", "mod_idx", "modification"), &SkeletonModificationStack2D::set_modification);
250
251 ClassDB::bind_method(D_METHOD("set_modification_count", "count"), &SkeletonModificationStack2D::set_modification_count);
252 ClassDB::bind_method(D_METHOD("get_modification_count"), &SkeletonModificationStack2D::get_modification_count);
253
254 ClassDB::bind_method(D_METHOD("get_is_setup"), &SkeletonModificationStack2D::get_is_setup);
255
256 ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &SkeletonModificationStack2D::set_enabled);
257 ClassDB::bind_method(D_METHOD("get_enabled"), &SkeletonModificationStack2D::get_enabled);
258
259 ClassDB::bind_method(D_METHOD("set_strength", "strength"), &SkeletonModificationStack2D::set_strength);
260 ClassDB::bind_method(D_METHOD("get_strength"), &SkeletonModificationStack2D::get_strength);
261
262 ClassDB::bind_method(D_METHOD("get_skeleton"), &SkeletonModificationStack2D::get_skeleton);
263
264 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "get_enabled");
265 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "0, 1, 0.001"), "set_strength", "get_strength");
266 ADD_PROPERTY(PropertyInfo(Variant::INT, "modification_count", PROPERTY_HINT_RANGE, "0, 100, 1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Modifications,modifications/"), "set_modification_count", "get_modification_count");
267}
268
269SkeletonModificationStack2D::SkeletonModificationStack2D() {
270}
271