1-- The initialize() function is called once, followed by calls to the update function.
2-- When the update function returns true, the attack is finished.
3
4-- Set the namespace
5local ns = {};
6setmetatable(ns, {__index = _G});
7kalya_throw_stone = ns;
8setfenv(1, ns);
9
10-- The current battle mode
11local Battle = nil
12
13-- local references
14local character = nil
15local target = nil
16local target_actor = nil
17local skill = nil
18
19local stone = nil
20local stone_shadow = nil
21local splash_image = nil
22
23local stone_pos_x = 0.0;
24local stone_pos_y = 0.0;
25
26local stone_height = 0.0;
27local total_distance = 0.0;
28local height_diff = 0.0;
29local height_min = 0.0;
30
31local a_coeff = 0.0;
32local distance_moved_x = 0.0;
33local distance_moved_y = 0.0;
34
35local enemy_pos_x = 0.0;
36local enemy_pos_y = 0.0;
37
38local attack_step = 0;
39local attack_time = 0.0;
40
41local damage_triggered = false;
42
43-- character, the BattleActor attacking (here Kalya)
44-- target, the BattleEnemy target
45-- The skill id used on target
46function Initialize(_character, _target, _skill)
47    -- Keep the reference in memory
48    character = _character;
49    target = _target;
50    target_actor = _target:GetActor();
51    skill = _skill;
52
53    -- Don't attack if the character isn't alive
54    if (character:IsAlive() == false) then
55        return;
56    end
57
58    -- Set the stone flying height members
59    stone_height = (character:GetSpriteHeight() / 2.0) + 5.0;
60    total_distance = math.abs(target_actor:GetXLocation() - character:GetXLocation());
61    height_diff = stone_height - (target_actor:GetSpriteHeight() / 2.0);
62    height_min = math.min(stone_height, (target_actor:GetSpriteHeight() / 2.0));
63
64    -- Set the stone starting position
65    stone_pos_x = character:GetXLocation() + character:GetSpriteWidth() / 2.0;
66    stone_pos_y = character:GetYLocation() - stone_height;
67
68    -- Make the stone reach the enemy center
69    enemy_pos_x = target_actor:GetXLocation();
70    enemy_pos_y = target_actor:GetYLocation() - target_actor:GetSpriteHeight() / 2.0;
71
72    attack_step = 0;
73    attack_time = 0;
74
75    damage_triggered = false;
76
77    distance_moved_x = SystemManager:GetUpdateTime() / vt_map.MapMode.NORMAL_SPEED * 210.0;
78    local x_diff = enemy_pos_x - stone_pos_x;
79    local y_diff = stone_pos_y - enemy_pos_y;
80    if (y_diff == 0.0) then
81        a_coeff = 0.0;
82        distance_moved_y = 0.0;
83    elseif (x_diff == 0.0) then
84        a_coeff = 0.0;
85        distance_moved_y = distance_moved_x;
86        distance_moved_x = 0.0;
87    else
88        a_coeff = y_diff / x_diff;
89        if (a_coeff < 0) then a_coeff = -a_coeff; end
90        distance_moved_y = a_coeff * distance_moved_x;
91    end
92
93    --print("distance x: ", enemy_pos_x - character_pos_x - 64.0)
94    --print("distance y: ", character_pos_y - enemy_pos_y)
95    --print (distance_moved_x, a_coeff, distance_moved_y);
96
97    Battle = ModeManager:GetTop();
98    -- The stone and shadow battle animations.
99    local ammo_filename = "data/entities/battle/ammo/rock_ammo.lua";
100    stone = Battle:CreateBattleAnimation(ammo_filename);
101    stone_shadow = Battle:CreateBattleAnimation(ammo_filename);
102    stone_shadow:GetAnimatedImage():SetGrayscale(true);
103    stone:SetVisible(false);
104    stone_shadow:SetVisible(false);
105
106    splash_image = Battle:CreateBattleAnimation("data/entities/battle/effects/hit_splash.lua");
107    splash_image:SetVisible(false);
108    splash_width = splash_image:GetAnimatedImage():GetWidth()
109    splash_height = splash_image:GetAnimatedImage():GetHeight()
110end
111
112
113function Update()
114    -- The update time can vary, so update the distance on each update as well.
115    distance_moved_x = SystemManager:GetUpdateTime() / vt_map.MapMode.NORMAL_SPEED * 210.0;
116    if (a_coeff ~= 0.0) then
117        distance_moved_y = a_coeff * distance_moved_x;
118    end
119
120    -- Make the speed the same whatever the angle between the character and the enemy is.
121    -- We deal only with a coefficients > 1.0 for simplification purpose.
122    if (a_coeff > 1.0 and distance_moved_x ~= 0.0 and distance_moved_y ~= 0.0) then
123        distance_moved_x = distance_moved_x * (distance_moved_x / distance_moved_y);
124        if (a_coeff ~= 0.0) then
125            distance_moved_y = a_coeff * distance_moved_x;
126        end
127        --print ("new_ratio: ", a_coeff, distance_moved_x / distance_moved_y)
128    end
129
130    -- Update the stone flying height according to the distance
131    -- Get the % of of x distance left
132    local distance_left = math.abs((stone_pos_x + distance_moved_x) - enemy_pos_x);
133
134    if (total_distance > 0.0) then
135        if (height_diff > 0.0) then
136            stone_height = height_min + ((distance_left / total_distance) * height_diff);
137        else
138            stone_height = height_min + (((total_distance - distance_left) / total_distance) * -height_diff);
139        end
140    end
141
142    -- Attack the enemy
143    if (attack_step == 0) then
144        character:ChangeSpriteAnimation("throw_stone")
145        attack_step = 1
146    end
147    -- Make the character go back to idle once attacked
148    if (attack_step == 1) then
149        attack_time = attack_time + SystemManager:GetUpdateTime();
150        if (attack_time > 700.0) then
151            attack_step = 2;
152            AudioManager:PlaySound("data/sounds/throw.wav");
153            stone:SetXLocation(stone_pos_x);
154            stone:SetYLocation(stone_pos_y);
155            stone:SetVisible(true);
156            stone:Reset();
157            stone_shadow:SetXLocation(stone_pos_x);
158            stone_shadow:SetYLocation(stone_pos_y + stone_height);
159            stone_shadow:SetVisible(true);
160            stone_shadow:Reset()
161        end
162    end
163
164    -- Triggers the arrow animation
165    if (attack_step == 2) then
166        if (stone_pos_x > enemy_pos_x) then
167            stone_pos_x = stone_pos_x - distance_moved_x;
168            if stone_pos_x < enemy_pos_x then stone_pos_x = enemy_pos_x end
169        end
170        if (stone_pos_x < enemy_pos_x) then
171            stone_pos_x = stone_pos_x + distance_moved_x;
172            if stone_pos_x > enemy_pos_x then stone_pos_x = enemy_pos_x end
173        end
174        if (stone_pos_y > enemy_pos_y) then
175            stone_pos_y = stone_pos_y - distance_moved_y;
176            if stone_pos_y < enemy_pos_y then stone_pos_y = enemy_pos_y end
177        end
178        if (stone_pos_y < enemy_pos_y) then
179            stone_pos_y = stone_pos_y + distance_moved_y;
180            if stone_pos_y > enemy_pos_y then stone_pos_y = enemy_pos_y end
181        end
182
183        if (stone ~= nil) then
184            stone:SetXLocation(stone_pos_x);
185            stone:SetYLocation(stone_pos_y);
186        end
187        if (stone_shadow ~= nil) then
188            stone_shadow:SetXLocation(stone_pos_x);
189            stone_shadow:SetYLocation(stone_pos_y + stone_height);
190        end
191
192        if (stone_pos_x >= enemy_pos_x and stone_pos_y == enemy_pos_y) then
193            character:ChangeSpriteAnimation("idle");
194            attack_step = 3;
195        end
196    end
197
198    if (attack_step == 3) then
199        -- Triggers the damage once the arrow has reached the enemy
200        if (damage_triggered == false) then
201            skill:ExecuteBattleFunction(character, target);
202            -- Remove the skill points at the end of the third attack
203            character:SubtractSkillPoints(skill:GetSPRequired());
204            damage_triggered = true;
205            -- The Remove() call will make the engine delete the objects, so we set them to nil to avoid using them again.
206            if (stone ~= nil) then
207                stone:SetVisible(false)
208                stone:Remove();
209                stone = nil;
210
211                -- Show the hit splash image
212                splash_image:SetXLocation(stone_pos_x);
213                splash_image:SetYLocation(stone_pos_y);
214                splash_image:SetVisible(true);
215                splash_image:Reset();
216
217                attack_time = 0.0
218            end
219            if (stone_shadow ~= nil) then
220                stone_shadow:SetVisible(false);
221                stone_shadow:Remove();
222                stone_shadow = nil;
223            end
224        end
225        attack_step = 4
226    end
227
228    if (attack_step == 4) then
229        if (splash_image ~= nil) then
230            attack_time = attack_time + SystemManager:GetUpdateTime();
231
232            splash_image:GetAnimatedImage():SetDimensions(splash_width * attack_time / 100.0, splash_height * attack_time / 100.0)
233            if (attack_time > 100.0) then
234                attack_step = 5
235            end
236        end
237    end
238
239    if (attack_step == 5) then
240        if (splash_image ~= nil) then
241            splash_image:SetVisible(false);
242            splash_image:Remove();
243            splash_image = nil;
244        end
245        return true;
246    end
247
248    return false;
249end
250