1 ////////////////////////////////////////////////////////////////////////////////
2 // Copyright (C) 2004-2011 by The Allacrost Project
3 // Copyright (C) 2012-2016 by Bertram (Valyria Tear)
4 // All Rights Reserved
5 //
6 // This code is licensed under the GNU GPL version 2. It is free software and
7 // you may modify it and/or redistribute it under the terms of this license.
8 // See https://www.gnu.org/copyleft/gpl.html for details.
9 ////////////////////////////////////////////////////////////////////////////////
10
11 #include "item_action.h"
12
13 #include "common/global/objects/global_item.h"
14 #include "engine/system.h"
15
16 #include "utils/ustring.h"
17
18 using namespace vt_global;
19 using namespace vt_script;
20 using namespace vt_system;
21 using namespace vt_utils;
22
23 namespace vt_battle
24 {
25
26 namespace private_battle
27 {
28
ItemAction(BattleActor * source,BattleTarget target,const std::shared_ptr<BattleItem> & item)29 ItemAction::ItemAction(BattleActor* source, BattleTarget target, const std::shared_ptr<BattleItem>& item) :
30 BattleAction(source, target),
31 _battle_item(item),
32 _action_canceled(false)
33 {
34 if(item == nullptr) {
35 PRINT_WARNING << "Item action without valid item!!" << std::endl;
36 return;
37 }
38
39 const GlobalItem& global_item = item->GetGlobalItem();
40
41 if(global_item.GetTargetType() == GLOBAL_TARGET_INVALID)
42 PRINT_WARNING << "constructor received invalid item" << std::endl;
43 if(global_item.GetTargetType() != target.GetType())
44 PRINT_WARNING << "item and target reference different target types" << std::endl;
45 if(global_item.IsUsableInBattle() == false)
46 PRINT_WARNING << "item is not usable in battle" << std::endl;
47
48 // Check for a custom item animation script for the given character
49 _is_scripted = false;
50 std::string animation_script_file = global_item.GetAnimationScript(_actor->GetID());
51
52 if(animation_script_file.empty())
53 return;
54
55 // Clears out old script data
56 std::string tablespace = ScriptEngine::GetTableSpace(animation_script_file);
57 ScriptManager->DropGlobalTable(tablespace);
58
59 if(!_anim_script.OpenFile(animation_script_file)) {
60 _anim_script.CloseFile();
61 return;
62 }
63
64 if(_anim_script.OpenTablespace().empty()) {
65 PRINT_ERROR << "No namespace found in file: " << animation_script_file << std::endl;
66 _anim_script.CloseFile();
67 return;
68 }
69
70 _init_function = _anim_script.ReadFunctionPointer("Initialize");
71
72 if(!_init_function.is_valid()) {
73 _anim_script.CloseFile();
74 return;
75 }
76
77 // Attempt to load a possible update function.
78 _update_function = _anim_script.ReadFunctionPointer("Update");
79 _is_scripted = true;
80 }
81
~ItemAction()82 ItemAction::~ItemAction()
83 {
84 // Remove reference from the init and update function
85 // to permit their deletion when freeing the lua coroutine.
86 _init_function = luabind::object();
87 _update_function = luabind::object();
88
89 _anim_script.CloseFile();
90 }
91
Initialize()92 bool ItemAction::Initialize()
93 {
94 if(IsScripted())
95 _InitAnimationScript();
96 return true;
97 }
98
Update()99 bool ItemAction::Update()
100 {
101 // When there is no update function, the animation is done.
102 if(!_update_function.is_valid())
103 return true;
104
105 try {
106 return luabind::call_function<bool>(_update_function);
107 } catch(const luabind::error& err) {
108 ScriptManager->HandleLuaError(err);
109 // Give the item back in case of error
110 _battle_item->IncrementBattleCount();
111 return true;
112 } catch(const luabind::cast_failed& e) {
113 ScriptManager->HandleCastError(e);
114 // Give the item back in case of error
115 _battle_item->IncrementBattleCount();
116 return true;
117 }
118
119 // Should never happen
120 return true;
121 }
122
Warmup()123 void ItemAction::Warmup()
124 {
125 const GlobalItem& global_item = _battle_item->GetGlobalItem();
126
127 const luabind::object& script_function = global_item.GetBattleWarmupFunction();
128 if(!script_function.is_valid()) {
129 return;
130 }
131
132 try {
133 luabind::call_function<void>(script_function, _actor, _target);
134 } catch(const luabind::error &err) {
135 ScriptManager->HandleLuaError(err);
136 } catch(const luabind::cast_failed &e) {
137 ScriptManager->HandleCastError(e);
138 }
139 }
140
Execute()141 bool ItemAction::Execute()
142 {
143 if(_battle_item == nullptr) {
144 PRINT_WARNING << "Item action Execute() without valid item!!" << std::endl;
145 return false;
146 }
147
148 const GlobalItem& global_item = _battle_item->GetGlobalItem();
149
150 // Note that the battle item is already removed from the item list at that step.
151 const luabind::object& script_function = global_item.GetBattleUseFunction();
152 if(!script_function.is_valid()) {
153 PRINT_WARNING << "item did not have a battle use function" << std::endl;
154
155 Cancel();
156 return false;
157 }
158
159 bool ret = false;
160 try {
161 ret = luabind::call_function<bool>(script_function, _actor, _target);
162 } catch(const luabind::error &err) {
163 ScriptManager->HandleLuaError(err);
164 ret = false;
165 } catch(const luabind::cast_failed &e) {
166 ScriptManager->HandleCastError(e);
167 ret = false;
168 }
169
170 // Cancel item action when failed.
171 if(!ret)
172 Cancel();
173
174 return ret;
175 }
176
Cancel()177 void ItemAction::Cancel()
178 {
179 if(_action_canceled)
180 return;
181
182 // Give the item back in that case
183 if (_battle_item)
184 _battle_item->IncrementBattleCount();
185
186 // Permit to cancel only once.
187 _action_canceled = true;
188 }
189
GetName() const190 ustring ItemAction::GetName() const
191 {
192 if(_battle_item)
193 return _battle_item->GetGlobalItem().GetName();
194 return UTranslate("[error]");
195 }
196
GetIconFilename() const197 std::string ItemAction::GetIconFilename() const
198 {
199 if(_battle_item)
200 return _battle_item->GetGlobalItem().GetIconImage().GetFilename();
201 return std::string();
202 }
203
GetWarmUpTime() const204 uint32_t ItemAction::GetWarmUpTime() const
205 {
206 if(_battle_item == nullptr)
207 return 0;
208 else
209 return _battle_item->GetWarmUpTime();
210 }
211
GetCoolDownTime() const212 uint32_t ItemAction::GetCoolDownTime() const
213 {
214 if(_battle_item == nullptr)
215 return 0;
216 else
217 return _battle_item->GetCoolDownTime();
218 }
219
_InitAnimationScript()220 void ItemAction::_InitAnimationScript()
221 {
222 try {
223 // N.B: _battle_item is a shared_ptr, but we need the actual pointer for luabind.
224 luabind::call_function<void>(_init_function, _actor, _target, _battle_item.get());
225 } catch(const luabind::error& err) {
226 ScriptManager->HandleLuaError(err);
227 // Fall back to hard-coded mode
228 _is_scripted = false;
229 } catch(const luabind::cast_failed& e) {
230 ScriptManager->HandleCastError(e);
231 // Fall back to hard-coded mode
232 _is_scripted = false;
233 }
234 }
235
236 } // namespace private_battle
237
238 } // namespace vt_battle
239