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