1-- Set the namespace according to the map name.
2local ns = {};
3setmetatable(ns, {__index = _G});
4layna_village_well_underground_script = ns;
5setfenv(1, ns);
6
7-- The map name, subname and location image
8map_name = "Mountain Village of Layna"
9map_image_filename = "data/story/common/locations/mountain_village.png"
10map_subname = "Well Undergrounds"
11
12-- The music file used as default background music on this map.
13-- Other musics will have to handled through scripting.
14music_filename = "data/music/shrine-OGA-yd.ogg"
15
16-- c++ objects instances
17local Map = nil
18local Script = nil
19local EventManager = nil
20
21-- Characters handler
22local bronann = nil
23local olivia = nil
24local hero = nil
25
26-- the main map loading code
27function Load(m)
28
29    Map = m;
30    Script = Map:GetScriptSupervisor()
31    EventManager = Map:GetEventSupervisor()
32
33    _CreateCharacters()
34    _CreateObjects()
35
36    -- Put last to get a proper night effect
37    Script:AddScript("data/story/common/lost_in_darkness.lua");
38
39    -- Set the camera focus on bronann
40    Map:SetCamera(bronann)
41
42    _CreateEvents()
43    _CreateZones()
44    _CreateEnemies()
45end
46
47-- the map update function handles checks done on each game tick.
48function Update()
49    -- Check whether the character is in one of the zones
50    _CheckZones()
51
52    _UpdateLampLocation()
53
54    _CheckMonstersState()
55end
56
57-- Character creation
58function _CreateCharacters()
59
60    bronann = CreateSprite(Map, "Bronann", 18, 3, vt_map.MapMode.GROUND_OBJECT)
61    bronann:SetDirection(vt_map.MapMode.SOUTH)
62    bronann:SetMovementSpeed(vt_map.MapMode.NORMAL_SPEED)
63    hero = bronann
64
65    olivia = CreateNPCSprite(Map, "Girl1", vt_system.Translate("Olivia"), 18, 3, vt_map.MapMode.GROUND_OBJECT)
66    olivia:SetDirection(vt_map.MapMode.SOUTH)
67    olivia:SetMovementSpeed(vt_map.MapMode.NORMAL_SPEED)
68end
69
70-- oil lamp
71local oil_lamp = nil
72local lamp_halo = nil
73local lamp_flare = nil
74
75-- The heal particle effect map object
76local heal_effect = nil
77
78function _CreateObjects()
79    -- Info sign
80    local object = CreateObject(Map, "Wood sign info", 5, 12, vt_map.MapMode.GROUND_OBJECT)
81    object:SetEventWhenTalking("Info about equipment")
82    local dialogue = vt_map.SpriteDialogue.Create();
83    local text = vt_system.Translate("Did you know?\nYou can equip weapons and armors from the menu.\nSimply open it and go to 'Inventory', and 'Equip'.");
84    dialogue:AddLine(text, nil);
85    local event = vt_map.DialogueEvent.Create("Info about equipment", dialogue)
86
87    oil_lamp = CreateObject(Map, "Oil Lamp", 15, 14, vt_map.MapMode.GROUND_OBJECT)
88    oil_lamp:SetCollisionMask(vt_map.MapMode.NO_COLLISION)
89    local lamp_x_pos = oil_lamp:GetXPosition()
90    local lamp_y_pos = oil_lamp:GetYPosition()
91
92    lamp_halo = vt_map.Halo.Create("data/visuals/lights/torch_light_mask2.lua", lamp_x_pos, lamp_y_pos,
93        vt_video.Color(0.9, 0.9, 0.4, 0.5))
94    lamp_flare = vt_map.Halo.Create("data/visuals/lights/sun_flare_light_main.lua", lamp_x_pos, lamp_y_pos,
95        vt_video.Color(0.99, 1.0, 0.27, 0.2))
96    _SetLampPosition(lamp_x_pos, lamp_y_pos)
97
98    -- hide it when the intro event is not done yet.
99    if (GlobalManager:GetGameEvents():DoesEventExist("story", "well_intro_event_done") == false) then
100        oil_lamp:SetVisible(false)
101        lamp_halo:SetVisible(false)
102        lamp_flare:SetVisible(false)
103    end
104
105    -- Add the heal icon
106    local npc = CreateObject(Map, "Layna Statue", 11, 10, vt_map.MapMode.GROUND_OBJECT);
107    npc:SetCollisionMask(vt_map.MapMode.NO_COLLISION);
108    npc:SetInteractionIcon("data/gui/map/heal_anim.lua")
109    npc = CreateSprite(Map, "Butterfly", 11, 10, vt_map.MapMode.GROUND_OBJECT);
110    npc:SetCollisionMask(vt_map.MapMode.NO_COLLISION);
111    npc:SetVisible(false);
112    npc:SetName(""); -- Unset the speaker name
113    dialogue = vt_map.SpriteDialogue.Create();
114    text = vt_system.Translate("Your party feels better.");
115    dialogue:AddLineEvent(text, npc, "Well entrance heal", "");
116    npc:AddDialogueReference(dialogue);
117
118    vt_map.ScriptedEvent.Create("Well entrance heal", "heal_party", "heal_done");
119
120    -- Load the spring heal effect.
121    heal_effect = vt_map.ParticleObject.Create("data/visuals/particle_effects/heal_particle.lua", 0, 0, vt_map.MapMode.GROUND_OBJECT);
122    heal_effect:Stop(); -- Don't run it until the character heals itself
123
124    CreateObject(Map, "Rock1", 59, 11.2, vt_map.MapMode.GROUND_OBJECT)
125    CreateObject(Map, "Rock1", 3, 43, vt_map.MapMode.GROUND_OBJECT)
126    CreateObject(Map, "Rock1", 5.5, 43.2, vt_map.MapMode.GROUND_OBJECT)
127    CreateObject(Map, "Rock1", 60, 25, vt_map.MapMode.GROUND_OBJECT)
128
129    local chest = CreateTreasure(Map, "well_underground_chest", "Wood_Chest2", 57, 11.5, vt_map.MapMode.GROUND_OBJECT)
130    chest:AddItem(1, 3)
131end
132
133local lamp_character = nil
134
135function _UpdateLampLocation()
136    -- Stick the lamp to a given character reference
137    -- if the reference is nil, just let the lamp where it is.
138    if (lamp_character == nil) then
139        return
140    end
141
142    -- Sets the lamp location depending on the character's location and orientation
143    local character_direction = lamp_character:GetDirection()
144    local x_pos = lamp_character:GetXPosition()
145    local y_pos = lamp_character:GetYPosition()
146    local x_offset = 0.0
147    local y_offset = 0.0
148    if (character_direction == vt_map.MapMode.WEST
149            or character_direction == vt_map.MapMode.NW_WEST
150            or character_direction == vt_map.MapMode.SW_WEST) then
151        x_offset = -0.5
152        y_offset = -0.5
153    elseif (character_direction == vt_map.MapMode.EAST
154            or character_direction == vt_map.MapMode.NE_EAST
155            or character_direction == vt_map.MapMode.SE_EAST) then
156        x_offset = 0.5
157        y_offset = -0.5
158    elseif (character_direction == vt_map.MapMode.NORTH
159            or character_direction == vt_map.MapMode.NW_NORTH
160            or character_direction == vt_map.MapMode.NE_NORTH) then
161        x_offset = -0.5
162        y_offset = -0.5
163    elseif (character_direction == vt_map.MapMode.SOUTH
164            or character_direction == vt_map.MapMode.SW_SOUTH
165            or character_direction == vt_map.MapMode.SE_SOUTH) then
166        x_offset = 0.5
167        y_offset = -0.5
168    end
169
170    _SetLampPosition(x_pos + x_offset, y_pos + y_offset)
171end
172
173-- Handles the fixed offsets betwwen the oil_lamp object, and the related ones
174function _SetLampPosition(x, y)
175    oil_lamp:SetPosition(x, y)
176    lamp_halo:SetPosition(x, y + 4.0)
177    lamp_flare:SetPosition(x, y + 2.0)
178end
179
180-- Special roam zones used to test whether Bronann did the fights
181local roam_zone1 = nil
182local roam_zone2 = nil
183local roam_zone3 = nil
184local rats_beaten = false
185
186function _CreateEnemies()
187    local enemy = nil
188
189    -- Hint: left, right, top, bottom
190    roam_zone1 = vt_map.EnemyZone.Create(48, 61, 2, 20)
191    enemy = CreateEnemySprite(Map, "ratto")
192    _SetBattleEnvironment(enemy)
193    enemy:NewEnemyParty()
194    enemy:AddEnemy(22)
195    roam_zone1:AddEnemy(enemy, 2)
196    roam_zone1:SetSpawnsLeft(1); -- This monster shall spawn only one time.
197
198    -- Hint: left, right, top, bottom
199    roam_zone2 = vt_map.EnemyZone.Create(2, 10, 42, 63)
200    enemy = CreateEnemySprite(Map, "ratto")
201    _SetBattleEnvironment(enemy)
202    enemy:NewEnemyParty()
203    enemy:AddEnemy(22)
204    roam_zone2:AddEnemy(enemy, 2)
205    roam_zone2:SetSpawnsLeft(1); -- This monster shall spawn only one time.
206
207    -- Hint: left, right, top, bottom
208    roam_zone3 = vt_map.EnemyZone.Create(58, 60, 58, 60)
209    enemy = CreateEnemySprite(Map, "ratto")
210    _SetBattleEnvironment(enemy)
211    enemy:NewEnemyParty()
212    enemy:AddEnemy(22)
213    enemy:AddEnemy(22)
214    enemy:SetBoss(true)
215    roam_zone3:AddEnemy(enemy, 1)
216    roam_zone3:SetSpawnsLeft(1); -- This monster shall spawn only one time.
217end
218
219function _CheckMonstersState()
220    if (rats_beaten == false and roam_zone1:GetSpawnsLeft() == 0 and roam_zone2:GetSpawnsLeft() == 0 and roam_zone3:GetSpawnsLeft() == 0) then
221        if (GlobalManager:GetGameEvents():DoesEventExist("story", "well_rats_beaten") == false) then
222            rats_beaten = true
223
224            -- Update Olivia dialogue to let Bronann go out
225            olivia:ClearDialogueReferences()
226            local dialogue = vt_map.SpriteDialogue.Create()
227            local text = vt_system.Translate("Congratulations, Bronann. Here is your reward.")
228            dialogue:AddLineEmote(text, olivia, "exclamation")
229            dialogue:SetEventAtDialogueEnd("Bronann receives reward")
230            olivia:AddDialogueReference(dialogue)
231
232            local event = vt_map.TreasureEvent.Create("Bronann receives reward");
233            event:AddItem(40002, 1) -- Long leather gloves
234            event:AddEventLinkAtEnd("Olivia leaves");
235
236            event = vt_map.PathMoveSpriteEvent.Create("Olivia leaves", olivia, 18, 1, false)
237            event:AddEventLinkAtEnd("Set Olivia invisible")
238
239            event = vt_map.ScriptedEvent.Create("Set Olivia invisible", "make_olivia_invisible", "")
240
241            -- Set intro event as done
242            GlobalManager:GetGameEvents():SetEventValue("story", "well_rats_beaten", 1);
243        end
244    end
245end
246
247-- Sets common battle environment settings for enemy sprites
248function _SetBattleEnvironment(enemy)
249    enemy:SetBattleMusicTheme("data/music/heroism-OGA-Edward-J-Blakeley.ogg")
250    enemy:SetBattleBackground("data/battles/battle_scenes/desert_cave/desert_cave.png")
251    enemy:AddBattleScript("data/battles/battle_scenes/desert_cave_battle_anim.lua")
252    enemy:AddBattleScript("data/story/common/lost_in_darkness.lua")
253    -- Add tutorial battle dialog with Olivia
254    enemy:AddBattleScript("data/story/common/tutorial_battle_dialogs.lua");
255    -- TODO: Add oil lamp as scenery in battles
256end
257
258-- Creates all events and sets up the entire event sequence chain
259function _CreateEvents()
260    local event = nil
261    local dialogue = nil
262    local text = nil
263
264    -- Triggered events
265    vt_map.MapTransitionEvent.Create("exit floor", "data/story/layna_village/layna_village_center_map.lua",
266                                     "data/story/layna_village/layna_village_center_script.lua",
267                                     "from_well_undergrounds")
268
269    -- Generic events
270    vt_map.LookAtSpriteEvent.Create("Olivia looks at Bronann", olivia, bronann)
271    vt_map.LookAtSpriteEvent.Create("Bronann looks at Olivia", bronann, olivia)
272    vt_map.ChangeDirectionSpriteEvent.Create("Bronann looks south", bronann, vt_map.MapMode.SOUTH)
273    vt_map.ChangeDirectionSpriteEvent.Create("Bronann looks east", bronann, vt_map.MapMode.EAST)
274
275    if (GlobalManager:GetGameEvents():DoesEventExist("story", "well_intro_event_done") == false) then
276        event = vt_map.ScriptedEvent.Create("Well undergrounds scene start", "well_intro_scene_start", "")
277        event:AddEventLinkAtEnd("Olivia takes a few steps")
278
279        event = vt_map.PathMoveSpriteEvent.Create("Olivia takes a few steps", olivia, 18, 13, false)
280        event:AddEventLinkAtEnd("Olivia shows the oil lamp", 1000)
281
282        event = vt_map.ScriptedEvent.Create("Olivia shows the oil lamp", "oil_lamp_move_start", "oil_lamp_move_update")
283        event:AddEventLinkAtEnd("Olivia lights the oil lamp", 1000)
284
285        event = vt_map.ScriptedEvent.Create("Olivia lights the oil lamp", "oil_lamp_light", "")
286        event:AddEventLinkAtEnd("Olivia looks at Bronann", 1000)
287        event:AddEventLinkAtEnd("Olivia goes near the fountain", 2000)
288
289        event = vt_map.PathMoveSpriteEvent.Create("Olivia goes near the fountain", olivia, 10, 14, false)
290        event:AddEventLinkAtEnd("Olivia looks at Bronann")
291        event:AddEventLinkAtEnd("Olivia tells Bronann to come in the undergrounds", 1000)
292
293        dialogue = vt_map.SpriteDialogue.Create()
294        text = vt_system.Translate("You can come, Bronann.")
295        dialogue:AddLine(text, olivia)
296        event = vt_map.DialogueEvent.Create("Olivia tells Bronann to come in the undergrounds", dialogue)
297        event:AddEventLinkAtEnd("Bronann goes near Olivia")
298
299        event = vt_map.PathMoveSpriteEvent.Create("Bronann goes near Olivia", bronann, 12, 14, false)
300        event:AddEventLinkAtEnd("Bronann looks at Olivia")
301        event:AddEventLinkAtEnd("Olivia tells Bronann about the undergrounds", 1000)
302
303        dialogue = vt_map.SpriteDialogue.Create()
304        text = vt_system.Translate("This is one of the many secrets of this village.")
305        dialogue:AddLine(text, olivia)
306        text = vt_system.Translate("This village was once a sanctuary and many people lived around here. Surprising, eh?")
307        dialogue:AddLineEvent(text, olivia, "", "Bronann looks south")
308        text = vt_system.Translate("Your father told me you were now ready.")
309        dialogue:AddLineEvent(text, olivia, "", "Bronann looks east")
310        text = vt_system.Translate("Huh? Ready for what?")
311        dialogue:AddLineEmote(text, bronann, "exclamation")
312        text = vt_system.Translate("For your first true trial.")
313        dialogue:AddLineEvent(text, olivia, "Olivia looks at Bronann", "")
314        text = vt_system.Translate("I know I will sound harsh Bronann, but we've been having issues with rats in the well recently.")
315        dialogue:AddLineEvent(text, olivia, "Bronann looks at Olivia", "")
316        text = vt_system.Translate("You're now going to go deep down this cave and chase them all before they start polluting our water.")
317        dialogue:AddLine(text, olivia)
318        text = vt_system.Translate("All by yourself.")
319        dialogue:AddLine(text, olivia)
320        text = vt_system.Translate("I'll stay around but I won't participate. This... will prepare you for the days to come.")
321        dialogue:AddLineEmote(text, olivia, "thinking dots")
322        text = vt_system.Translate("Take this lamp, your father gave it to me earlier. You can keep it.")
323        dialogue:AddLineEvent(text, olivia, "Move lamp near Bronann", "")
324
325        event = vt_map.ScriptedEvent.Create("Move lamp near Bronann", "move_lamp_to_bronann_start", "move_lamp_to_bronann_update")
326
327        text = vt_system.Translate("You can use the fountain here to restore your health, but you are to complete this before going out again. Not too afraid?")
328        dialogue:AddLine(text, olivia)
329        text = vt_system.Translate("I'm completely panicking, Olivia. Why now?")
330        dialogue:AddLineEmote(text, bronann, "exclamation")
331        text = vt_system.Translate("It will be fine.")
332        dialogue:AddLine(text, olivia)
333        event = vt_map.DialogueEvent.Create("Olivia tells Bronann about the undergrounds", dialogue)
334        event:AddEventLinkAtEnd("Olivia goes to the entrance and watch")
335
336        event = vt_map.PathMoveSpriteEvent.Create("Olivia goes to the entrance and watch", olivia, 18, 6, false)
337        event:AddEventLinkAtEnd("Olivia looks at Bronann")
338        event:AddEventLinkAtEnd("Olivia tells Bronann to go")
339
340        dialogue = vt_map.SpriteDialogue.Create()
341        text = vt_system.Translate("You can make it.")
342        dialogue:AddLine(text, olivia)
343        event = vt_map.DialogueEvent.Create("Olivia tells Bronann to go", dialogue)
344        event:AddEventLinkAtEnd("Well undergrounds scene end")
345
346        event = vt_map.ScriptedEvent.Create("Well undergrounds scene end", "well_intro_scene_end", "")
347
348        -- Make the whole scene start at map fade in
349        EventManager:StartEvent("Well undergrounds scene start");
350    elseif (GlobalManager:GetGameEvents():DoesEventExist("story", "well_rats_beaten") == false) then
351        -- Should not happen
352    else
353        -- Stick the lamp to Bronann and make it visible
354        lamp_character = bronann
355        oil_lamp:SetVisible(true)
356        lamp_halo:SetVisible(true)
357        lamp_flare:SetVisible(true)
358
359        -- Disable Olivia when the event is done.
360        olivia:SetVisible(false)
361        olivia:SetPosition(0, 0)
362    end
363end
364
365-- zones
366local room_exit_zone = nil
367
368-- Create the different map zones triggering events
369function _CreateZones()
370    -- N.B.: left, right, top, bottom
371    room_exit_zone = vt_map.CameraZone.Create(16, 18, 0, 1)
372end
373
374-- Check whether the active camera has entered a zone. To be called within Update()
375function _CheckZones()
376    if (room_exit_zone:IsCameraEntering() == true) then
377        bronann:SetMoving(false);
378        EventManager:StartEvent("exit floor")
379    end
380end
381
382
383-- Map Custom functions
384-- Used through scripted events
385
386local oil_lamp_move_x_pos = 0
387local oil_lamp_move_y_pos = 0
388local oil_lamp_move_y_pos_end = 0
389local oil_lamp_move_y_pos_end = 0
390
391-- Effect time used when applying the heal light effect
392local heal_effect_time = 0;
393local heal_color = vt_video.Color(0.0, 0.0, 1.0, 1.0);
394
395map_functions = {
396
397    well_intro_scene_start = function()
398        Map:PushState(vt_map.MapMode.STATE_SCENE)
399        bronann:SetMoving(false)
400    end,
401
402    -- make oil lamp appear in front of Olivia
403    oil_lamp_move_start = function()
404        oil_lamp_move_y_pos = olivia:GetYPosition() - 1.0
405        _SetLampPosition(olivia:GetXPosition() + 0.5, oil_lamp_move_y_pos)
406        oil_lamp:SetVisible(true)
407        oil_lamp_move_y_pos_end = oil_lamp_move_y_pos + 0.5
408    end,
409
410    oil_lamp_move_update = function()
411        oil_lamp_move_y_pos = oil_lamp_move_y_pos + SystemManager:GetUpdateTime() / 200
412        _SetLampPosition(olivia:GetXPosition() + 0.5, oil_lamp_move_y_pos)
413        if (oil_lamp_move_y_pos < oil_lamp_move_y_pos_end) then
414            return false
415        end
416        -- Stick the lamp to Olivia
417        lamp_character = olivia
418        return true
419    end,
420
421    oil_lamp_light = function()
422        lamp_flare:SetVisible(true)
423        lamp_halo:SetVisible(true)
424    end,
425
426    move_lamp_to_bronann_start = function()
427        -- unstick lamp from Olivia
428        lamp_character = nil
429        -- Set starting coords
430        oil_lamp_move_x_pos = oil_lamp:GetXPosition()
431        oil_lamp_move_y_pos = oil_lamp:GetYPosition()
432        oil_lamp_move_x_pos_end = oil_lamp_move_x_pos + 0.5
433        oil_lamp_move_y_pos_end = oil_lamp_move_y_pos - 0.5
434    end,
435
436    move_lamp_to_bronann_update = function()
437        local move_update = SystemManager:GetUpdateTime() / 200
438        _SetLampPosition(oil_lamp_move_x_pos, oil_lamp_move_y_pos)
439        if (oil_lamp_move_y_pos > oil_lamp_move_y_pos_end) then
440            oil_lamp_move_y_pos = oil_lamp_move_y_pos - move_update
441            return false
442        end
443        if (oil_lamp_move_x_pos < oil_lamp_move_x_pos_end) then
444            oil_lamp_move_x_pos = oil_lamp_move_x_pos + move_update
445            return false
446        end
447        -- Stick the lamp to Bronann
448        lamp_character = bronann
449        return true
450    end,
451
452    well_intro_scene_end = function()
453        -- Set new Olivia dialogue
454        olivia:ClearDialogueReferences()
455        local dialogue = vt_map.SpriteDialogue.Create()
456        local text = vt_system.Translate("Bronann. Sorry, you have to complete this first.")
457        dialogue:AddLine(text, olivia)
458        text = vt_system.Translate("You can use the fountain here to restore your health.")
459        dialogue:AddLine(text, olivia)
460        olivia:AddDialogueReference(dialogue)
461        -- Set intro event as done
462        GlobalManager:GetGameEvents():SetEventValue("story", "well_intro_event_done", 1)
463        Map:PopState()
464    end,
465
466    heal_party = function()
467        hero:SetMoving(false);
468        -- Should be sufficient to heal anybody
469        local character_handler = GlobalManager:GetCharacterHandler()
470        character_handler:GetActiveParty():AddHitPoints(10000)
471        character_handler:GetActiveParty():AddSkillPoints(10000)
472        Map:SetStamina(10000)
473        Map:RemoveNegativeActiveStatusEffects()
474        AudioManager:PlaySound("data/sounds/heal_spell.wav");
475        heal_effect:SetPosition(hero:GetXPosition(), hero:GetYPosition());
476        heal_effect:Start();
477        heal_effect_time = 0;
478    end,
479
480    heal_done = function()
481        heal_effect_time = heal_effect_time + SystemManager:GetUpdateTime();
482
483        if (heal_effect_time < 300.0) then
484            heal_color:SetAlpha(heal_effect_time / 300.0 / 3.0);
485            Map:GetEffectSupervisor():EnableLightingOverlay(heal_color);
486            return false;
487        end
488
489        if (heal_effect_time < 1000.0) then
490            heal_color:SetAlpha(((1000.0 - heal_effect_time) / 700.0) / 3.0);
491            Map:GetEffectSupervisor():EnableLightingOverlay(heal_color);
492            return false;
493        end
494        return true;
495    end,
496
497    make_olivia_invisible = function()
498        olivia:SetVisible(false)
499        olivia:SetPosition(0, 0)
500    end,
501}
502