1177 lines
44 KiB
C++
1177 lines
44 KiB
C++
//
|
|
// SuperTuxKart - a fun racing game with go-kart
|
|
// Copyright (C) 2004-2015 Steve Baker <sjbaker1@airmail.net>
|
|
// Copyright (C) 2006-2015 Joerg Henrichs, SuperTuxKart-Team, Steve Baker
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 3
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
#include "states_screens/race_gui_base.hpp"
|
|
|
|
#include "audio/music_manager.hpp"
|
|
#include "config/user_config.hpp"
|
|
#include "graphics/2dutils.hpp"
|
|
#include "graphics/camera.hpp"
|
|
#include "graphics/central_settings.hpp"
|
|
#include "graphics/irr_driver.hpp"
|
|
#include "graphics/material.hpp"
|
|
#include "graphics/material_manager.hpp"
|
|
#include "graphics/referee.hpp"
|
|
#include "guiengine/scalable_font.hpp"
|
|
#include "io/file_manager.hpp"
|
|
#include "items/attachment_manager.hpp"
|
|
#include "items/powerup.hpp"
|
|
#include "karts/abstract_kart.hpp"
|
|
#include "karts/abstract_kart_animation.hpp"
|
|
#include "karts/controller/controller.hpp"
|
|
#include "karts/explosion_animation.hpp"
|
|
#include "karts/kart_properties.hpp"
|
|
#include "karts/kart_properties_manager.hpp"
|
|
#include "karts/rescue_animation.hpp"
|
|
#include "modes/capture_the_flag.hpp"
|
|
#include "modes/linear_world.hpp"
|
|
#include "modes/world.hpp"
|
|
#include "network/network_config.hpp"
|
|
#include "states_screens/race_gui_multitouch.hpp"
|
|
#include "tracks/track.hpp"
|
|
#include "utils/constants.hpp"
|
|
|
|
#include <ICameraSceneNode.h>
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
extern bool useCoreContext;
|
|
}
|
|
}
|
|
|
|
RaceGUIBase::RaceGUIBase()
|
|
{
|
|
m_ignore_unimportant_messages = false;
|
|
m_max_font_height = GUIEngine::getFontHeight() + 10;
|
|
m_small_font_max_height = GUIEngine::getSmallFontHeight() + 5;
|
|
//I18N: as in "ready, set, go", shown at the beginning of the race
|
|
m_string_ready = _("Ready!");
|
|
//I18N: as in "ready, set, go", shown at the beginning of the race
|
|
m_string_set = _("Set!");
|
|
//I18N: as in "ready, set, go", shown at the beginning of the race
|
|
m_string_go = _("Go!");
|
|
//I18N: Shown when a goal is scored
|
|
m_string_goal = _("GOAL!");
|
|
// I18N: Shown waiting for other players in network to finish loading or
|
|
// waiting
|
|
m_string_waiting_for_others = _("Waiting for others");
|
|
|
|
m_music_icon = irr_driver->getTexture("notes.png");
|
|
if (!m_music_icon)
|
|
{
|
|
Log::error("RaceGuiBase", "Can't find 'notes.png' texture, aborting.");
|
|
}
|
|
|
|
m_plunger_face = irr_driver->getTexture("plungerface.png");
|
|
if (!m_plunger_face)
|
|
{
|
|
Log::error("RaceGuiBase",
|
|
"Can't find 'plungerface.png' texture, aborting.");
|
|
}
|
|
|
|
//read frame picture for icons in the mini map.
|
|
m_icons_frame = irr_driver->getTexture("icons-frame.png");
|
|
if (!m_icons_frame)
|
|
{
|
|
Log::error("RaceGuiBase",
|
|
"Can't find 'icons-frame.png' texture, aborting.");
|
|
}
|
|
|
|
m_gauge_full = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_full.png");
|
|
m_gauge_full_bright = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_full_bright.png");
|
|
m_gauge_empty = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_empty.png");
|
|
m_gauge_goal = irr_driver->getTexture(FileManager::GUI_ICON, "gauge_goal.png");
|
|
m_lap_flag = irr_driver->getTexture(FileManager::GUI_ICON, "lap_flag.png");
|
|
m_dist_show_overlap = 2;
|
|
m_icons_inertia = 2;
|
|
|
|
m_referee = NULL;
|
|
m_multitouch_gui = NULL;
|
|
} // RaceGUIBase
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** This is a second initialisation call (after the constructor) for the race
|
|
* gui. This is called after the world has been initialised, e.g. all karts
|
|
* do exist (while the constructor is called before the karts are created).
|
|
* In the base gui this is used to initialise the referee data (which needs
|
|
* the start positions of the karts). Note that this function is (and must
|
|
* be called only once, after its constructor).
|
|
* \pre All karts must be created, since this object defines the
|
|
* positions for the referees based on the karts.
|
|
*/
|
|
void RaceGUIBase::init()
|
|
{
|
|
m_kart_display_infos.resize(race_manager->getNumberOfKarts());
|
|
|
|
// Do everything else required at a race restart as well, esp.
|
|
// resetting the height of the referee.
|
|
m_referee = new Referee();
|
|
m_referee_pos.resize(race_manager->getNumberOfKarts());
|
|
m_referee_rotation.resize(race_manager->getNumberOfKarts());
|
|
} // init
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** This is called when restarting a race. In the race gui base it resets
|
|
* height of the referee, so that it can start flying down again.
|
|
*/
|
|
void RaceGUIBase::reset()
|
|
{
|
|
// While we actually only need the positions for local players,
|
|
// we add all karts, since it's easier to get a world kart id from
|
|
// the kart then the local player id (and it avoids problems in
|
|
// profile mode where there might be a camera, but no player).
|
|
for(unsigned int i=0; i<race_manager->getNumberOfKarts(); i++)
|
|
{
|
|
const AbstractKart *kart = World::getWorld()->getKart(i);
|
|
m_referee_pos[i] = kart->getTrans()(Referee::getStartOffset());
|
|
Vec3 hpr;
|
|
btQuaternion q = btQuaternion(kart->getTrans().getBasis().getColumn(1),
|
|
Referee::getStartRotation().getY() * DEGREE_TO_RAD) *
|
|
kart->getTrans().getRotation();
|
|
hpr.setHPR(q);
|
|
m_referee_rotation[i] = hpr.toIrrHPR();
|
|
}
|
|
|
|
m_referee_height = 10.0f;
|
|
m_referee->attachToSceneNode();
|
|
m_referee->selectReadySetGo(0); // set red color
|
|
m_plunger_move_time = 0;
|
|
m_plunger_offset = core::vector2di(0,0);
|
|
m_plunger_speed = core::vector2df(0,0);
|
|
m_plunger_state = PLUNGER_STATE_INIT;
|
|
clearAllMessages();
|
|
|
|
if (m_multitouch_gui != NULL)
|
|
{
|
|
m_multitouch_gui->reset();
|
|
}
|
|
} // reset
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** The destructor removes the marker texture and the referee scene node.
|
|
*/
|
|
RaceGUIBase::~RaceGUIBase()
|
|
{
|
|
//irr_driver->removeTexture(m_marker);
|
|
|
|
// If the referee is currently being shown,
|
|
// remove it from the scene graph.
|
|
delete m_referee;
|
|
} // ~RaceGUIBase
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Creates the 2D vertices for a regular polygon. Adopted from Irrlicht.
|
|
* \param n Number of vertices to use.
|
|
* \param radius Radius of the polygon.
|
|
* \param center The center point of the polygon.
|
|
* \param v Pointer to the array of vertices.
|
|
*/
|
|
void RaceGUIBase::createRegularPolygon(unsigned int n, float radius,
|
|
const core::vector2df ¢er,
|
|
const video::SColor &color,
|
|
video::S3DVertex *v,
|
|
unsigned short int *index)
|
|
{
|
|
float f = 2*M_PI/(float)n;
|
|
for (unsigned int i=0; i<n; i++)
|
|
{
|
|
float p = i*f;
|
|
core::vector2df X = center + core::vector2df( sin(p)*radius,
|
|
-cos(p)*radius);
|
|
v[i].Pos.X = X.X;
|
|
v[i].Pos.Y = X.Y;
|
|
v[i].Color = color;
|
|
index[i] = i;
|
|
}
|
|
} // createRegularPolygon
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Displays all messages in the message queue
|
|
**/
|
|
void RaceGUIBase::drawAllMessages(const AbstractKart* kart,
|
|
const core::recti &viewport,
|
|
const core::vector2df &scaling)
|
|
{
|
|
int y = viewport.LowerRightCorner.Y - m_small_font_max_height - 10;
|
|
|
|
const int x = viewport.getCenter().X;
|
|
const int w = viewport.getWidth();
|
|
|
|
// Draw less important messages first, at the very bottom of the screen
|
|
// unimportant messages are skipped in multiplayer, they take too much screen space
|
|
if (race_manager->getNumLocalPlayers() < 2 &&
|
|
!m_ignore_unimportant_messages)
|
|
{
|
|
for (AllMessageType::const_iterator i = m_messages.begin();
|
|
i != m_messages.end(); ++i)
|
|
{
|
|
TimedMessage const &msg = *i;
|
|
if (!msg.m_important)
|
|
{
|
|
// Display only messages for all karts, or messages for this kart
|
|
if (msg.m_kart && msg.m_kart!=kart) continue;
|
|
|
|
core::rect<s32> pos(x - w/2, y, x + w/2, y + m_max_font_height);
|
|
|
|
gui::ScalableFont* font = GUIEngine::getSmallFont();
|
|
|
|
if (msg.m_outline)
|
|
font->setBlackBorder(true);
|
|
|
|
font->draw(
|
|
core::stringw(msg.m_message.c_str()).c_str(),
|
|
pos, msg.m_color, true /* hcenter */, true /* vcenter */);
|
|
|
|
if (msg.m_outline)
|
|
font->setBlackBorder(false);
|
|
|
|
y -= m_small_font_max_height;
|
|
}
|
|
}
|
|
}
|
|
|
|
// First line of text somewhat under the top of the viewport.
|
|
y = viewport.UpperLeftCorner.Y +
|
|
(viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y)/4;
|
|
|
|
gui::ScalableFont* font = GUIEngine::getFont();
|
|
gui::ScalableFont* big_font = GUIEngine::getTitleFont();
|
|
|
|
int font_height = m_max_font_height;
|
|
if (race_manager->getNumLocalPlayers() > 2)
|
|
{
|
|
font = GUIEngine::getSmallFont();
|
|
}
|
|
|
|
irr_driver->getVideoDriver()->enableMaterial2D(); // seems like we need to remind irrlicht from time to time to use the Material2D
|
|
|
|
// The message are displayed in reverse order, so that a multi-line
|
|
// message (addMessage("1", ...); addMessage("2",...) is displayed
|
|
// in the right order: "1" on top of "2"
|
|
for (AllMessageType::const_iterator i = m_messages.begin();
|
|
i != m_messages.end(); ++i)
|
|
{
|
|
TimedMessage const &msg = *i;
|
|
|
|
// less important messages were already displayed
|
|
if (!msg.m_important) continue;
|
|
|
|
// Display only messages for all karts, or messages for this kart
|
|
if (msg.m_kart && msg.m_kart!=kart) continue;
|
|
|
|
core::rect<s32> pos(x - w/2, y, x + w/2, y + font_height);
|
|
|
|
if (msg.m_big_font)
|
|
{
|
|
big_font->draw(core::stringw(msg.m_message.c_str()).c_str(),
|
|
pos, msg.m_color, true /* hcenter */,
|
|
true /* vcenter */);
|
|
y += GUIEngine::getTitleFontHeight();
|
|
}
|
|
else
|
|
{
|
|
if (msg.m_outline)
|
|
font->setBlackBorder(true);
|
|
|
|
font->draw(core::stringw(msg.m_message.c_str()).c_str(),
|
|
pos, msg.m_color, true /* hcenter */,
|
|
true /* vcenter */);
|
|
|
|
if (msg.m_outline)
|
|
font->setBlackBorder(false);
|
|
|
|
y += font_height;
|
|
}
|
|
} // for i in all messages
|
|
} // drawAllMessages
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Removes messages which have been displayed long enough. This function
|
|
* must be called after drawAllMessages, otherwise messages which are only
|
|
* displayed once will not be drawn!
|
|
*/
|
|
void RaceGUIBase::cleanupMessages(const float dt)
|
|
{
|
|
AllMessageType::iterator p =m_messages.begin();
|
|
while(p!=m_messages.end())
|
|
{
|
|
if((*p).done(dt))
|
|
{
|
|
p = m_messages.erase(p);
|
|
}
|
|
else
|
|
{
|
|
++p;
|
|
}
|
|
}
|
|
} // cleanupMessages
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Draws the powerup icons on the screen (called once for each player).
|
|
* \param kart The kart for which to draw the powerup icons.
|
|
* \param viewport The viewport into which to draw the icons.
|
|
* \param scaling The scaling to use when drawing the icons.
|
|
*/
|
|
void RaceGUIBase::drawPowerupIcons(const AbstractKart* kart,
|
|
const core::recti &viewport,
|
|
const core::vector2df &scaling)
|
|
{
|
|
#ifndef SERVER_ONLY
|
|
// If player doesn't have any powerups or has completed race, do nothing.
|
|
const Powerup* powerup = kart->getPowerup();
|
|
if (powerup->getType() == PowerupManager::POWERUP_NOTHING
|
|
|| kart->hasFinishedRace()) return;
|
|
|
|
int n = kart->getPowerup()->getNum() ;
|
|
int many_powerups = 0;
|
|
if (n<1) return; // shouldn't happen, but just in case
|
|
if (n>5)
|
|
{
|
|
many_powerups = n;
|
|
n = 1;
|
|
}
|
|
|
|
float scale = (float)(std::min(scaling.X, scaling.Y));
|
|
|
|
int nSize = (int)(64.0f * scale);
|
|
|
|
int itemSpacing = (int)(scale * 32.0f);
|
|
|
|
int x1, y1;
|
|
|
|
// When there is not much height, move items on the side
|
|
if ((float) viewport.getWidth() / (float) viewport.getHeight() > 2.0f)
|
|
{
|
|
x1 = viewport.UpperLeftCorner.X + 3*(viewport.getWidth()/4)
|
|
- ((n * itemSpacing)/2);
|
|
}
|
|
else
|
|
{
|
|
x1 = viewport.UpperLeftCorner.X + (viewport.getWidth()/2)
|
|
- ((n * itemSpacing)/2);
|
|
}
|
|
|
|
// When the viewport is smaller in splitscreen, reduce the top margin
|
|
if ((race_manager->getNumLocalPlayers() == 2 &&
|
|
viewport.getWidth() > viewport.getHeight()) ||
|
|
race_manager->getNumLocalPlayers() >= 3 )
|
|
y1 = viewport.UpperLeftCorner.Y + (int)(5 * scaling.Y);
|
|
else
|
|
y1 = viewport.UpperLeftCorner.Y + (int)(20 * scaling.Y);
|
|
|
|
int x2 = 0;
|
|
|
|
assert(powerup != NULL);
|
|
assert(powerup->getIcon() != NULL);
|
|
video::ITexture *t=powerup->getIcon()->getTexture();
|
|
assert(t != NULL);
|
|
core::rect<s32> rect(core::position2di(0, 0), t->getSize());
|
|
|
|
for ( int i = 0 ; i < n ; i++ )
|
|
{
|
|
x2 = (int)((x1+i*itemSpacing) - (itemSpacing / 2));
|
|
core::rect<s32> pos(x2, y1, x2+nSize, y1+nSize);
|
|
draw2DImage(t, pos, rect, NULL,
|
|
NULL, true);
|
|
} // for i
|
|
|
|
if (many_powerups > 0)
|
|
{
|
|
gui::ScalableFont* font = GUIEngine::getHighresDigitFont();
|
|
core::rect<s32> pos(x2+nSize, y1, x2+nSize+nSize, y1+nSize);
|
|
font->setScale(scale);
|
|
font->draw(core::stringw(L"x")+StringUtils::toWString(many_powerups),
|
|
pos, video::SColor(255, 255, 255, 255));
|
|
font->setScale(1.0f);
|
|
}
|
|
#endif
|
|
} // drawPowerupIcons
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Updates lightning related information.
|
|
*/
|
|
void RaceGUIBase::renderGlobal(float dt)
|
|
{
|
|
|
|
} // renderGlobal
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** Update, called once per frame. This updates the height of the referee
|
|
* (to create a flying down animation).
|
|
* \param dt Time step size.
|
|
*/
|
|
void RaceGUIBase::update(float dt)
|
|
{
|
|
// E.g. a race result gui does not have a referee
|
|
if(m_referee)
|
|
{
|
|
World *world = World::getWorld();
|
|
// During GO move the referee up again
|
|
if(world->getPhase()==World::SETUP_PHASE)
|
|
{
|
|
m_referee_height = 10.0f;
|
|
m_referee->selectReadySetGo(0); // set red color
|
|
}
|
|
else if(world->getPhase()==World::GO_PHASE)
|
|
{
|
|
m_referee_height += dt*5.0f;
|
|
m_referee->selectReadySetGo(2);
|
|
}
|
|
else if (world->getPhase()==World::WAIT_FOR_SERVER_PHASE ||
|
|
(NetworkConfig::get()->isNetworking() &&
|
|
world->getPhase()==World::TRACK_INTRO_PHASE))
|
|
{
|
|
}
|
|
else if ((!NetworkConfig::get()->isNetworking() &&
|
|
world->getPhase()==World::TRACK_INTRO_PHASE) ||
|
|
(NetworkConfig::get()->isNetworking() &&
|
|
world->getPhase()==World::SERVER_READY_PHASE))
|
|
{
|
|
m_referee->selectReadySetGo(0); // set red color
|
|
m_referee_height -= dt*5.0f;
|
|
if(m_referee_height<0)
|
|
m_referee_height = 0;
|
|
}
|
|
else if(world->isStartPhase()) // must be ready or set now
|
|
{
|
|
m_referee_height = 0;
|
|
m_referee->selectReadySetGo(world->getPhase()==World::SET_PHASE
|
|
? 1 : 0);
|
|
}
|
|
else if(world->getPhase()==World::IN_GAME_MENU_PHASE)
|
|
{
|
|
// Don't do anything, without this the next clause
|
|
// would completely remove thunderbird.
|
|
}
|
|
else if(m_referee->isAttached()) // race phase:
|
|
{
|
|
m_referee->removeFromSceneGraph();
|
|
}
|
|
} // if referee node
|
|
} // update
|
|
|
|
// ----------------------------------------------------------------------------
|
|
/** This function is called just before rendering the view for each kart. This
|
|
* is used here to display the referee during the ready-set-go phase.
|
|
* \param kart The kart whose view is rendered next.
|
|
*/
|
|
void RaceGUIBase::preRenderCallback(const Camera *camera)
|
|
{
|
|
if(m_referee && camera->getKart())
|
|
{
|
|
unsigned int world_id = camera->getKart()->getWorldKartId();
|
|
Vec3 xyz = m_referee_pos[world_id] +
|
|
camera->getKart()->getNormal() * m_referee_height;
|
|
m_referee->setPosition(xyz);
|
|
m_referee->setRotation(m_referee_rotation[world_id]);
|
|
}
|
|
} // preRenderCallback
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void RaceGUIBase::renderPlayerView(const Camera *camera, float dt)
|
|
{
|
|
const core::recti &viewport = camera->getViewport();
|
|
const core::vector2df scaling = camera->getScaling();
|
|
const AbstractKart* kart = camera->getKart();
|
|
if(!kart) return;
|
|
|
|
if (m_multitouch_gui != NULL)
|
|
{
|
|
m_multitouch_gui->draw(kart, viewport, scaling);
|
|
}
|
|
} // renderPlayerView
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Adds a message to the message queue. The message is displayed for a
|
|
* certain amount of time (unless time<0, then the message is displayed
|
|
* once).
|
|
*/
|
|
void RaceGUIBase::addMessage(const core::stringw &msg,
|
|
const AbstractKart *kart,
|
|
float time, const video::SColor &color,
|
|
bool important, bool big_font, bool outline)
|
|
{
|
|
m_messages.push_back(TimedMessage(msg, kart, time, color, important, big_font, outline));
|
|
} // addMessage
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Displays the description given for the music currently being played.
|
|
* This is usually the title and composer.
|
|
*/
|
|
void RaceGUIBase::drawGlobalMusicDescription()
|
|
{
|
|
#ifndef SERVER_ONLY
|
|
// show no music description when it's off
|
|
if (!UserConfigParams::m_music) return;
|
|
|
|
gui::IGUIFont* font = GUIEngine::getFont();
|
|
|
|
float race_time =
|
|
stk_config->ticks2Time(World::getWorld()->getTicksSinceStart());
|
|
|
|
// ---- Manage pulsing effect
|
|
// 3.0 is the duration of ready/set (TODO: don't hardcode)
|
|
float timeProgression = (float)(race_time) /
|
|
(float)(stk_config->m_music_credit_time - 2.0f);
|
|
|
|
const int x_pulse = (int)(sin(race_time*9.0f)*10.0f);
|
|
const int y_pulse = (int)(cos(race_time*9.0f)*10.0f);
|
|
|
|
float resize = 1.0f;
|
|
if (timeProgression < 0.1)
|
|
{
|
|
resize = timeProgression/0.1f;
|
|
}
|
|
else if (timeProgression > 0.9)
|
|
{
|
|
resize = 1.0f - (timeProgression - 0.9f)/0.1f;
|
|
}
|
|
|
|
const float resize3 = resize*resize*resize;
|
|
|
|
// Get song name, and calculate its size, allowing us to position stuff
|
|
const MusicInformation* mi = music_manager->getCurrentMusic();
|
|
if (!mi) return;
|
|
|
|
core::stringw thetext = core::stringw(L"\"") + mi->getTitle() + L"\"";
|
|
|
|
core::dimension2d< u32 > textSize = font->getDimension(thetext.c_str());
|
|
int textWidth = textSize.Width;
|
|
|
|
int textWidth2 = 0;
|
|
core::stringw thetext_composer;
|
|
if (mi->getComposer()!="")
|
|
{
|
|
// I18N: string used to show the author of the music. (e.g. "Sunny Song" by "John Doe")
|
|
thetext_composer = _("by");
|
|
thetext_composer += " ";
|
|
thetext_composer += mi->getComposer().c_str();
|
|
textWidth2 = font->getDimension(thetext_composer.c_str()).Width;
|
|
}
|
|
const int max_text_size = (int)(irr_driver->getActualScreenSize().Width*2.0f/3.0f);
|
|
if (textWidth > max_text_size) textWidth = max_text_size;
|
|
if (textWidth2 > max_text_size) textWidth2 = max_text_size;
|
|
|
|
const int ICON_SIZE = 64;
|
|
const int y = irr_driver->getActualScreenSize().Height - 80;
|
|
// the 20 is an arbitrary space left between the note icon and the text
|
|
const int noteX = (irr_driver->getActualScreenSize().Width / 2)
|
|
- std::max(textWidth, textWidth2)/2 - ICON_SIZE/2 - 20;
|
|
const int noteY = y;
|
|
// the 20 is an arbitrary space left between the note icon and the text
|
|
const int textXFrom = (irr_driver->getActualScreenSize().Width / 2)
|
|
- std::max(textWidth, textWidth2)/2 + 20;
|
|
const int textXTo = (irr_driver->getActualScreenSize().Width / 2)
|
|
+ std::max(textWidth, textWidth2)/2 + 20;
|
|
|
|
// ---- Draw "by" text
|
|
const int text_y = (int)(irr_driver->getActualScreenSize().Height - 80*(resize3)
|
|
+ 40*(1-resize));
|
|
|
|
static const video::SColor white = video::SColor(255, 255, 255, 255);
|
|
if(mi->getComposer()!="")
|
|
{
|
|
core::rect<s32> pos_by(textXFrom, text_y+40,
|
|
textXTo, text_y+40);
|
|
font->draw(thetext_composer, pos_by, white,
|
|
true, true);
|
|
}
|
|
|
|
// ---- Draw "song name" text
|
|
core::rect<s32> pos(textXFrom, text_y,
|
|
textXTo, text_y);
|
|
|
|
font->draw(thetext.c_str(), pos, white, true /* hcenter */,
|
|
true /* vcenter */);
|
|
|
|
// Draw music icon
|
|
if (m_music_icon != NULL)
|
|
{
|
|
int iconSizeX = (int)(ICON_SIZE*resize + x_pulse*resize*resize);
|
|
int iconSizeY = (int)(ICON_SIZE*resize + y_pulse*resize*resize);
|
|
|
|
core::rect<s32> dest(noteX-iconSizeX/2+20,
|
|
noteY-iconSizeY/2+ICON_SIZE/2,
|
|
noteX+iconSizeX/2+20,
|
|
noteY+iconSizeY/2+ICON_SIZE/2);
|
|
const core::rect<s32> source(core::position2d<s32>(0,0),
|
|
m_music_icon->getSize());
|
|
|
|
draw2DImage(m_music_icon, dest, source, NULL, NULL, true);
|
|
}
|
|
#endif
|
|
} // drawGlobalMusicDescription
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void RaceGUIBase::drawGlobalGoal()
|
|
{
|
|
static video::SColor color = video::SColor(255, 255, 255, 255);
|
|
core::rect<s32> pos(irr_driver->getActualScreenSize().Width/2,
|
|
irr_driver->getActualScreenSize().Height/2,
|
|
irr_driver->getActualScreenSize().Width/2,
|
|
irr_driver->getActualScreenSize().Height/2);
|
|
gui::IGUIFont* font = GUIEngine::getTitleFont();
|
|
font->draw(m_string_goal.c_str(), pos, color, true, true);
|
|
}
|
|
// ----------------------------------------------------------------------------
|
|
/** Draws the ready-set-go message on the screen.
|
|
*/
|
|
void RaceGUIBase::drawGlobalReadySetGo()
|
|
{
|
|
// This function is called only in a relevant phase,
|
|
// So we can put common elements here
|
|
|
|
static video::SColor color = video::SColor(255, 255, 255, 255);
|
|
gui::IGUIFont* font = GUIEngine::getTitleFont();
|
|
int x = irr_driver->getActualScreenSize().Width/2;
|
|
int y = irr_driver->getActualScreenSize().Height*2/5;
|
|
core::rect<s32> pos(x,y,x,y);
|
|
|
|
switch (World::getWorld()->getPhase())
|
|
{
|
|
case WorldStatus::WAIT_FOR_SERVER_PHASE:
|
|
{
|
|
font->draw(StringUtils::loadingDots(
|
|
m_string_waiting_for_others.c_str()), pos, color, true, true);
|
|
}
|
|
break;
|
|
case WorldStatus::READY_PHASE:
|
|
{
|
|
font->draw(m_string_ready.c_str(), pos, color, true, true);
|
|
}
|
|
break;
|
|
case WorldStatus::SET_PHASE:
|
|
{
|
|
font->draw(m_string_set.c_str(), pos, color, true, true);
|
|
}
|
|
break;
|
|
case WorldStatus::GO_PHASE:
|
|
{
|
|
if (race_manager->getCoinTarget() > 0)
|
|
font->draw(_("Collect nitro!"), pos, color, true, true);
|
|
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
|
|
font->draw(_("Follow the leader!"), pos, color, true, true);
|
|
else
|
|
font->draw(m_string_go.c_str(), pos, color, true, true);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
} // switch
|
|
} // drawGlobalReadySetGo
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Draw players icons and, depending on the current mode, their time
|
|
* or their score (battle lives, egg collected, etc.).
|
|
*/
|
|
void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
|
|
{
|
|
#ifndef SERVER_ONLY
|
|
// For now, don't draw player icons when in soccer mode
|
|
const RaceManager::MinorRaceModeType minor_mode = race_manager->getMinorMode();
|
|
if(minor_mode == RaceManager::MINOR_MODE_SOCCER)
|
|
return;
|
|
|
|
int x_base = 10;
|
|
int y_base = 25;
|
|
unsigned int y_space = irr_driver->getActualScreenSize().Height - bottom_margin - y_base;
|
|
// Special case : when 3 players play, use 4th window to display such stuff
|
|
if (race_manager->getIfEmptyScreenSpaceExists())
|
|
{
|
|
irr::core::recti Last_Space = irr_driver->getSplitscreenWindow(race_manager->getNumLocalPlayers());
|
|
x_base = Last_Space.UpperLeftCorner.X;
|
|
y_base = Last_Space.UpperLeftCorner.Y;
|
|
y_space = irr_driver->getActualScreenSize().Height - y_base;
|
|
}
|
|
|
|
unsigned int sta = race_manager->getNumSpareTireKarts();
|
|
const unsigned int num_karts = race_manager->getNumberOfKarts() - sta;
|
|
|
|
// -2 because that's the spacing further on
|
|
int ICON_PLAYER_WIDTH = y_space / (num_karts) - 2;
|
|
|
|
int icon_width_max = (int)(60*(irr_driver->getActualScreenSize().Width/1024.0f));
|
|
int icon_width_min = (int)(35*((irr_driver->getActualScreenSize().Height - (y_base+10))/720.0f));
|
|
if (icon_width_min < 35) icon_width_min = 35;
|
|
if (icon_width_min > icon_width_max)
|
|
{
|
|
int icon_width_tmp = icon_width_max;
|
|
icon_width_max = icon_width_min;
|
|
icon_width_min = icon_width_tmp;
|
|
}
|
|
|
|
// Make sure it fits within our boundaries
|
|
if (ICON_PLAYER_WIDTH > icon_width_max) ICON_PLAYER_WIDTH = icon_width_max;
|
|
if (ICON_PLAYER_WIDTH < icon_width_min) ICON_PLAYER_WIDTH = icon_width_min;
|
|
|
|
// Icon width for the AI karts
|
|
int ICON_WIDTH = ICON_PLAYER_WIDTH * 5 / 6;
|
|
|
|
WorldWithRank* world = dynamic_cast<WorldWithRank*>(World::getWorld());
|
|
|
|
//initialize m_previous_icons_position
|
|
if(m_previous_icons_position.size()==0)
|
|
{
|
|
for(unsigned int i=0; i<num_karts; i++)
|
|
{
|
|
const AbstractKart *kart = world->getKart(i);
|
|
int position = kart->getPosition();
|
|
core::vector2d<s32> pos(x_base,y_base+(position-1)*(ICON_PLAYER_WIDTH+2));
|
|
m_previous_icons_position.push_back(pos);
|
|
}
|
|
}
|
|
|
|
int x;
|
|
int y;
|
|
float previous_distance=0.0;//no need to be far ahead, first kart won't try to overlap
|
|
|
|
|
|
|
|
int previous_x=x_base;
|
|
int previous_y=y_base-ICON_PLAYER_WIDTH-2;
|
|
|
|
gui::ScalableFont* font = GUIEngine::getFont();
|
|
const unsigned int kart_amount = world->getNumKarts() - sta;
|
|
|
|
//where is the limit to hide last icons
|
|
int y_icons_limit = irr_driver->getActualScreenSize().Height -
|
|
bottom_margin - ICON_PLAYER_WIDTH;
|
|
if (race_manager->getIfEmptyScreenSpaceExists())
|
|
{
|
|
y_icons_limit = irr_driver->getActualScreenSize().Height - ICON_WIDTH;
|
|
}
|
|
|
|
world->getKartsDisplayInfo(&m_kart_display_infos);
|
|
|
|
for(int position = 1; position <= (int)kart_amount ; position++)
|
|
{
|
|
AbstractKart *kart = world->getKartAtPosition(position);
|
|
|
|
if (kart->getPosition() == -1)//if position is not set
|
|
{
|
|
//we use karts ordered by id only
|
|
//(needed for beginning of battle mode)
|
|
kart= world->getKart(position-1);
|
|
}
|
|
|
|
if(kart->isEliminated()) continue;
|
|
unsigned int kart_id = kart->getWorldKartId();
|
|
|
|
KartIconDisplayInfo &info = m_kart_display_infos[kart_id];
|
|
//x,y is the target position
|
|
int lap = info.lap;
|
|
|
|
// In battle mode mode there is no distance along track etc.
|
|
if (minor_mode==RaceManager::MINOR_MODE_3_STRIKES ||
|
|
minor_mode==RaceManager::MINOR_MODE_FREE_FOR_ALL ||
|
|
minor_mode==RaceManager::MINOR_MODE_CAPTURE_THE_FLAG ||
|
|
minor_mode==RaceManager::MINOR_MODE_EASTER_EGG)
|
|
{
|
|
x = x_base;
|
|
y = previous_y+ICON_PLAYER_WIDTH+2;
|
|
}
|
|
else
|
|
{
|
|
LinearWorld *linear_world = (LinearWorld*)(World::getWorld());
|
|
|
|
float distance = linear_world->getDistanceDownTrackForKart(kart_id, true)
|
|
+ Track::getCurrentTrack()->getTrackLength()*lap;
|
|
|
|
if ((position>1) &&
|
|
(previous_distance-distance<m_dist_show_overlap) &&
|
|
(!kart->hasFinishedRace()) && lap >= 0 )
|
|
{
|
|
//linear translation : form (0,ICON_PLAYER_WIDTH+2) to
|
|
// (previous_x-x_base+(ICON_PLAYER_WIDTH+2)/2,0)
|
|
x=(int)(x_base+(1-(previous_distance-distance)
|
|
/m_dist_show_overlap)
|
|
*(previous_x-x_base+(ICON_PLAYER_WIDTH+2)/2));
|
|
y=(int)(previous_y+(previous_distance-distance)
|
|
/m_dist_show_overlap*(ICON_PLAYER_WIDTH+2));
|
|
}
|
|
else
|
|
{
|
|
x=x_base;
|
|
y=previous_y+ICON_PLAYER_WIDTH+2;
|
|
}
|
|
previous_distance=distance;
|
|
} // not three-strike-battle
|
|
|
|
|
|
previous_x=x;//save coord of the previous kart in list
|
|
previous_y=y;
|
|
|
|
//soft movement using previous position:
|
|
x=(int)((x+m_previous_icons_position[kart_id].X*m_icons_inertia)
|
|
/(m_icons_inertia+1));
|
|
y=(int)((y+m_previous_icons_position[kart_id].Y*m_icons_inertia)
|
|
/(m_icons_inertia+1));
|
|
|
|
//save position for next time
|
|
m_previous_icons_position[kart_id].X=x;
|
|
m_previous_icons_position[kart_id].Y=y;
|
|
|
|
if (y>y_icons_limit)
|
|
{
|
|
//there are too many icons, write "Top 9", to express that
|
|
//there is not everybody shown
|
|
core::recti pos_top;
|
|
pos_top.UpperLeftCorner.Y = y_base-22;
|
|
pos_top.UpperLeftCorner.X = x_base;
|
|
|
|
static video::SColor color = video::SColor(255, 255, 255, 255);
|
|
pos_top.LowerRightCorner = pos_top.UpperLeftCorner;
|
|
|
|
//I18N: When some GlobalPlayerIcons are hidden, write "Top 10" to show it
|
|
font->setBlackBorder(true);
|
|
font->setThinBorder(true);
|
|
font->draw(_("Top %i", position-1 ), pos_top, color);
|
|
font->setThinBorder(false);
|
|
font->setBlackBorder(false);
|
|
|
|
break;
|
|
}
|
|
|
|
if (info.m_text.size() > 0)
|
|
{
|
|
core::rect<s32> pos(x+ICON_PLAYER_WIDTH, y+5,
|
|
x+ICON_PLAYER_WIDTH, y+5);
|
|
if (info.m_outlined_font)
|
|
{
|
|
GUIEngine::getOutlineFont()->draw(info.m_text, pos,
|
|
info.m_color, false, false, NULL, true/*ignore RTL*/);
|
|
}
|
|
else
|
|
{
|
|
font->setBlackBorder(true);
|
|
font->setThinBorder(true);
|
|
font->draw(info.m_text, pos, info.m_color, false, false, NULL,
|
|
true/*ignore RTL*/);
|
|
font->setThinBorder(false);
|
|
font->setBlackBorder(false);
|
|
}
|
|
}
|
|
|
|
if (info.special_title.size() > 0)
|
|
{
|
|
core::rect<s32> pos(x+ICON_PLAYER_WIDTH, y+5,
|
|
x+ICON_PLAYER_WIDTH, y+5);
|
|
core::stringw s(info.special_title.c_str());
|
|
font->setBlackBorder(true);
|
|
font->setThinBorder(true);
|
|
font->draw(s.c_str(), pos, info.m_color, false, false, NULL,
|
|
true /* ignore RTL */);
|
|
font->setThinBorder(false);
|
|
font->setBlackBorder(false);
|
|
}
|
|
|
|
int w = kart->getController()
|
|
->isLocalPlayerController() ? ICON_PLAYER_WIDTH
|
|
: ICON_WIDTH;
|
|
drawPlayerIcon(kart, x, y, w);
|
|
} //next position
|
|
#endif
|
|
} // drawGlobalPlayerIcons
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/** Draw one player icon
|
|
* Takes care of icon looking different due to plumber, squashing, ...
|
|
*/
|
|
void RaceGUIBase::drawPlayerIcon(AbstractKart *kart, int x, int y, int w)
|
|
{
|
|
#ifndef SERVER_ONLY
|
|
video::ITexture *icon =
|
|
kart->getKartProperties()->getIconMaterial()->getTexture();
|
|
|
|
CaptureTheFlag* ctf = dynamic_cast<CaptureTheFlag*>(World::getWorld());
|
|
unsigned int kart_id = kart->getWorldKartId();
|
|
|
|
// CTF
|
|
if (ctf)
|
|
{
|
|
if (ctf->getRedHolder() == (int)kart_id)
|
|
{
|
|
video::ITexture* red =
|
|
irr_driver->getTexture(FileManager::GUI_ICON, "red_flag.png");
|
|
const core::rect<s32> rect(core::position2d<s32>(0, 0),
|
|
red->getSize());
|
|
const core::rect<s32> pos1
|
|
(x - 20, y - 10, x + w - 20, y + w - 30);
|
|
draw2DImage(red, pos1, rect, NULL, NULL, true);
|
|
}
|
|
else if (ctf->getBlueHolder() == (int)kart_id)
|
|
{
|
|
video::ITexture* blue =
|
|
irr_driver->getTexture(FileManager::GUI_ICON, "blue_flag.png");
|
|
const core::rect<s32> rect(core::position2d<s32>(0, 0),
|
|
blue->getSize());
|
|
const core::rect<s32> pos1
|
|
(x - 20, y - 10, x + w - 20, y + w - 30);
|
|
draw2DImage(blue, pos1, rect, NULL, NULL, true);
|
|
}
|
|
}
|
|
|
|
const core::rect<s32> pos(x, y, x+w, y+w);
|
|
|
|
//to bring to light the player's icon: add a background
|
|
if (kart->getController()->isLocalPlayerController() &&
|
|
m_icons_frame != NULL)
|
|
{
|
|
video::SColor colors[4];
|
|
for (unsigned int i=0;i<4;i++)
|
|
{
|
|
colors[i]=kart->getKartProperties()->getColor();
|
|
colors[i].setAlpha(
|
|
100+(int)(100*cos(M_PI/2*i+World::getWorld()->getTime()*2)));
|
|
}
|
|
const core::rect<s32> rect(core::position2d<s32>(0,0),
|
|
m_icons_frame->getSize());
|
|
draw2DImage(m_icons_frame, pos, rect,NULL, colors, true);
|
|
}
|
|
|
|
// Fixes crash bug, why are certain icons not showing up?
|
|
if (icon && !kart->getKartAnimation() && !kart->isSquashed())
|
|
{
|
|
const core::rect<s32> rect(core::position2d<s32>(0,0),
|
|
icon->getSize());
|
|
draw2DImage(icon, pos, rect, NULL, NULL, true, kart->isGhostKart());
|
|
}
|
|
|
|
//draw status info - icon fade out in case of rescue/explode
|
|
|
|
if (icon && dynamic_cast<RescueAnimation*>(kart->getKartAnimation()))
|
|
{
|
|
//icon fades to the left
|
|
float t = kart->getKartAnimation()->getAnimationTimer();
|
|
float t_anim=100*sin(0.5f*M_PI*t);
|
|
const core::rect<s32> rect1(core::position2d<s32>(0,0),
|
|
icon->getSize());
|
|
const core::rect<s32> pos1((int)(x-t_anim), y,
|
|
(int)(x+w-t_anim), y+w);
|
|
draw2DImage(icon, pos1, rect1,
|
|
NULL, NULL, true);
|
|
}
|
|
|
|
if (icon && !kart->getKartAnimation() && kart->isSquashed() )
|
|
{
|
|
//syncs icon squash with kart squash
|
|
const core::rect<s32> destRect(core::position2d<s32>(x,y+w/4),
|
|
core::position2d<s32>(x+w,y+w*3/4));
|
|
const core::rect<s32> sourceRect(core::position2d<s32>(0,0),
|
|
icon->getSize());
|
|
draw2DImage(icon, destRect,
|
|
sourceRect, NULL, NULL,
|
|
true);
|
|
}
|
|
|
|
if (icon &&
|
|
dynamic_cast<ExplosionAnimation*>(kart->getKartAnimation()) )
|
|
{
|
|
//exploses into 4 parts
|
|
float t = kart->getKartAnimation()->getAnimationTimer();
|
|
float t_anim=50.0f*sin(0.5f*M_PI*t);
|
|
u16 icon_size_x=icon->getSize().Width;
|
|
u16 icon_size_y=icon->getSize().Height;
|
|
|
|
const core::rect<s32> rect1(0, 0, icon_size_x/2,icon_size_y/2);
|
|
const core::rect<s32> pos1((int)(x-t_anim), (int)(y-t_anim),
|
|
(int)(x+w/2-t_anim),
|
|
(int)(y+w/2-t_anim));
|
|
draw2DImage(icon, pos1, rect1,
|
|
NULL, NULL, true);
|
|
|
|
const core::rect<s32> rect2(icon_size_x/2,0,
|
|
icon_size_x,icon_size_y/2);
|
|
const core::rect<s32> pos2((int)(x+w/2+t_anim),
|
|
(int)(y-t_anim),
|
|
(int)(x+w+t_anim),
|
|
(int)(y+w/2-t_anim));
|
|
draw2DImage(icon, pos2, rect2,
|
|
NULL, NULL, true);
|
|
|
|
const core::rect<s32> rect3(0, icon_size_y/2, icon_size_x/2,icon_size_y);
|
|
const core::rect<s32> pos3((int)(x-t_anim), (int)(y+w/2+t_anim),
|
|
(int)(x+w/2-t_anim), (int)(y+w+t_anim));
|
|
draw2DImage(icon, pos3, rect3, NULL, NULL, true);
|
|
|
|
const core::rect<s32> rect4(icon_size_x/2,icon_size_y/2,icon_size_x,icon_size_y);
|
|
const core::rect<s32> pos4((int)(x+w/2+t_anim), (int)(y+w/2+t_anim),
|
|
(int)(x+w+t_anim), (int)(y+w+t_anim));
|
|
draw2DImage(icon, pos4, rect4, NULL, NULL, true);
|
|
}
|
|
|
|
//Plunger
|
|
if (kart->getBlockedByPlungerTicks()>0)
|
|
{
|
|
video::ITexture *icon_plunger =
|
|
powerup_manager->getIcon(PowerupManager::POWERUP_PLUNGER)->getTexture();
|
|
if (icon_plunger != NULL)
|
|
{
|
|
const core::rect<s32> rect(core::position2d<s32>(0,0),
|
|
icon_plunger->getSize());
|
|
const core::rect<s32> pos1(x+10, y-10, x+w+10, y+w-10);
|
|
draw2DImage(icon_plunger, pos1,
|
|
rect, NULL, NULL,
|
|
true);
|
|
}
|
|
}
|
|
//attachment
|
|
if (kart->getAttachment()->getType() != Attachment::ATTACH_NOTHING)
|
|
{
|
|
video::ITexture *icon_attachment =
|
|
attachment_manager->getIcon(kart->getAttachment()->getType())
|
|
->getTexture();
|
|
if (icon_attachment != NULL)
|
|
{
|
|
const core::rect<s32> rect(core::position2d<s32>(0,0),
|
|
icon_attachment->getSize());
|
|
const core::rect<s32> pos1(x-20, y-10, x+w-20, y+w-10);
|
|
draw2DImage(icon_attachment,
|
|
pos1, rect, NULL,
|
|
NULL, true);
|
|
}
|
|
}
|
|
|
|
//lap flag for finished karts
|
|
if (kart->hasFinishedRace())
|
|
{
|
|
if (m_lap_flag != NULL)
|
|
{
|
|
const core::rect<s32> rect(core::position2d<s32>(0, 0),
|
|
m_lap_flag->getSize());
|
|
const core::rect<s32> pos1(x - 20, y - 10, x + w - 20, y + w - 10);
|
|
draw2DImage(m_lap_flag, pos1, rect, NULL, NULL, true);
|
|
}
|
|
}
|
|
#endif
|
|
} // drawPlayerIcon
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/** Draws the plunger-in-face if necessary. Does nothing if there is no
|
|
* plunger in face atm.
|
|
*/
|
|
void RaceGUIBase::drawPlungerInFace(const Camera *camera, float dt)
|
|
{
|
|
#ifndef SERVER_ONLY
|
|
const AbstractKart *kart = camera->getKart();
|
|
if (kart->getBlockedByPlungerTicks()<=0)
|
|
{
|
|
m_plunger_state = PLUNGER_STATE_INIT;
|
|
return;
|
|
}
|
|
|
|
const core::recti &viewport = camera->getViewport();
|
|
|
|
const int screen_width = viewport.LowerRightCorner.X
|
|
- viewport.UpperLeftCorner.X;
|
|
|
|
if(m_plunger_state == PLUNGER_STATE_INIT)
|
|
{
|
|
m_plunger_move_time = 0.0f;
|
|
m_plunger_offset = core::vector2di(0,0);
|
|
m_plunger_state = PLUNGER_STATE_SLOW_2;
|
|
m_plunger_speed = core::vector2df(0, 0);
|
|
}
|
|
|
|
if(World::getWorld()->getPhase()!=World::IN_GAME_MENU_PHASE)
|
|
{
|
|
m_plunger_move_time -= dt;
|
|
if(m_plunger_move_time < dt && m_plunger_state!=PLUNGER_STATE_FAST)
|
|
{
|
|
const float fast_time = 0.3f;
|
|
if(kart->getBlockedByPlungerTicks()<stk_config->time2Ticks(fast_time))
|
|
{
|
|
// First time we reach faste state: select random target point
|
|
// at top of screen and set speed accordingly
|
|
RandomGenerator random;
|
|
float movement_fraction = 0.3f;
|
|
int plunger_x_target = screen_width/2
|
|
+ random.get((int)(screen_width*movement_fraction))
|
|
- (int)(screen_width*movement_fraction*0.5f);
|
|
m_plunger_state = PLUNGER_STATE_FAST;
|
|
m_plunger_speed =
|
|
core::vector2df((plunger_x_target-screen_width/2)/fast_time,
|
|
viewport.getHeight()*0.5f/fast_time);
|
|
m_plunger_move_time = fast_time;
|
|
}
|
|
else
|
|
{
|
|
RandomGenerator random;
|
|
m_plunger_move_time = 0.1f+random.get(50)/200.0f;
|
|
// Plunger is either moving or not moving
|
|
if(m_plunger_state==PLUNGER_STATE_SLOW_1)
|
|
{
|
|
m_plunger_state = PLUNGER_STATE_SLOW_2;
|
|
m_plunger_speed =
|
|
core::vector2df(0, 0.05f*viewport.getHeight()
|
|
/m_plunger_move_time );
|
|
}
|
|
else
|
|
{
|
|
m_plunger_state = PLUNGER_STATE_SLOW_1;
|
|
m_plunger_speed =
|
|
core::vector2df(0, 0.02f*viewport.getHeight()
|
|
/m_plunger_move_time );
|
|
}
|
|
} // has not reach fast moving state
|
|
}
|
|
|
|
m_plunger_offset.X += (int)(m_plunger_speed.X * dt);
|
|
m_plunger_offset.Y += (int)(m_plunger_speed.Y * dt);
|
|
}
|
|
|
|
if (m_plunger_face != NULL)
|
|
{
|
|
const int plunger_size = (int)(0.6f * screen_width);
|
|
int offset_y = viewport.UpperLeftCorner.Y + viewport.getHeight()/2
|
|
- plunger_size/2 - m_plunger_offset.Y;
|
|
|
|
int plunger_x = viewport.UpperLeftCorner.X + screen_width/2
|
|
- plunger_size/2;
|
|
|
|
plunger_x += (int)m_plunger_offset.X;
|
|
core::rect<s32> dest(plunger_x, offset_y,
|
|
plunger_x+plunger_size, offset_y+plunger_size);
|
|
|
|
const core::rect<s32> source(core::position2d<s32>(0,0),
|
|
m_plunger_face->getSize());
|
|
|
|
draw2DImage(m_plunger_face, dest, source,
|
|
&viewport /* clip */,
|
|
NULL /* color */,
|
|
true /* alpha */ );
|
|
}
|
|
#endif // !SERVER_ONLY
|
|
} // drawPlungerInFace
|
|
|
|
// ----------------------------------------------------------------------------
|
|
void RaceGUIBase::removeReferee()
|
|
{
|
|
if (m_referee->isAttached()) // race phase:
|
|
{
|
|
m_referee->removeFromSceneGraph();
|
|
}
|
|
} // removeReferee
|
|
|