1------------------------------------------------------------------------------[[
2-- Description: This file contains the definitions of all weapon skills.
3-- Each attack skill has a unique integer identifier
4-- that is used as its key in the skills table below. Some skills are primarily
5-- intended for characters to use while others are intended for enemies to use.
6-- Normally, we do not want to share skills between characters and enemies as
7-- character skills animate the sprites while enemy skills do not.
8--
9-- Skill IDs 1 through 10,000 are reserved for attack skills.
10--
11-- Each skill entry requires the following data to be defined:
12-- {name}: Text that defines the name of the skill
13-- {description}: A brief (one sentence) description of the skill
14--                (This field is required only for character skills and is optional for enemy skills)
15-- {sp_required}: The number of skill points (SP) that are required to use the skill
16--                (Zero is a valid value for this field, but a negative number is not)
17-- {warmup_time}: The number of milliseconds that the actor using the skill must wait between
18--                selecting the skill and executing it (a value of zero is valid).
19-- {cooldown_time}: The number of milliseconds that the actor using the skill must wait after
20--                  executing the skill before their stamina begins regenrating (zero is valid).
21-- {action_name}: The sprite action played before executing the battle scripted function.
22-- {target_type}: The type of target the skill affects, which may be an attack point, actor, or party.
23--
24-- Each skill entry requires a function called {BattleExecute} to be defined. This function implements the
25-- execution of the skill in battle, dealing damage, causing status changes, playing sounds, and animating
26-- sprites.
27------------------------------------------------------------------------------]]
28
29function clampDuration(effect_duration)
30    local minimum_duration = 15000
31    return (effect_duration >= minimum_duration) and effect_duration or minimum_duration
32end
33
34-- common functions
35function trigger_potential_stun(user, target)
36    local target_actor = target:GetActor();
37    local attack_point = target_actor:GetAttackPoint(target:GetAttackPoint());
38    local chance_modifier = (user:GetTotalMagicalAttack(vt_global.GameGlobal.GLOBAL_ELEMENTAL_NEUTRAL)
39                            - attack_point:GetTotalMagicalDefense(vt_global.GameGlobal.GLOBAL_ELEMENTAL_NEUTRAL)) * 3.0;
40    local chance = (vt_utils.RandomFloat() * 100.0);
41    --print( chance.. "/".. 50.0 + chance_modifier);
42    if (chance > (50.0 + chance_modifier)) then
43        return;
44    end
45
46    -- Compute an effect duration time based on the characters' stats
47    -- Divide stun effect length by 2 has it makes the game more usable
48    local effect_duration = clampDuration(user:GetMagAtk() - target_actor:GetMagDef())
49    target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_PARALYSIS,
50                                         vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_LESSER,
51                                         effect_duration);
52    local Battle = ModeManager:GetTop();
53    Battle:TriggerBattleParticleEffect("data/visuals/particle_effects/stun_star.lua", target_actor:GetXLocation(), target_actor:GetYLocation());
54end
55
56function trigger_potential_attack_lowering(user, target)
57    local target_actor = target:GetActor();
58    local attack_point = target_actor:GetAttackPoint(target:GetAttackPoint());
59    local chance_modifier = (user:GetTotalMagicalAttack(vt_global.GameGlobal.GLOBAL_ELEMENTAL_NEUTRAL)
60                            - attack_point:GetTotalMagicalDefense(vt_global.GameGlobal.GLOBAL_ELEMENTAL_NEUTRAL)) * 3.0;
61    local chance = (vt_utils.RandomFloat() * 100.0);
62    --print( chance.. "/".. 50.0 + chance_modifier);
63    if (chance > (50.0 + chance_modifier)) then
64        return;
65    end
66
67    -- Compute an effect duration time based on the characters' stats
68    local effect_duration = clampDuration(user:GetMagAtk() - target_actor:GetMagDef())
69    target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_PHYS_ATK,
70                                         vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_MODERATE,
71                                         effect_duration);
72end
73
74-- All attack skills definitions are stored in this table
75if (skills == nil) then
76    skills = {}
77end
78
79--------------------------------------------------------------------------------
80-- IDs 1 - 1,000 are reserved for character attack skills
81--------------------------------------------------------------------------------
82
83skills[1] = {
84    name = vt_system.Translate("Sword Slash"),
85    description = vt_system.Translate("A textbook maneuver that deals an effective blow."),
86    sp_required = 0,
87    warmup_time = 1200,
88    cooldown_time = 200,
89    action_name = "attack",
90    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE,
91
92    BattleExecute = function(user, target)
93        local target_actor = target:GetActor();
94
95        if (vt_battle.RndEvade(target_actor) == false) then
96            -- Normal +0 attack
97            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor), target);
98            AudioManager:PlaySound("data/sounds/swordslice1.wav");
99        else
100            target_actor:RegisterMiss(true);
101            AudioManager:PlaySound("data/sounds/sword_swipe.wav");
102        end
103    end,
104
105    animation_scripts = {
106        [BRONANN] = "data/battles/characters_animations/bronann_attack.lua",
107        [THANIS] = "data/battles/characters_animations/thanis_attack.lua"
108    }
109}
110
111skills[2] = {
112    name = vt_system.Translate("Forward Thrust"),
113    show_notice = true,
114    description = vt_system.Translate("A concentrated attack on an enemy."),
115    sp_required = 2,
116    warmup_time = 800,
117    cooldown_time = 200,
118    action_name = "attack",
119    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
120
121    BattleExecute = function(user, target)
122        local target_actor = target:GetActor();
123        local attack_point = target:GetAttackPoint();
124
125        if (vt_battle.RndEvade(target_actor, 0, 1.0, attack_point) == false) then
126            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 0, 1.30, attack_point), target);
127        else
128            target_actor:RegisterMiss(true);
129            AudioManager:PlaySound("data/sounds/sword_swipe.wav");
130        end
131
132    end,
133
134    animation_scripts = {
135        [BRONANN] = "data/battles/characters_animations/bronann_attack_forward_thrust.lua",
136        [KALYA] = "data/battles/characters_animations/kalya_double_attack.lua",
137        [THANIS] = "data/battles/characters_animations/thanis_attack.lua"
138    }
139}
140
141skills[3] = {
142    name = vt_system.Translate("Stun Strike"),
143    show_notice = true,
144    description = vt_system.Translate("A blow which temporarily stun its target."),
145    sp_required = 4,
146    warmup_time = 1200,
147    cooldown_time = 200,
148    action_name = "attack",
149    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
150
151    BattleExecute = function(user, target)
152        local target_actor = target:GetActor();
153        local atk_point = target:GetAttackPoint();
154
155        if (vt_battle.RndEvade(target_actor, 5.5, 1.0, atk_point) == false) then
156            -- Calculate chance for paralysis effect and activate it
157            trigger_potential_stun(user, target);
158
159            -- The damages are applied after the potential effects, so that a potential target death handles the effect removal properly
160            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 0, 1.0, atk_point), target);
161            AudioManager:PlaySound("data/sounds/swordslice1.wav");
162        else
163            target_actor:RegisterMiss(true);
164            AudioManager:PlaySound("data/sounds/sword_swipe.wav");
165        end
166    end,
167
168    animation_scripts = {
169        [BRONANN] = "data/battles/characters_animations/bronann_attack.lua",
170        [KALYA] = "data/battles/characters_animations/kalya_attack.lua",
171        [THANIS] = "data/battles/characters_animations/thanis_attack.lua"
172    }
173}
174
175
176
177skills[4] = {
178    name = vt_system.Translate("X-Strike"),
179    show_notice = true,
180    description = vt_system.Translate("A strong and aggressive attack with a blade that deals significant damage."),
181    sp_required = 32,
182    warmup_time = 2500,
183    cooldown_time = 1000,
184    action_name = "attack",
185    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE,
186
187    BattleExecute = function(user, target)
188        local target_actor = target:GetActor();
189
190        if (vt_battle.RndEvade(target_actor, 8.5) == false) then
191            local effect_duration = clampDuration(user:GetMagAtk())
192            target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_STAMINA,
193                                                 vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_GREATER,
194                                                 effect_duration);
195
196            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 20), target);
197            AudioManager:PlaySound("data/sounds/swordslice2.wav");
198        else
199            target_actor:RegisterMiss(true);
200            AudioManager:PlaySound("data/sounds/sword_swipe.wav");
201        end
202    end,
203
204    animation_scripts = {
205        [BRONANN] = "data/battles/characters_animations/bronann_attack.lua",
206        [THANIS] = "data/battles/characters_animations/thanis_blade_rush_attack.lua"
207    }
208}
209
210-- Kalya first attack
211skills[5] = {
212    name = vt_system.Translate("Single Shot"),
213    description = vt_system.Translate("A simple shot using an arbalest."),
214    sp_required = 0,
215    warmup_time = 2500,
216    cooldown_time = 200,
217    action_name = "attack",
218    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE,
219
220    BattleExecute = function(user, target)
221        local target_actor = target:GetActor();
222
223        if (vt_battle.RndEvade(target_actor) == false) then
224            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 5), target);
225            AudioManager:PlaySound("data/sounds/crossbow.ogg");
226        else
227            target_actor:RegisterMiss(true);
228            AudioManager:PlaySound("data/sounds/crossbow_miss.ogg");
229        end
230    end,
231
232    animation_scripts = {
233        [KALYA] = "data/battles/characters_animations/kalya_attack.lua"
234    }
235}
236
237skills[6] = {
238    name = vt_system.Translate("Blade Rush"),
239    show_notice = true,
240    description = vt_system.Translate("A strong and aggressive attack with a blade that deals significant damage."),
241    sp_required = 100,
242    warmup_time = 5000,
243    cooldown_time = 3000,
244    action_name = "attack",
245    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE,
246
247    BattleExecute = function(user, target)
248        local target_actor = target:GetActor();
249
250        if (vt_battle.RndEvade(target_actor, 8.5) == false) then
251            local effect_duration = clampDuration(user:GetMagAtk())
252            target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_STAMINA,
253                                                 vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_GREATER,
254                                                 effect_duration);
255
256            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 20), target);
257            AudioManager:PlaySound("data/sounds/swordslice2.wav");
258        else
259            target_actor:RegisterMiss(true);
260            AudioManager:PlaySound("data/sounds/sword_swipe.wav");
261        end
262    end,
263
264    animation_scripts = {
265        [BRONANN] = "data/battles/characters_animations/bronann_attack.lua",
266        [THANIS] = "data/battles/characters_animations/thanis_blade_rush_attack.lua"
267    }
268}
269
270-- Kalya's attacks
271skills[101] = {
272    name = vt_system.Translate("Incapacitating Shot"),
273    show_notice = true,
274    description = vt_system.Translate("A powerful shot aimed at lowering the enemy's ability to attack."),
275    sp_required = 14,
276    warmup_time = 2700,
277    cooldown_time = 600,
278    action_name = "attack",
279    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
280
281    BattleExecute = function(user, target)
282        local target_actor = target:GetActor();
283        local atk_point = target:GetAttackPoint();
284
285        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
286            -- Calculate chance for attack lowering effect and activate it
287            trigger_potential_attack_lowering(user, target);
288            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 15, 1.0, atk_point), target);
289            AudioManager:PlaySound("data/sounds/crossbow.ogg");
290        else
291            target_actor:RegisterMiss(true);
292            AudioManager:PlaySound("data/sounds/crossbow_miss.ogg");
293        end
294    end,
295
296    animation_scripts = {
297        [KALYA] = "data/battles/characters_animations/kalya_attack.lua"
298    }
299}
300
301skills[102] = {
302    name = vt_system.Translate("Rain of Arrows"),
303    show_notice = true,
304    description = vt_system.Translate("An attack striking all enemies."),
305    sp_required = 28,
306    warmup_time = 3500,
307    cooldown_time = 1000,
308    action_name = "attack",
309    target_type = vt_global.GameGlobal.GLOBAL_TARGET_ALL_FOES,
310
311    BattleExecute = function(user, target)
312        local index = 0;
313        while (target:GetPartyActor(index) ~= nil) do
314            local target_actor = target:GetPartyActor(index)
315
316            if (vt_battle.RndEvade(target_actor) == false) then
317                target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 5), target);
318                AudioManager:PlaySound("data/sounds/crossbow.ogg");
319            else
320                target_actor:RegisterMiss(true);
321                AudioManager:PlaySound("data/sounds/crossbow_miss.ogg");
322            end
323
324            index = index + 1
325        end
326    end,
327
328    animation_scripts = {
329        [KALYA] = "data/battles/characters_animations/kalya_attack_party_target.lua"
330    }
331}
332
333-- Sylve first attack
334skills[200] = {
335    name = vt_system.Translate("Dagger Slash"),
336    description = vt_system.Translate("A simple but efficient dagger attack."),
337    sp_required = 0,
338    warmup_time = 1000,
339    cooldown_time = 200,
340    action_name = "attack",
341    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
342
343    BattleExecute = function(user, target)
344        local target_actor = target:GetActor();
345        local atk_point = target:GetAttackPoint();
346
347        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
348            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 5, 1.0, atk_point), target);
349            AudioManager:PlaySound("data/sounds/swordslice2.wav");
350        else
351            target_actor:RegisterMiss(true);
352            AudioManager:PlaySound("data/sounds/missed_target.wav");
353        end
354    end
355}
356
357skills[201] = {
358    name = vt_system.Translate("Poison Slash"),
359    show_notice = true,
360    description = vt_system.Translate("A dagger attack, poisoning the enemy."),
361    sp_required = 5,
362    warmup_time = 1000,
363    cooldown_time = 200,
364    action_name = "attack",
365    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
366
367    BattleExecute = function(user, target)
368        local target_actor = target:GetActor();
369        local atk_point = target:GetAttackPoint();
370
371        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
372            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 5, 1.0, atk_point), target);
373            -- TODO: Add poison damage
374            AudioManager:PlaySound("data/sounds/swordslice2.wav");
375        else
376            target_actor:RegisterMiss(true);
377            AudioManager:PlaySound("data/sounds/missed_target.wav");
378        end
379    end
380}
381
382skills[202] = {
383    name = vt_system.Translate("High Strike"),
384    show_notice = true,
385    description = vt_system.Translate("A strike from above, stunning the enemy and breaking its magical defense."),
386    sp_required = 10,
387    warmup_time = 1000,
388    cooldown_time = 200,
389    action_name = "attack",
390    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
391
392    BattleExecute = function(user, target)
393        local target_actor = target:GetActor();
394        local atk_point = target:GetAttackPoint();
395--TODO
396        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
397            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 5, 1.0, atk_point), target);
398            -- TODO: Add poison damage
399            AudioManager:PlaySound("data/sounds/swordslice2.wav");
400        else
401            target_actor:RegisterMiss(true);
402            AudioManager:PlaySound("data/sounds/missed_target.wav");
403        end
404    end
405}
406
407skills[203] = {
408    name = vt_system.Translate("Sonic Wind"),
409    show_notice = true,
410    description = vt_system.Translate("Deals high wind damage to all enemies, stunning them along the way."),
411    sp_required = 100,
412    warmup_time = 3000,
413    cooldown_time = 1200,
414    action_name = "attack",
415    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
416
417    BattleExecute = function(user, target)
418        local target_actor = target:GetActor();
419        local atk_point = target:GetAttackPoint();
420--TODO
421        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
422            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 5, 1.0, atk_point), target);
423            AudioManager:PlaySound("data/sounds/swordslice2.wav");
424        else
425            target_actor:RegisterMiss(true);
426            AudioManager:PlaySound("data/sounds/missed_target.wav");
427        end
428    end
429}
430
431-- Thanis attacks
432skills[300] = {
433    name = vt_system.Translate("Sword Breaker"),
434    show_notice = true,
435    description = vt_system.Translate("An attack that breaks the enemy's own attack power."),
436    sp_required = 6,
437    warmup_time = 2000,
438    cooldown_time = 500,
439    action_name = "attack",
440    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
441
442    BattleExecute = function(user, target)
443        local target_actor = target:GetActor();
444        local atk_point = target:GetAttackPoint();
445--TODO
446        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
447            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 5, 1.0, atk_point), target);
448            AudioManager:PlaySound("data/sounds/swordslice2.wav");
449        else
450            target_actor:RegisterMiss(true);
451            AudioManager:PlaySound("data/sounds/missed_target.wav");
452        end
453    end,
454
455    animation_scripts = {
456        [THANIS] = "data/battles/characters_animations/thanis_attack.lua"
457    }
458}
459
460skills[301] = {
461    name = vt_system.Translate("Execution"),
462    show_notice = true,
463    description = vt_system.Translate("Attacks an enemy with a chance of causing death to catch its soul."),
464    sp_required = 48,
465    warmup_time = 4000,
466    cooldown_time = 1500,
467    action_name = "attack",
468    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
469
470    BattleExecute = function(user, target)
471        local target_actor = target:GetActor();
472        local atk_point = target:GetAttackPoint();
473--TODO
474        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
475            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 5, 1.0, atk_point), target);
476            AudioManager:PlaySound("data/sounds/swordslice2.wav");
477        else
478            target_actor:RegisterMiss(true);
479            AudioManager:PlaySound("data/sounds/missed_target.wav");
480        end
481    end,
482}
483
484--------------------------------------------------------------------------------
485-- IDs 1,001 - 10,000 are reserved for enemy attack skills
486--------------------------------------------------------------------------------
487
488skills[1001] = {
489    name = "Slime Attack",
490    sp_required = 0,
491    warmup_time = 1100,
492    cooldown_time = 500,
493    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
494
495    animation_scripts = {
496        -- N.B.: [1] is the enemy ID.
497        [1] = "data/battles/enemies_animations/standard_enemy_attack.lua",
498        [6] = "data/battles/enemies_animations/standard_enemy_attack.lua",
499    },
500
501    BattleExecute = function(user, target)
502        local target_actor = target:GetActor();
503        local atk_point = target:GetAttackPoint();
504
505        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
506            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 10, 1.0, atk_point), target);
507            AudioManager:PlaySound("data/sounds/slime_attack.wav");
508        else
509            target_actor:RegisterMiss(true);
510            AudioManager:PlaySound("data/sounds/missed_target.wav");
511        end
512    end
513}
514
515skills[1002] = {
516    name = "Spider Bite",
517    sp_required = 0,
518    warmup_time = 1400,
519    cooldown_time = 0,
520    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
521
522    animation_scripts = {
523        -- N.B.: [2] is the enemy ID.
524        [2] = "data/battles/enemies_animations/standard_enemy_attack.lua",
525    },
526
527    BattleExecute = function(user, target)
528        local target_actor = target:GetActor();
529        local atk_point = target:GetAttackPoint();
530
531        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
532            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 13, 1.0, atk_point), target);
533            AudioManager:PlaySound("data/sounds/spider_attack.wav");
534        else
535            target_actor:RegisterMiss(true);
536            AudioManager:PlaySound("data/sounds/missed_target.wav");
537        end
538    end
539}
540
541skills[1003] = {
542    name = "Snake Bite",
543    sp_required = 0,
544    warmup_time = 900,
545    cooldown_time = 0,
546    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
547
548    animation_scripts = {
549        -- N.B.: [4] is the enemy ID.
550        [4] = "data/battles/enemies_animations/standard_enemy_attack.lua",
551    },
552
553    BattleExecute = function(user, target)
554        local target_actor = target:GetActor();
555        local atk_point = target:GetAttackPoint();
556
557        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
558            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 14, 1.0, atk_point), target);
559            AudioManager:PlaySound("data/sounds/snake_attack.wav");
560        else
561            target_actor:RegisterMiss(true);
562            AudioManager:PlaySound("data/sounds/missed_target.wav");
563        end
564    end
565}
566
567skills[1004] = {
568    name = vt_system.Translate("Stun Bite"),
569    icon = "data/skills/monster_attacks/animal-fangs.png",
570    show_notice = true,
571    sp_required = 1,
572    warmup_time = 3000,
573    cooldown_time = 1000,
574    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
575
576    animation_scripts = {
577        -- N.B.: [4] is the enemy ID.
578        [4] = "data/battles/enemies_animations/standard_enemy_attack.lua",
579    },
580
581    BattleExecute = function(user, target)
582        local target_actor = target:GetActor();
583        local atk_point = target:GetAttackPoint();
584
585        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
586            -- Calculate chance for paralysis effect and activate it
587            trigger_potential_stun(user, target);
588
589            -- The damages are applied after the potential effects, so that a potential target death handles the effect removal properly
590            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 14, 1.0, atk_point), target);
591            AudioManager:PlaySound("data/sounds/snake_attack.wav");
592        else
593            target_actor:RegisterMiss(true);
594            AudioManager:PlaySound("data/sounds/missed_target.wav");
595        end
596    end
597}
598
599skills[1005] = {
600    -- Snake Dampening Bite
601    name = vt_system.Translate("Dampening Bite"),
602    icon = "data/skills/monster_attacks/animal-fangs.png",
603    show_notice = true,
604    sp_required = 2,
605    warmup_time = 5000,
606    cooldown_time = 2000,
607    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
608
609    animation_scripts = {
610        -- N.B.: [4] is the enemy ID.
611        [4] = "data/battles/enemies_animations/snake_dampen_attack.lua",
612        [12] = "data/battles/enemies_animations/snake_dampen_attack.lua",
613    },
614
615    BattleExecute = function(user, target)
616        local target_actor = target:GetActor();
617        local atk_point = target:GetAttackPoint();
618
619        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
620            local effect_duration = clampDuration(user:GetMagAtk())
621            target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_STAMINA,
622                                                 vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_LESSER,
623                                                 effect_duration);
624
625            -- The damages are applied after the potential effects, so that a potential target death handles the effect removal properly
626            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 6, 1.0, atk_point), target);
627            AudioManager:PlaySound("data/sounds/bite_01.ogg");
628        else
629            target_actor:RegisterMiss(true);
630            AudioManager:PlaySound("data/sounds/missed_target.wav");
631        end
632    end
633}
634
635skills[1006] = {
636    name = "Skeleton Sword Attack",
637    sp_required = 0,
638    warmup_time = 1400,
639    cooldown_time = 0,
640    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
641
642    BattleExecute = function(user, target)
643        local target_actor = target:GetActor();
644        local atk_point = target:GetAttackPoint();
645
646        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
647            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 20, 1.0, atk_point), target);
648            AudioManager:PlaySound("data/sounds/skeleton_attack.wav");
649        else
650            target_actor:RegisterMiss(true);
651            AudioManager:PlaySound("data/sounds/missed_target.wav");
652        end
653    end
654}
655
656skills[1007] = {
657    name = vt_system.Translate("HP Drain"),
658    icon = "data/skills/monster_attacks/animal-fangs.png",
659    show_notice = true,
660    sp_required = 2,
661    warmup_time = 5000,
662    cooldown_time = 2000,
663    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
664
665    animation_scripts = {
666        -- N.B.: [6] is the enemy ID.
667        [6] = "data/battles/enemies_animations/bat_drain_attack.lua",
668        [11] = "data/battles/enemies_animations/bat_drain_attack.lua",
669        [12] = "data/battles/enemies_animations/bat_drain_attack.lua",
670    },
671
672    BattleExecute = function(user, target)
673        local target_actor = target:GetActor();
674        local atk_point = target:GetAttackPoint();
675
676        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
677            local hp_drain = vt_battle.RndPhysicalDamage(user, target_actor, 8, 1.0, atk_point);
678            target_actor:RegisterDamage(hp_drain, target);
679            -- If the damage dealt was 1, don't recover any HP from the attack
680            if (hp_drain > 0) then
681                user:RegisterHealing(hp_drain, true);
682            end
683            AudioManager:PlaySound("data/sounds/bite_01.ogg")
684        else
685            target_actor:RegisterMiss(true);
686            AudioManager:PlaySound("data/sounds/missed_target.wav")
687        end
688    end
689}
690
691skills[1008] = {
692    name = "Fenrir Attack",
693    sp_required = 0,
694    warmup_time = 1200,
695    cooldown_time = 300,
696    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
697
698    animation_scripts = {
699        -- N.B.: [3] is the enemy ID.
700        [3] = "data/battles/enemies_animations/wolf_claw_attack.lua",
701        [7] = "data/battles/enemies_animations/wolf_claw_attack.lua",
702        [8] = "data/battles/enemies_animations/wolf_claw_attack.lua",
703    },
704
705    BattleExecute = function(user, target)
706        local target_actor = target:GetActor();
707        local atk_point = target:GetAttackPoint();
708
709        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
710            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 20, 1.0, atk_point), target);
711            AudioManager:PlaySound("data/sounds/bite_01.ogg")
712        else
713            target_actor:RegisterMiss(true);
714            AudioManager:PlaySound("data/sounds/missed_target.wav")
715        end
716    end
717}
718
719skills[1009] = {
720    -- Fenrir Multiple Attack
721    name = vt_system.Translate("Multiple Attack"),
722    icon = "data/skills/monster_attacks/animal-fangs.png",
723    show_notice = true,
724    sp_required = 1,
725    warmup_time = 1600,
726    cooldown_time = 500,
727    target_type = vt_global.GameGlobal.GLOBAL_TARGET_ALL_FOES,
728
729    animation_scripts = {
730        -- N.B.: [3] is the enemy ID.
731        [3] = "data/battles/enemies_animations/wolf_fangs_party_attack.lua",
732        [7] = "data/battles/enemies_animations/wolf_fangs_party_attack.lua",
733        [8] = "data/battles/enemies_animations/wolf_fangs_party_attack.lua",
734    },
735
736    BattleExecute = function(user, target)
737        local index = 0;
738        while true do
739            local target_actor = target:GetPartyActor(index);
740            if (target_actor == nil) then
741                break;
742            end
743
744            if (target_actor:IsAlive() == true and vt_battle.RndEvade(target_actor) == false) then
745                target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 40));
746                AudioManager:PlaySound("data/sounds/bite_02.ogg")
747            else
748                target_actor:RegisterMiss(true);
749            end
750
751            index = index + 1;
752        end
753    end
754}
755
756skills[1010] = {
757    name = "Rat Poison Bite",
758    sp_required = 5,
759    warmup_time = 900,
760    cooldown_time = 100,
761    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
762
763    animation_scripts = {
764        -- N.B.: [X] is the enemy ID.
765        [16] = "data/battles/enemies_animations/rat_poison_bite_attack.lua",
766    },
767
768    BattleExecute = function(user, target)
769        local target_actor = target:GetActor();
770        local atk_point = target:GetAttackPoint();
771
772        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
773            local intensity = target_actor:GetActiveStatusEffectIntensity(vt_global.GameGlobal.GLOBAL_STATUS_HP);
774
775            -- Only apply up to a moderate poison
776            if (intensity > vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_MODERATE) then
777                local effect_duration = clampDuration(user:GetMagAtk())
778                target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_HP,
779                                                     vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_LESSER,
780                                                     effect_duration);
781            end
782
783            -- The damages are applied after the potential effects, so that a potential target death handles the effect removal properly
784            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 6, 1.0, atk_point), target);
785        else
786            target_actor:RegisterMiss(true);
787            AudioManager:PlaySound("data/sounds/missed_target.wav")
788        end
789    end
790}
791
792skills[1011] = {
793    -- Skeleton Frenzy Attack
794    name = vt_system.Translate("Frenzy Attack"),
795    show_notice = true,
796    sp_required = 10,
797    warmup_time = 2400,
798    cooldown_time = 500,
799    target_type = vt_global.GameGlobal.GLOBAL_TARGET_ALL_FOES,
800
801    animation_scripts = {
802        -- N.B.: [19] is the enemy ID.
803        [19] = "data/battles/enemies_animations/skeleton_frenzy_attack.lua",
804    },
805
806    BattleExecute = function(user, target)
807        local index = 0;
808        while true do
809            local target_actor = target:GetPartyActor(index);
810            if (target_actor == nil) then
811                break;
812            end
813
814            if (target_actor:IsAlive() == true and vt_battle.RndEvade(target_actor) == false) then
815                target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 25), target);
816                AudioManager:PlaySound("data/sounds/skeleton_attack.wav");
817            else
818                target_actor:RegisterMiss(true);
819                AudioManager:PlaySound("data/sounds/missed_target.wav");
820            end
821
822            index = index + 1;
823        end
824    end
825}
826
827skills[1012] = {
828    name = vt_system.Translate("Stamina Steal"),
829    show_notice = true,
830    sp_required = 12,
831    warmup_time = 2400,
832    cooldown_time = 500,
833    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE,
834
835    animation_scripts = {
836        -- N.B.: [X] is the enemy ID.
837        [15] = "data/battles/enemies_animations/slime_water_spray_attack.lua", -- Beetle
838    },
839
840    BattleExecute = function(user, target)
841        local target_actor = target:GetActor();
842
843        if (vt_battle.RndEvade(target_actor) == false) then
844            local effect_duration = clampDuration(user:GetMagAtk())
845            target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_STAMINA,
846                                                 vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_MODERATE,
847                                                 effect_duration);
848            user:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_STAMINA,
849                                         vt_global.GameGlobal.GLOBAL_INTENSITY_POS_MODERATE,
850                                         effect_duration);
851
852            -- The damages are applied after the potential effects, so that a potential target death handles the effect removal properly
853            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 6), target);
854            AudioManager:PlaySound("data/sounds/spider_attack.wav");
855        else
856            target_actor:RegisterMiss(true);
857            AudioManager:PlaySound("data/sounds/missed_target.wav");
858        end
859    end
860}
861
862skills[1013] = {
863    name = "Dorver Frenzy",
864    sp_required = 10,
865    warmup_time = 2900,
866    cooldown_time = 1000,
867    target_type = vt_global.GameGlobal.GLOBAL_TARGET_SELF,
868
869    BattleExecute = function(user, target) -- target is self, we'll use user...
870        -- Add phys_atk & stamina, but decrease defense
871        local effect_duration = clampDuration(user:GetMagAtk())
872        user:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_PHYS_ATK,
873                                     vt_global.GameGlobal.GLOBAL_INTENSITY_POS_MODERATE,
874                                     effect_duration);
875        user:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_STAMINA,
876                                     vt_global.GameGlobal.GLOBAL_INTENSITY_POS_MODERATE,
877                                     effect_duration);
878        user:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_PHYS_DEF,
879                                     vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_MODERATE,
880                                     effect_duration);
881
882        AudioManager:PlaySound("data/sounds/defense1_spell.ogg");
883    end
884}
885
886skills[1014] = {
887    name = "Andromalius Ground Hit",
888    sp_required = 0,
889    warmup_time = 1900,
890    cooldown_time = 1000,
891    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE,
892
893    BattleExecute = function(user, target)
894        local target_actor = target:GetActor();
895
896        if (vt_battle.RndEvade(target_actor) == false) then
897            local effect_duration = clampDuration(user:GetMagAtk())
898            target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_STAMINA,
899                                                 vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_MODERATE,
900                                                 effect_duration);
901
902            -- The damages are applied after the potential effects, so that a potential target death handles the effect removal properly
903            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 6), target);
904            AudioManager:PlaySound("data/sounds/cave-in.ogg");
905        else
906            target_actor:RegisterMiss(true);
907        end
908    end
909}
910
911skills[1015] = {
912    name = vt_system.Translate("Water Spray"),
913    icon = "data/skills/monster_attacks/waterspray.png",
914    show_notice = true,
915    sp_required = 0,
916    warmup_time = 1100,
917    cooldown_time = 500,
918    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
919
920    animation_scripts = {
921        -- N.B.: [1] is the slime enemy ID.
922        [1] = "data/battles/enemies_animations/slime_water_spray_attack.lua",
923    },
924
925    BattleExecute = function(user, target)
926        local target_actor = target:GetActor();
927        local atk_point = target:GetAttackPoint();
928
929        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
930            local effect_duration = clampDuration(user:GetMagAtk())
931            target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_EVADE,
932                                                 vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_LESSER,
933                                                 effect_duration);
934            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 10, 1.0, atk_point), target);
935
936            local Battle = ModeManager:GetTop();
937            Battle:TriggerBattleParticleEffect("data/visuals/particle_effects/waterspray_skill.lua",
938                                               target_actor:GetXLocation(), target_actor:GetYLocation());
939        else
940            target_actor:RegisterMiss(true);
941        end
942        -- Play the sound in any case
943        AudioManager:PlaySound("data/sounds/watersplash.wav");
944    end
945}
946
947skills[1016] = {
948    name = vt_system.Translate("Spider Web"),
949    icon = "data/skills/monster_attacks/web.png",
950    show_notice = true,
951    sp_required = 0,
952    warmup_time = 1100,
953    cooldown_time = 500,
954    target_type = vt_global.GameGlobal.GLOBAL_TARGET_FOE_POINT,
955
956    animation_scripts = {
957        -- N.B.: [2] is the spider enemy ID.
958        [2] = "data/battles/enemies_animations/spider_web_attack.lua",
959    },
960
961    BattleExecute = function(user, target)
962        local target_actor = target:GetActor();
963        local atk_point = target:GetAttackPoint();
964
965        if (vt_battle.RndEvade(target_actor, 0.0, 1.0, atk_point) == false) then
966            local effect_duration = clampDuration(user:GetMagAtk())
967            target_actor:ApplyActiveStatusEffect(vt_global.GameGlobal.GLOBAL_STATUS_STAMINA,
968                                                 vt_global.GameGlobal.GLOBAL_INTENSITY_NEG_MODERATE,
969                                                 effect_duration);
970            target_actor:RegisterDamage(vt_battle.RndPhysicalDamage(user, target_actor, 13, 1.0, atk_point), target);
971        else
972            target_actor:RegisterMiss(true);
973        end
974        -- Play the sound in any case
975        AudioManager:PlaySound("data/sounds/missed_target.wav");
976    end
977}
978