1// SuperTux
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program. If not, see <http://www.gnu.org/licenses/>.
15
16#include "badguy/ghoul.hpp"
17
18#include "object/player.hpp"
19#include "editor/editor.hpp"
20#include "sprite/sprite.hpp"
21#include "supertux/game_session.hpp"
22#include "supertux/sector.hpp"
23#include "util/reader_mapping.hpp"
24#include "util/writer.hpp"
25
26static const float FLYSPEED = 80.0f; /**< speed in px per second */
27static const float TRACK_RANGE = 2500.0f; /**< at what distance to start tracking the player */
28
29Ghoul::Ghoul(const ReaderMapping& reader) :
30 BadGuy(reader, "images/creatures/ghoul/ghoul.sprite"),
31 PathObject(),
32 m_mystate(STATE_IDLE),
33 m_flyspeed(),
34 m_track_range()
35{
36 reader.get("flyspeed", m_flyspeed, FLYSPEED);
37 reader.get("track-range", m_track_range, TRACK_RANGE);
38
39 bool running;
40 reader.get("running", running, false);
41
42 init_path(reader, running);
43
44 m_sprite->set_action(m_dir == Direction::LEFT ? "left" : "right", /* loops = */ -1);
45}
46
47bool
48Ghoul::collision_squished(GameObject& object)
49{
50 auto player = Sector::get().get_nearest_player(m_col.m_bbox);
51 if (player)
52 player->bounce (*this);
53 m_sprite->set_action("squished", 1);
54 kill_fall();
55 return true;
56}
57
58bool
59Ghoul::is_freezable() const
60{
61 return false;
62}
63
64bool
65Ghoul::is_flammable() const
66{
67 return false;
68}
69
70void
71Ghoul::finish_construction()
72{
73 if (get_walker() && get_walker()->is_running()) {
74 m_mystate = STATE_PATHMOVING_TRACK;
75 }
76}
77
78void
79Ghoul::activate()
80{
81 if (Editor::is_active())
82 return;
83}
84
85void
86Ghoul::deactivate()
87{
88 switch (m_mystate) {
89 case STATE_TRACKING:
90 m_mystate = STATE_IDLE;
91 break;
92 default:
93 break;
94 }
95}
96
97void
98Ghoul::active_update(float dt_sec)
99{
100 if (Editor::is_active() && get_path() && get_path()->is_valid()) {
101 get_walker()->update(dt_sec);
102 set_pos(get_walker()->get_pos());
103 return;
104 }
105
106 auto player = get_nearest_player();
107 if (!player)
108 return;
109 Vector p1 = m_col.m_bbox.get_middle();
110 Vector p2 = player->get_bbox().get_middle();
111 Vector dist = (p2 - p1);
112
113 const Rectf& player_bbox = player->get_bbox();
114
115 if (player_bbox.get_right() < m_col.m_bbox.get_left()) {
116 m_sprite->set_action("left", -1);
117 }
118
119 if (player_bbox.get_left() > m_col.m_bbox.get_right()) {
120 m_sprite->set_action("right", -1);
121 }
122
123 switch (m_mystate) {
124 case STATE_STOPPED:
125 break;
126
127 case STATE_IDLE:
128 if (dist.norm() <= m_track_range) {
129 m_mystate = STATE_TRACKING;
130 }
131 break;
132
133 case STATE_TRACKING:
134 if (dist.norm() >= 1) {
135 Vector dir_ = dist.unit();
136 m_col.m_movement = dir_ * dt_sec * m_flyspeed;
137 } else {
138 /* We somehow landed right on top of the player without colliding.
139 * Sit tight and avoid a division by zero. */
140 }
141 break;
142
143 case STATE_PATHMOVING:
144 case STATE_PATHMOVING_TRACK:
145 if (get_walker() == nullptr)
146 return;
147 get_walker()->update(dt_sec);
148 m_col.m_movement = get_walker()->get_pos() - get_pos();
149 if (m_mystate == STATE_PATHMOVING_TRACK && dist.norm() <= m_track_range) {
150 m_mystate = STATE_TRACKING;
151 }
152 break;
153
154 default:
155 assert(false);
156 }
157}
158
159void
160Ghoul::goto_node(int node_no)
161{
162 get_walker()->goto_node(node_no);
163 if (m_mystate != STATE_PATHMOVING && m_mystate != STATE_PATHMOVING_TRACK) {
164 m_mystate = STATE_PATHMOVING;
165 }
166}
167
168void
169Ghoul::start_moving()
170{
171 get_walker()->start_moving();
172}
173
174void
175Ghoul::stop_moving()
176{
177 get_walker()->stop_moving();
178}
179
180void
181Ghoul::set_state(const std::string& new_state)
182{
183 if (new_state == "stopped") {
184 m_mystate = STATE_STOPPED;
185 } else if (new_state == "idle") {
186 m_mystate = STATE_IDLE;
187 } else if (new_state == "move_path") {
188 m_mystate = STATE_PATHMOVING;
189 get_walker()->start_moving();
190 } else if (new_state == "move_path_track") {
191 m_mystate = STATE_PATHMOVING_TRACK;
192 get_walker()->start_moving();
193 } else if (new_state == "normal") {
194 m_mystate = STATE_IDLE;
195 } else {
196 log_warning << "Can't set unknown state '" << new_state << std::endl;
197 }
198}
199
200void
201Ghoul::move_to(const Vector& pos)
202{
203 Vector shift = pos - m_col.m_bbox.p1();
204 if (get_path()) {
205 get_path()->move_by(shift);
206 }
207 set_pos(pos);
208}
209
210/* EOF */
211