| 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 | |