1-- Initialize hero behavior specific to this quest.
2
3require("scripts/multi_events")
4require("scripts/utils")
5local all = require("scripts/meta/all")
6
7local hero_meta = sol.main.get_metatable("hero")
8
9local function initialize_hero_features(game)
10
11  local hero = game:get_hero()
12  -- In this game you can never die
13  hero:set_invincible()
14  hero:set_walking_speed(80)
15  -- Set max level of stamina
16  game:set_max_magic(20)
17
18  function silence_music()
19    -- silence music for brandish SFX
20    local volume = sol.audio.get_music_volume()
21    sol.audio.set_music_volume(0)
22    log("Music volume: 0")
23    local timer = sol.timer.start(2600, function()
24      log("Music volume: 100")
25      sol.audio.set_music_volume(volume)
26    end)
27    timer:set_suspended_with_map(false)
28  end
29
30  -- The hero can only swing the sword, nothing else
31  function hero:on_state_changed(state)
32    if state == "sword loading"
33    or state == "sword tapping"
34    or state == "sword spin attack" then
35      hero:freeze()
36      hero:unfreeze()
37    end
38  end
39
40  -- Return the hero's position relative to the screen.
41  function hero:get_screen_position()
42    local camera = self:get_map():get_camera()
43    local hero_x, hero_y = self:get_position()
44    local camera_x, camera_y = camera:get_position()
45    return hero_x-camera_x, hero_y-camera_y
46  end
47
48  -- Tracks the position of the hero and updates the callback every frame
49  function hero:track(callback)
50    hero:register_event("on_position_changed", function(self, x, y, z)
51      callback(x, y, z)
52    end)
53  end
54
55  -- The hero will brandish an item without obtaining it.
56  -- Reimplementation of hero:start_treasure() except you don't get the item.
57  function hero:brandish(item_key, variant)
58    self:freeze()
59    silence_music()
60    sol.audio.play_sound("treasure")
61
62    -- Set some variables
63    if not variant then variant = 1 end
64    local x, y, z = self:get_position()
65    local item = hero:get_game():get_item(item_key)
66    assert(item)
67
68    -- Set the hero's sprite
69    local hero_sprite = hero:get_sprite()
70    hero_sprite:set_animation("brandish")
71
72    -- Create a map entity for the item to show during brandishing
73    local map = item:get_map()
74    local item_entity = map:create_custom_entity({
75      sprite = "entities/items",
76      direction = variant - 1,
77      x=0, y=0, layer=0, width=16, height=16
78    })
79    local item_sprite = item_entity:get_sprite()
80    item_sprite:set_animation(item_key)
81    local ox, oy = item_sprite:get_origin()
82    item_entity:set_position(x, y-28+oy, z+1)
83
84    -- Start item dialog
85    local game = item:get_game()
86    game:start_dialog("_treasure." .. item_key .. "." .. variant, function()
87      -- End
88      self:unfreeze()
89      hero_sprite:set_animation("stopped")
90      item_entity:remove()
91    end)
92  end
93
94  -- Make the hero blink and update
95  function hero:on_created()
96    -- Make the hero blink her eyes every 6 seconds
97    sol.timer.start(self, 6000, function()
98
99      local sprite = self:get_sprite()
100      -- Blink conditions
101      local is_stopped = sprite:get_animation() == "stopped"
102      local is_climbing = self:get_tunic_sprite_id() == "hero/tunic1-climbing"
103
104      if is_stopped and not is_climbing then
105        sprite:set_animation("blink", "stopped")
106      end
107
108      return true  -- loop forever
109    end)
110
111    -- Make the hero be updated every 10 ms
112    sol.timer.start(self, 10, function()
113      self:update()
114      return true
115    end)
116  end
117
118  function hero:on_state_changed(new_state_name)
119    if new_state_name == "treasure" then
120      local item_star = self:create_sprite("menus/item_star", "item_star")
121      item_star:set_ignore_suspend()
122      self:bring_sprite_to_back(item_star)
123
124      -- HACK: Layer the tunic sprite on again because
125      -- setting the sprite order doesn't affect the hero
126      -- https://gitlab.com/solarus-games/solarus/issues/1348#note_142442035
127      local tunic_sprite = hero:get_sprite("tunic")
128      local tunic_top = hero:create_sprite(tunic_sprite:get_animation_set(), "tunic_top")
129      tunic_top:set_direction(tunic_sprite:get_direction())
130      tunic_top:set_animation("brandish")
131      -- END HACK
132
133      force_animation(item_star, true)
134      silence_music()
135    end
136  end
137
138  function hero:on_state_changing(state_name, next_state_name)
139    local item_star = self:get_sprite("item_star")
140    local tunic_top = self:get_sprite("tunic_top") -- HACK
141    if state_name == "treasure" then
142      if item_star then self:remove_sprite(item_star) end
143      if tunic_top then self:remove_sprite(tunic_top) end -- HACK
144    end
145  end
146
147  -- Rachel has a thought bubble animation
148  function hero:think()
149    all.think(self)
150    force_animation(self:get_sprite("thought_bubble"))
151  end
152
153  -- Check if Rachel changed its ground below
154  function hero:update()
155    local current_ground = hero:get_ground_below()
156    if self.previous_ground and self.previous_ground ~= current_ground then
157      self:on_ground_below_changed(current_ground)
158    end
159    self.previous_ground = current_ground
160  end
161
162  -- Make Rachel change its tunic if she is climbing
163  function hero:on_ground_below_changed(ground_below)
164    if ground_below == "ladder" then
165      self:set_tunic_sprite_id("hero/tunic1-climbing")
166    else
167      self:set_tunic_sprite_id("hero/tunic1")
168    end
169  end
170end
171
172-- Set up Eldran hero sprite on any game that starts.
173local game_meta = sol.main.get_metatable("game")
174game_meta:register_event("on_started", initialize_hero_features)
175return true
176