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 |
|
26 | static const float FLYSPEED = 80.0f; /**< speed in px per second */
|
27 | static const float TRACK_RANGE = 2500.0f; /**< at what distance to start tracking the player */
|
28 |
|
29 | Ghoul::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 |
|
47 | bool
|
48 | Ghoul::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 |
|
58 | bool
|
59 | Ghoul::is_freezable() const
|
60 | {
|
61 | return false;
|
62 | }
|
63 |
|
64 | bool
|
65 | Ghoul::is_flammable() const
|
66 | {
|
67 | return false;
|
68 | }
|
69 |
|
70 | void
|
71 | Ghoul::finish_construction()
|
72 | {
|
73 | if (get_walker() && get_walker()->is_running()) {
|
74 | m_mystate = STATE_PATHMOVING_TRACK;
|
75 | }
|
76 | }
|
77 |
|
78 | void
|
79 | Ghoul::activate()
|
80 | {
|
81 | if (Editor::is_active())
|
82 | return;
|
83 | }
|
84 |
|
85 | void
|
86 | Ghoul::deactivate()
|
87 | {
|
88 | switch (m_mystate) {
|
89 | case STATE_TRACKING:
|
90 | m_mystate = STATE_IDLE;
|
91 | break;
|
92 | default:
|
93 | break;
|
94 | }
|
95 | }
|
96 |
|
97 | void
|
98 | Ghoul::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 |
|
159 | void
|
160 | Ghoul::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 |
|
168 | void
|
169 | Ghoul::start_moving()
|
170 | {
|
171 | get_walker()->start_moving();
|
172 | }
|
173 |
|
174 | void
|
175 | Ghoul::stop_moving()
|
176 | {
|
177 | get_walker()->stop_moving();
|
178 | }
|
179 |
|
180 | void
|
181 | Ghoul::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 |
|
200 | void
|
201 | Ghoul::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 | |