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