Merge remote-tracking branch 'origin/master'
@ -1,6 +1,8 @@
|
||||
## SuperTuxKart 0.9.4
|
||||
* Networking game for normal race, time trial, free for all, capture the flag and soccer
|
||||
* Better random item distribution for various numbers of karts
|
||||
* Improved powerup handling in AI
|
||||
* New tracks Las Dunas Soccer by samuncle
|
||||
|
||||
## SuperTuxKart 0.9.3
|
||||
* Reduced RAM and VRAM usage, reducing load times by Auria and Benau
|
||||
|
27
INSTALL.md
@ -98,17 +98,26 @@ location, specify `CMAKE_INSTALL_PREFIX` when running cmake, e.g.:
|
||||
## Building SuperTuxKart on Windows
|
||||
To Build SuperTuxKart on Windows, follow these instructions:
|
||||
|
||||
1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/). The free Visual Studio Community edition works fine.
|
||||
2. Download the SuperTuxKart source package from either [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/) or [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), and unpack it.
|
||||
*Note: If you downloaded the source package from here: [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control), then both `stk-code` and `stk-assets` **must** be in the same directory, otherwise the build can result in failure*
|
||||
3. Download the Windows dependencies package from either [SuperTuxKart download area: Dependencies - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/)
|
||||
or [SuperTuxKart on GitHub - Dependencies](https://github.com/supertuxkart/dependencies), and unpack it; then, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory, rename it to `dependencies-64bit` if you want to compile a 64bit build.
|
||||
4. Download CMake from here: [CMake - download page](https://cmake.org/download/), install it; once CMake is installed, double click on the CMake icon on your desktop, and point it towards your `stk-code` directory in the 'Where is the source code' field, and point it to a directory called `build` or `bld` inside the stk-code directory.
|
||||
1. Download and install Visual Studio from here: [Visual Studio - Download](https://www.visualstudio.com/downloads/). The free Visual Studio Community edition works fine. Remember to select "Desktop development with C++" in the installer.
|
||||
|
||||
2a. If you want the stable version, download the SuperTuxKart source package from the latest stable version [SuperTuxKart download area - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart/) and unpack it.
|
||||
2b. If you want the development version, you will need a Git client and a SVN client. More information can be found here: [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control).
|
||||
Open your file browser and find somewhere you want to put the development version of SuperTuxKart. For example in C:\Users\<Your Username> as the Git and SVN clients will have write permissions there, and you should create its own directory, for example SuperTuxKart-dev. Enter that directory, and create a directory inside called stk-assets, and enter it. If you installed TortoiseSVN, right-click, select TortoiseSVN -> Checkout... and paste the correspodning URL found in [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control). While it is downloading the game assets, go back to your file browser and one level up. Right-click again somewhere empty and select "Git clone..." and paste the corresponding link found in [SuperTuxKart.net - Source Control](https://supertuxkart.net/Source_control).
|
||||
*Note: Both `stk-code` and `stk-assets` **must** be in the same directory, otherwise the build will likely fail!*
|
||||
|
||||
3a. If you got the stable version, download the Windows dependencies package from [SuperTuxKart download area: Dependencies - SourceForge.net](https://sourceforge.net/projects/supertuxkart/files/SuperTuxKart%20Dependencies/Windows/) and unpack it.
|
||||
3b. If you got the development version go to SuperTuxKart-dev in your file browser, right-click somewhere empty, select "Git clone..." and paste https://github.com/supertuxkart/dependencies.git in the URL field; click OK. When finished, copy the `dependencies` directory from either the `windows` or the `windows_64bit` directories into the `stk-code` directory; rename the latter to `dependencies-64bit` if you want to compile a 64-bit build.
|
||||
|
||||
4. Download CMake from here: [CMake - download page](https://cmake.org/download/), install it; once CMake is installed, double click on the CMake icon on your desktop, and point it towards your `stk-code` directory in the 'Where is the source code' field, and point it to a new directory called `build` or `bld` inside the stk-code directory.
|
||||
|
||||
5. Press 'Configure'; CMake will ask you if it is OK to create the aforementioned directory, press `Yes`. CMake will then ask you about your version of Visual Studio.
|
||||
Confirm your selection; *Please look at the table below to avoid confusion between version numbers and releases of Visual Studio*;
|
||||
CMake will begin creating the required files for the build in the directory.
|
||||
|
||||
Confirm your selection; *Please look at the table below to avoid confusion between version numbers and releases of Visual Studio*; CMake will begin creating the required files for the build in the directory. If you want to do a 64-bit build, select the version of Visual Studio you installed with "Win64" appended.
|
||||
|
||||
6. Navigate to your build directory and open the `SuperTuxKart.sln` file; Visual Studio will now load the solution.
|
||||
7. In the 'Solution Explorer', right click on the `supertuxkart` project and select "Set as StartUp project"
|
||||
|
||||
7. In the 'Solution Explorer', right click on the `supertuxkart` project and select "Set as StartUp project".
|
||||
|
||||
8. Open the 'Build' menu and select 'Build Solution'; or, press the default keyboard shortcut: `CTRL + SHIFT + B` to build the solution.
|
||||
|
||||
*Note: To avoid confusion between releases and versions, refer to this table:*
|
||||
|
@ -372,8 +372,8 @@ if [ ! -f "$DIRNAME/obj/curl.stamp" ]; then
|
||||
cp -a -f "$DIRNAME/../lib/curl/"* "$DIRNAME/obj/curl"
|
||||
|
||||
cd "$DIRNAME/obj/curl"
|
||||
CPPFLAGS="-I$DIRNAME/obj/openssl/include $CPPFLAGS" \
|
||||
LDFLAGS="-L$DIRNAME/obj/openssl/ $LDFLAGS" \
|
||||
CPPFLAGS="-I$DIRNAME/obj/openssl/include -I$DIRNAME/obj/zlib $CPPFLAGS" \
|
||||
LDFLAGS="-L$DIRNAME/obj/openssl/ -L$DIRNAME/obj/zlib $LDFLAGS" \
|
||||
./configure --host=$HOST \
|
||||
--with-ssl \
|
||||
--disable-shared \
|
||||
|
@ -56,11 +56,11 @@
|
||||
</div>
|
||||
|
||||
<div width="100%" proportion="2" layout="horizontal-row">
|
||||
<icon align="center" width="64" height="64" icon="gui/mode_3strikes.png"/>
|
||||
<icon align="center" width="64" height="64" icon="gui/weapons.png"/>
|
||||
<spacer width="25" height="25"/>
|
||||
<bubble proportion="1" height="100%"
|
||||
I18N="In the help menu"
|
||||
text="3 Strikes Battle: Hit others with weapons until they lose all their lives."/>
|
||||
text="There are 3 types of battle mode: In 3 Strikes Battle, you need to hit others with weapons until they lose all their lives. In Free-For-All, the player who hits others the most will win in a given hit or time limit. In Capture The Flag, your team needs to bring the flag of the other team to your own flag base, as long as your flag is not captured by the other team."/>
|
||||
</div>
|
||||
|
||||
<div width="100%" proportion="2" layout="horizontal-row">
|
||||
|
@ -19,7 +19,7 @@
|
||||
<spacer height="10" width="20"/>
|
||||
|
||||
<div width="100%" height="fit" layout="horizontal-row" >
|
||||
<label proportion="1" text_align="left" I18N="In the server creation screen" text="Password (optional)"/>
|
||||
<label proportion="1" text_align="left" I18N="In the server creation screen" text="Password for private server (optional)"/>
|
||||
<textbox proportion="1" id="password" I18N="In the server creation screen"/>
|
||||
</div>
|
||||
|
||||
@ -42,8 +42,8 @@
|
||||
I18N="Multiplayer game mode" text="Normal Race"/>
|
||||
<icon-button id="timetrial" width="128" height="128" icon="gui/mode_tt.png"
|
||||
I18N="Multiplayer game mode" text="Time Trial"/>
|
||||
<icon-button id="3strikes" width="128" height="128" icon="gui/mode_3strikes.png"
|
||||
I18N="Multiplayer game mode" text="3 Strikes Battle"/>
|
||||
<icon-button id="3strikes" width="128" height="128" icon="gui/weapons.png"
|
||||
I18N="Multiplayer game mode" text="Battle"/>
|
||||
<icon-button id="soccer" width="128" height="128" icon="gui/mode_soccer.png"
|
||||
I18N="Multiplayer game mode" text="Soccer"/>
|
||||
</ribbon>
|
||||
|
1788
data/po/ca.po
1794
data/po/zh_TW.po
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.0 KiB |
@ -1,5 +1,5 @@
|
||||
# Modify this file to change the last-modified date when you add/remove a file.
|
||||
# This will then trigger a new cmake run automatically.
|
||||
# This will then trigger a new cmake run automatically.
|
||||
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
|
||||
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
|
||||
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")
|
||||
|
@ -680,10 +680,6 @@ namespace UserConfigParams
|
||||
/** True if graphical profiler should be displayed */
|
||||
PARAM_PREFIX bool m_profiler_enabled PARAM_DEFAULT( false );
|
||||
|
||||
/** How many seconds worth of data the circular profile buffer
|
||||
* can store. */
|
||||
PARAM_PREFIX float m_profiler_buffer_duration PARAM_DEFAULT(20.0f);
|
||||
|
||||
// ---- Networking
|
||||
PARAM_PREFIX StringToUIntUserConfigParam m_stun_list
|
||||
PARAM_DEFAULT(StringToUIntUserConfigParam("stun_list",
|
||||
@ -727,7 +723,7 @@ namespace UserConfigParams
|
||||
PARAM_DEFAULT(FloatUserConfigParam(20.0f, "validation-timeout",
|
||||
&m_network_group, "Timeout in seconds for validation of clients."));
|
||||
PARAM_PREFIX IntUserConfigParam m_server_max_players
|
||||
PARAM_DEFAULT(IntUserConfigParam(8, "server_max_players",
|
||||
PARAM_DEFAULT(IntUserConfigParam(8, "server-max-players",
|
||||
&m_network_group, "Maximum number of players on the server."));
|
||||
PARAM_PREFIX BoolUserConfigParam m_firewalled_server
|
||||
PARAM_DEFAULT(BoolUserConfigParam(true, "firewalled-server",
|
||||
@ -743,13 +739,42 @@ namespace UserConfigParams
|
||||
&m_network_group, "Only auto start kart selection when number of "
|
||||
"connected player is larger than max player * this value, for "
|
||||
"owner less or ranked server, after start-game-counter."));
|
||||
|
||||
PARAM_PREFIX FloatUserConfigParam m_flag_return_timemout
|
||||
PARAM_DEFAULT(FloatUserConfigParam(20.0f, "flag-return-timemout",
|
||||
&m_network_group, "Time in seconds when a flag is dropped a by player in CTF "
|
||||
"returning to its own base."));
|
||||
PARAM_PREFIX FloatUserConfigParam m_hit_limit_threshold
|
||||
PARAM_DEFAULT(FloatUserConfigParam(5.0f, "hit-limit-threshold",
|
||||
&m_network_group, "Value used to calculate hit limit in free for all, which "
|
||||
"is min(number of players * hit-limit-threshold, 40), negative value to disable hit limit."));
|
||||
PARAM_PREFIX FloatUserConfigParam m_time_limit_threshold_ffa
|
||||
PARAM_DEFAULT(FloatUserConfigParam(0.7f, "time-limit-threshold-ffa",
|
||||
&m_network_group, "Value used to calculate time limit in free for all, which "
|
||||
"is max(number of players * time-limit-threshold-ffa, 2.0) * 60, negative value to disable time limit."));
|
||||
PARAM_PREFIX FloatUserConfigParam m_capture_limit_threshold
|
||||
PARAM_DEFAULT(FloatUserConfigParam(0.5f, "capture-limit-threshold",
|
||||
&m_network_group, "Value used to calculate capture limit in CTF, which "
|
||||
"is max(2.0, number of players * capture-limit-threshold), negative value to disable capture limit."));
|
||||
PARAM_PREFIX FloatUserConfigParam m_time_limit_threshold_ctf
|
||||
PARAM_DEFAULT(FloatUserConfigParam(0.9f, "time-limit-threshold-ctf",
|
||||
&m_network_group, "Value used to calculate time limit in CTF, which "
|
||||
"is max(2.0, number of players * (time-limit-threshold-ctf + flag-return-timemout / 60.0)) * 60.0,"
|
||||
" negative value to disable time limit."));
|
||||
PARAM_PREFIX StringToUIntUserConfigParam m_server_ban_list
|
||||
PARAM_DEFAULT(StringToUIntUserConfigParam("server_ban_list",
|
||||
"LHS: IP in x.x.x.x format, RHS: online id, if 0 than all players "
|
||||
"from this IP will be banned.",
|
||||
{ { "0.0.0.0", 0u } }
|
||||
));
|
||||
PARAM_PREFIX IntUserConfigParam m_max_ping
|
||||
PARAM_DEFAULT(IntUserConfigParam(300, "max-ping",
|
||||
&m_network_group, "Maximum ping allowed for a player (in ms)."));
|
||||
PARAM_PREFIX IntUserConfigParam m_jitter_tolerance
|
||||
PARAM_DEFAULT(IntUserConfigParam(100, "jitter-tolerance",
|
||||
&m_network_group, "Tolerance of jitter in network allowed (in ms)."));
|
||||
PARAM_PREFIX BoolUserConfigParam m_kick_high_ping_players
|
||||
PARAM_DEFAULT(BoolUserConfigParam(false, "kick-high-ping-players",
|
||||
&m_network_group, "Kick players whose ping is above max-ping."));
|
||||
|
||||
// ---- Gamemode setup
|
||||
PARAM_PREFIX UIntToUIntUserConfigParam m_num_karts_per_gamemode
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "karts/max_speed.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "tracks/quad.hpp"
|
||||
#include "utils/constants.hpp"
|
||||
#include "utils/mini_glm.hpp"
|
||||
@ -825,12 +826,28 @@ void SlipStream::update(int ticks)
|
||||
|
||||
m_slipstream_time = 0.0f;
|
||||
m_bonus_active = true;
|
||||
float speed_increase = kp->getSlipstreamMaxSpeedIncrease();
|
||||
float add_power = kp->getSlipstreamAddPower();
|
||||
int duration = stk_config->time2Ticks(m_bonus_time);
|
||||
int fade_out = kp->getSlipstreamFadeOutTicks();
|
||||
m_kart->instantSpeedIncrease(MaxSpeed::MS_INCREASE_SLIPSTREAM,
|
||||
kp->getSlipstreamMaxSpeedIncrease(),
|
||||
kp->getSlipstreamMaxSpeedIncrease(),
|
||||
kp->getSlipstreamAddPower(),
|
||||
stk_config->time2Ticks(m_bonus_time), fade_out);
|
||||
m_kart->instantSpeedIncrease(
|
||||
MaxSpeed::MS_INCREASE_SLIPSTREAM, speed_increase,
|
||||
speed_increase, add_power, duration, fade_out);
|
||||
|
||||
if (RewindManager::get()->useLocalEvent())
|
||||
{
|
||||
AbstractKart* kart = m_kart;
|
||||
RewindManager::get()->addRewindInfoEventFunction(new
|
||||
RewindInfoEventFunction(
|
||||
World::getWorld()->getTicksSinceStart(),
|
||||
[](){},
|
||||
[kart, speed_increase, add_power, duration, fade_out]()
|
||||
{
|
||||
kart->instantSpeedIncrease(
|
||||
MaxSpeed::MS_INCREASE_SLIPSTREAM, speed_increase,
|
||||
speed_increase, add_power, duration, fade_out);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_sstreaming)
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "guiengine/widget.hpp"
|
||||
#include "guiengine/widgets/list_widget.hpp"
|
||||
#include "guiengine/widgets/ribbon_widget.hpp"
|
||||
#include "guiengine/widgets/spinner_widget.hpp"
|
||||
#include "input/input_manager.hpp"
|
||||
#include "modes/demo_world.hpp"
|
||||
#include "modes/world.hpp"
|
||||
@ -488,6 +489,16 @@ void EventHandler::navigate(const NavigationDirection nav, const int playerID)
|
||||
sendEventToUser(ribbon, ribbon->m_properties[PROP_ID], playerID);
|
||||
}
|
||||
}
|
||||
|
||||
// For spinners, select the most intuitive button
|
||||
// based on where the navigation came from
|
||||
// Right if coming from right by a left press
|
||||
// Left for all other directions
|
||||
if (closest_widget->getType() == WTYPE_SPINNER)
|
||||
{
|
||||
SpinnerWidget* spinner = dynamic_cast<SpinnerWidget*>(closest_widget);
|
||||
spinner->setSelectedButton(nav == NAV_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
@ -724,6 +735,7 @@ EventPropagation EventHandler::onWidgetActivated(GUIEngine::Widget* w, const int
|
||||
|
||||
Widget* parent = w->m_event_handler;
|
||||
|
||||
//FIXME : sendEventToUser do the same screen keyboard and modal dialog checks, so they are done twice
|
||||
if (ScreenKeyboard::isActive())
|
||||
{
|
||||
if (ScreenKeyboard::getCurrent()->processEvent(w->m_properties[PROP_ID]) == EVENT_BLOCK)
|
||||
@ -743,6 +755,13 @@ EventPropagation EventHandler::onWidgetActivated(GUIEngine::Widget* w, const int
|
||||
|
||||
//Log::info("EventHandler", "Widget activated: %s", w->m_properties[PROP_ID].c_str());
|
||||
|
||||
// For spinners, also trigger activation
|
||||
if (w->getType() == WTYPE_SPINNER)
|
||||
{
|
||||
SpinnerWidget* spinner = dynamic_cast<SpinnerWidget*>(w);
|
||||
spinner->activateSelectedButton();
|
||||
}
|
||||
|
||||
if (w->m_event_handler != NULL)
|
||||
{
|
||||
/* Find all parents. Stop looping if a widget event handler's is itself, to not fall
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "guiengine/modaldialog.hpp"
|
||||
#include "guiengine/widget.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "states_screens/state_manager.hpp"
|
||||
|
||||
#include <irrlicht.h>
|
||||
@ -90,7 +91,8 @@ Screen::~Screen()
|
||||
*/
|
||||
void Screen::init()
|
||||
{
|
||||
if(m_pause_race && World::getWorld())
|
||||
if (m_pause_race && World::getWorld() &&
|
||||
!NetworkConfig::get()->isNetworking())
|
||||
World::getWorld()->schedulePause(World::IN_GAME_MENU_PHASE);
|
||||
} // init
|
||||
|
||||
@ -111,7 +113,8 @@ void Screen::push()
|
||||
*/
|
||||
void Screen::tearDown()
|
||||
{
|
||||
if(m_pause_race && World::getWorld())
|
||||
if (m_pause_race && World::getWorld() &&
|
||||
!NetworkConfig::get()->isNetworking())
|
||||
World::getWorld()->scheduleUnpause();
|
||||
} // tearDown
|
||||
|
||||
|
@ -1410,20 +1410,29 @@ void Skin::drawSpinnerBody(const core::recti &rect, Widget* widget,
|
||||
void Skin::drawSpinnerChild(const core::recti &rect, Widget* widget,
|
||||
const bool pressed, bool focused)
|
||||
{
|
||||
if (!widget->isVisible()) return;
|
||||
if (!widget->isVisible() || widget->m_deactivated) return;
|
||||
|
||||
if (!widget->m_deactivated && pressed)
|
||||
int areas = 0;
|
||||
bool right;
|
||||
|
||||
if (widget->m_properties[PROP_ID] == "left")
|
||||
{
|
||||
Widget* spinner = widget->m_event_handler;
|
||||
int areas = 0;
|
||||
areas = BoxRenderParams::LEFT;
|
||||
right = false;
|
||||
}
|
||||
else if (widget->m_properties[PROP_ID] == "right")
|
||||
{
|
||||
areas = BoxRenderParams::RIGHT;
|
||||
right = true;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
if (widget->m_properties[PROP_ID] == "left")
|
||||
areas = BoxRenderParams::LEFT;
|
||||
else if (widget->m_properties[PROP_ID] == "right")
|
||||
areas = BoxRenderParams::RIGHT;
|
||||
else
|
||||
return;
|
||||
SpinnerWidget* spinner = dynamic_cast<SpinnerWidget*>(widget->m_event_handler);
|
||||
bool spinner_focused = spinner->isFocusedForPlayer(PLAYER_ID_GAME_MASTER);
|
||||
|
||||
if (pressed || (spinner->isRightButtonSelected() == right && spinner_focused))
|
||||
{
|
||||
core::recti rect2(spinner->m_x, spinner->m_y,
|
||||
spinner->m_x + spinner->m_w,
|
||||
spinner->m_y + spinner->m_h );
|
||||
@ -1434,7 +1443,7 @@ void Skin::drawSpinnerChild(const core::recti &rect, Widget* widget,
|
||||
widget->m_deactivated);
|
||||
}
|
||||
|
||||
}
|
||||
} // drawSpinnerChild
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/**
|
||||
@ -1513,6 +1522,18 @@ void Skin::drawIconButton(const core::recti &rect, Widget* widget,
|
||||
|
||||
IconButtonWidget* icon_widget = (IconButtonWidget*) widget;
|
||||
|
||||
if (icon_widget->hasTooltip() > 0)
|
||||
{
|
||||
const core::position2di mouse_position =
|
||||
irr_driver->getDevice()->getCursorControl()->getPosition();
|
||||
|
||||
if (rect.isPointInside(mouse_position))
|
||||
{
|
||||
m_tooltip_at_mouse.push_back(true);
|
||||
m_tooltips.push_back(widget);
|
||||
}
|
||||
}
|
||||
|
||||
if (widget->m_type == WTYPE_MODEL_VIEW)
|
||||
{
|
||||
// Model view widgets don't generate mipmaps so disable material 2D
|
||||
|
@ -89,37 +89,57 @@ KartStatsWidget::KartStatsWidget(core::recti area, const int player_id,
|
||||
void KartStatsWidget::setValues(const KartProperties* props,
|
||||
PerPlayerDifficulty d)
|
||||
{
|
||||
// Use kart properties computed for "hard" difficulty to show the user, so
|
||||
// Use kart properties computed for best difficulty to show the user, so
|
||||
// that properties don't change according to the the last used difficulty
|
||||
// (And because this code uses arbitrary scaling factors to make them look
|
||||
// nice and the arbitrary factors were optimised for hard difficulty)
|
||||
RaceManager::Difficulty previous_difficulty = race_manager->getDifficulty();
|
||||
race_manager->setDifficulty(RaceManager::DIFFICULTY_HARD);
|
||||
race_manager->setDifficulty(RaceManager::DIFFICULTY_BEST);
|
||||
KartProperties kp_computed;
|
||||
kp_computed.copyForPlayer(props, d);
|
||||
for (SkillLevelWidget* skills : m_skills)
|
||||
skills->setVisible(true);
|
||||
|
||||
// Scale the values so they look better
|
||||
// The scaling factor and offset were found by trial and error.
|
||||
// It should look nice and you should be able to see the difference between
|
||||
// different masses or velocities.
|
||||
// A value of 100 takes the whole bar width, including borders.
|
||||
// So values should be in the 0-99 range
|
||||
|
||||
// The base mass is of 350 ; 350/3.89 ~= 90
|
||||
m_skills[SKILL_MASS]->setValue((int)
|
||||
((kp_computed.getCombinedCharacteristic()->getMass() - 20) / 4));
|
||||
(kp_computed.getCombinedCharacteristic()->getMass()/3.89f));
|
||||
m_skills[SKILL_MASS]->setIcon(irr::core::stringc(
|
||||
file_manager->getAsset(FileManager::GUI, "mass.png").c_str()));
|
||||
m_skills[SKILL_MASS]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_mass", m_player_id);
|
||||
m_skills[SKILL_MASS]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_mass", m_player_id);
|
||||
m_skills[SKILL_MASS]->m_iconbutton->setTooltip( _("Mass") );
|
||||
|
||||
// The base speed is of 25
|
||||
// Here we are not fully proportional, because small differences matter more
|
||||
m_skills[SKILL_SPEED]->setValue((int)
|
||||
((kp_computed.getCombinedCharacteristic()->getEngineMaxSpeed() - 15) * 6));
|
||||
((kp_computed.getCombinedCharacteristic()->getEngineMaxSpeed() - 20) * 15));
|
||||
m_skills[SKILL_SPEED]->setIcon(irr::core::stringc(
|
||||
file_manager->getAsset(FileManager::GUI, "speed.png").c_str()));
|
||||
m_skills[SKILL_SPEED]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_speed", m_player_id);
|
||||
m_skills[SKILL_SPEED]->m_iconbutton->setTooltip( _("Maximum speed") );
|
||||
|
||||
m_skills[SKILL_POWER]->setValue((int)((kp_computed.getAvgPower() - 30) / 20));
|
||||
m_skills[SKILL_POWER]->setIcon(irr::core::stringc(
|
||||
// The acceleration depend on power and mass, and it changes depending on speed
|
||||
// We call a function which gives us a single number to represent it
|
||||
// power/mass gives numbers in the 1-10 range, so we multiply it by 10.
|
||||
|
||||
m_skills[SKILL_ACCELERATION]->setValue((int)(kp_computed.getAccelerationEfficiency()*10));
|
||||
m_skills[SKILL_ACCELERATION]->setIcon(irr::core::stringc(
|
||||
file_manager->getAsset(FileManager::GUI, "power.png").c_str()));
|
||||
m_skills[SKILL_POWER]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_power", m_player_id);
|
||||
m_skills[SKILL_ACCELERATION]->m_properties[PROP_ID] =
|
||||
StringUtils::insertValues("@p%i_acceleration", m_player_id);
|
||||
m_skills[SKILL_ACCELERATION]->m_iconbutton->setTooltip( _("Acceleration") );
|
||||
|
||||
// The base nitro consumption is 1, higher for heavier karts.
|
||||
// Nitro efficiency is hence 90/nitro_consumption
|
||||
|
||||
m_skills[SKILL_NITRO_EFFICIENCY]->setValue((int)
|
||||
(90.0f/kp_computed.getCombinedCharacteristic()->getNitroConsumption()));
|
||||
m_skills[SKILL_NITRO_EFFICIENCY]->setIcon(irr::core::stringc(
|
||||
file_manager->getAsset(FileManager::GUI, "nitro.png").c_str()));
|
||||
m_skills[SKILL_NITRO_EFFICIENCY]->m_properties[PROP_ID] =
|
||||
StringUtils::insertValues("@p%i_nitro_efficiency", m_player_id);
|
||||
m_skills[SKILL_NITRO_EFFICIENCY]->m_iconbutton->setTooltip( _("Nitro efficiency") );
|
||||
|
||||
race_manager->setDifficulty(previous_difficulty);
|
||||
} // setValues
|
||||
|
@ -65,7 +65,8 @@ namespace GUIEngine
|
||||
{
|
||||
SKILL_MASS,
|
||||
SKILL_SPEED,
|
||||
SKILL_POWER,
|
||||
SKILL_ACCELERATION,
|
||||
SKILL_NITRO_EFFICIENCY,
|
||||
SKILL_COUNT
|
||||
};
|
||||
|
||||
|
@ -479,7 +479,7 @@ EventPropagation ListWidget::transmitEvent(Widget* w,
|
||||
m_header_elements[col].getIrrlichtElement<IGUIButton>()->setPressed(true);
|
||||
*/
|
||||
|
||||
if (m_listener) m_listener->onColumnClicked(m_sort_col);
|
||||
if (m_listener) m_listener->onColumnClicked(m_sort_col, m_sort_desc, m_sort_default);
|
||||
|
||||
return EVENT_BLOCK;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace GUIEngine
|
||||
public:
|
||||
virtual ~IListWidgetHeaderListener(){}
|
||||
|
||||
virtual void onColumnClicked(int columnId) = 0;
|
||||
virtual void onColumnClicked(int column_id, bool sort_desc, bool sort_default) = 0;
|
||||
};
|
||||
|
||||
/** \brief A vertical list widget with text entries
|
||||
|
@ -49,6 +49,7 @@ SpinnerWidget::SpinnerWidget(const bool gauge) : Widget(WTYPE_SPINNER)
|
||||
m_spinner_widget_player_id=-1;
|
||||
m_min = 0;
|
||||
m_max = 999;
|
||||
m_right_selected = false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -230,45 +231,65 @@ void SpinnerWidget::move(const int x, const int y, const int w, const int h)
|
||||
|
||||
EventPropagation SpinnerWidget::rightPressed(const int playerID)
|
||||
{
|
||||
//Log::info("SpinnerWidget", "Right pressed");
|
||||
|
||||
// if widget is deactivated, do nothing
|
||||
if (m_deactivated) return EVENT_BLOCK;
|
||||
|
||||
//Log::info("SpinnerWidget", "Right pressed");
|
||||
if (m_value+1 <= m_max)
|
||||
{
|
||||
setValue(m_value+1);
|
||||
}
|
||||
else if (m_wrap_around)
|
||||
{
|
||||
setValue(m_min);
|
||||
}
|
||||
|
||||
//GUIEngine::transmitEvent( this, m_properties[PROP_ID], playerID );
|
||||
// if right arrow is selected, let event handler move to next widget
|
||||
if (m_right_selected)
|
||||
return EVENT_BLOCK;
|
||||
else
|
||||
setSelectedButton(/* right*/ true);
|
||||
|
||||
return EVENT_LET;
|
||||
}
|
||||
} // rightPressed
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
EventPropagation SpinnerWidget::leftPressed(const int playerID)
|
||||
{
|
||||
//Log::info("SpinnerWidget", "Left pressed");
|
||||
|
||||
// if widget is deactivated, do nothing
|
||||
if (m_deactivated) return EVENT_BLOCK;
|
||||
|
||||
//Log::info("SpinnerWidget", "Left pressed");
|
||||
if (m_value-1 >= m_min)
|
||||
{
|
||||
setValue(m_value-1);
|
||||
}
|
||||
else if (m_wrap_around)
|
||||
{
|
||||
setValue(m_max);
|
||||
}
|
||||
|
||||
//GUIEngine::transmitEvent( this, m_properties[PROP_ID], playerID );
|
||||
// if right arrow is selected, select left arrow
|
||||
if (m_right_selected)
|
||||
setSelectedButton(/* right*/ false);
|
||||
// if left arrow is selected, let navigation move to next widget
|
||||
else
|
||||
return EVENT_BLOCK;
|
||||
|
||||
return EVENT_LET;
|
||||
}
|
||||
} // leftPressed
|
||||
|
||||
void SpinnerWidget::activateSelectedButton()
|
||||
{
|
||||
if (m_right_selected)
|
||||
{
|
||||
if (m_value+1 <= m_max)
|
||||
{
|
||||
setValue(m_value+1);
|
||||
}
|
||||
else if (m_wrap_around)
|
||||
{
|
||||
setValue(m_min);
|
||||
}
|
||||
}
|
||||
else // left button active
|
||||
{
|
||||
if (m_value-1 >= m_min)
|
||||
{
|
||||
setValue(m_value-1);
|
||||
}
|
||||
else if (m_wrap_around)
|
||||
{
|
||||
setValue(m_max);
|
||||
}
|
||||
}
|
||||
} // activateSelectedButton
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@ -283,11 +304,13 @@ EventPropagation SpinnerWidget::transmitEvent(Widget* w,
|
||||
|
||||
if (originator == "left")
|
||||
{
|
||||
leftPressed(playerID);
|
||||
m_right_selected = false;
|
||||
activateSelectedButton();
|
||||
}
|
||||
else if (originator == "right")
|
||||
{
|
||||
rightPressed(playerID);
|
||||
m_right_selected = true;
|
||||
activateSelectedButton();
|
||||
}
|
||||
else if (originator == "spinnerbody" || originator == m_properties[PROP_ID])
|
||||
{
|
||||
|
@ -76,6 +76,9 @@ namespace GUIEngine
|
||||
/** \brief Whether to wrap back to the first value when going "beyond" the last value */
|
||||
bool m_wrap_around;
|
||||
|
||||
/** \brief Whether the right or left arrow is the currently selected one */
|
||||
bool m_right_selected;
|
||||
|
||||
/** \brief Keeps track of the custom text in spinner (a text which isn't related to a value)
|
||||
* to remember it and set it back (example : when we deactivate the widget)
|
||||
*/
|
||||
@ -124,6 +127,11 @@ namespace GUIEngine
|
||||
int getSpinnerWidgetPlayerID() {return m_spinner_widget_player_id; }
|
||||
void unsetUseBackgroundColor() {m_use_background_color=false; }
|
||||
|
||||
void activateSelectedButton();
|
||||
void setSelectedButton(bool right) {m_right_selected = right; }
|
||||
bool isRightButtonSelected() {return m_right_selected; }
|
||||
|
||||
|
||||
|
||||
|
||||
void setListener(ISpinnerConfirmListener* listener) { m_listener = listener; }
|
||||
|
@ -396,7 +396,7 @@ void InputManager::handleStaticAction(int key, int value)
|
||||
{
|
||||
AbstractKart* kart = world->getLocalPlayerKart(0);
|
||||
if(control_is_pressed && race_manager->getMinorMode()!=
|
||||
RaceManager::MINOR_MODE_3_STRIKES)
|
||||
RaceManager::MINOR_MODE_BATTLE)
|
||||
kart->setPowerup(PowerupManager::POWERUP_RUBBERBALL,
|
||||
10000);
|
||||
else
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include "karts/controller/controller.hpp"
|
||||
#include "karts/explosion_animation.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "modes/three_strikes_battle.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "physics/triangle_mesh.hpp"
|
||||
@ -47,7 +46,6 @@
|
||||
/** Initialises the attachment each kart has.
|
||||
*/
|
||||
Attachment::Attachment(AbstractKart* kart)
|
||||
: EventRewinder()
|
||||
{
|
||||
m_type = ATTACH_NOTHING;
|
||||
m_ticks_left = 0;
|
||||
@ -148,7 +146,7 @@ void Attachment::set(AttachmentType type, int ticks,
|
||||
m_node->setMesh(attachment_manager->getMesh(ATTACH_NOLOKS_SWATTER));
|
||||
else
|
||||
m_node->setMesh(attachment_manager->getMesh(type));
|
||||
m_plugin = new Swatter(m_kart, was_bomb, bomb_scene_node);
|
||||
m_plugin = new Swatter(m_kart, was_bomb, bomb_scene_node, ticks);
|
||||
break;
|
||||
case ATTACH_BOMB:
|
||||
m_node->setMesh(attachment_manager->getMesh(type));
|
||||
@ -208,17 +206,6 @@ void Attachment::set(AttachmentType type, int ticks,
|
||||
}
|
||||
}
|
||||
m_node->setVisible(true);
|
||||
#ifndef SERVER_ONLY
|
||||
// Save event about the new attachment
|
||||
RewindManager *rwm = RewindManager::get();
|
||||
if(rwm->isEnabled() && !rwm->isRewinding())
|
||||
{
|
||||
// FIXME!!!! For now commented out
|
||||
//BareNetworkString *buffer = new BareNetworkString(2);
|
||||
//saveState(buffer);
|
||||
//rwm->addEvent(this, buffer, /*confirmed*/true);
|
||||
}
|
||||
#endif
|
||||
} // set
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -311,7 +298,7 @@ void Attachment::rewindTo(BareNetworkString *buffer)
|
||||
}
|
||||
|
||||
// If playing kart animation, don't rewind to any attacment
|
||||
if (is_removing_bomb)
|
||||
if (is_removing_bomb || m_kart->getKartAnimation())
|
||||
return;
|
||||
|
||||
// Attaching an object can be expensive (loading new models, ...)
|
||||
@ -331,16 +318,6 @@ void Attachment::rewindTo(BareNetworkString *buffer)
|
||||
/*disable_swatter_animation*/);
|
||||
} // rewindTo
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Called when going forwards in time during a rewind.
|
||||
* \param buffer Buffer with the rewind information.
|
||||
*/
|
||||
void Attachment::rewind(BareNetworkString *buffer)
|
||||
{
|
||||
// Event has same info as a state, so re-use the restore function
|
||||
rewindTo(buffer);
|
||||
} // rewind
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Selects the new attachment. In order to simplify synchronisation with the
|
||||
* server, the new item is based on the current world time.
|
||||
@ -365,7 +342,7 @@ void Attachment::hitBanana(ItemState *item_state)
|
||||
|
||||
bool add_a_new_item = true;
|
||||
|
||||
if (dynamic_cast<ThreeStrikesBattle*>(World::getWorld()) != NULL)
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE)
|
||||
{
|
||||
World::getWorld()->kartHit(m_kart->getWorldKartId());
|
||||
ExplosionAnimation::create(m_kart);
|
||||
|
@ -21,9 +21,7 @@
|
||||
|
||||
#include "config/stk_config.hpp"
|
||||
#include "items/attachment_plugin.hpp"
|
||||
#include "network/event_rewinder.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/random_generator.hpp"
|
||||
|
||||
#include <IAnimatedMeshSceneNode.h>
|
||||
using namespace irr;
|
||||
@ -46,8 +44,7 @@ class SFXBase;
|
||||
* a scene node).
|
||||
* \ingroup items
|
||||
*/
|
||||
class Attachment: public NoCopy, public scene::IAnimationEndCallBack,
|
||||
public EventRewinder
|
||||
class Attachment: public NoCopy, public scene::IAnimationEndCallBack
|
||||
{
|
||||
public:
|
||||
// Some loop in attachment.cpp depend on ATTACH_FIRST and ATTACH_MAX.
|
||||
@ -102,15 +99,12 @@ private:
|
||||
* for certain attachments. */
|
||||
AttachmentPlugin *m_plugin;
|
||||
|
||||
/** Pseudo random number generator. */
|
||||
RandomGenerator m_random;
|
||||
|
||||
/** Ticking sound for the bomb */
|
||||
SFXBase *m_bomb_sound;
|
||||
|
||||
/** Soung for exploding bubble gum shield */
|
||||
SFXBase *m_bubble_explode_sound;
|
||||
|
||||
|
||||
public:
|
||||
Attachment(AbstractKart* kart);
|
||||
~Attachment();
|
||||
@ -123,7 +117,6 @@ public:
|
||||
void set (AttachmentType type, int ticks,
|
||||
AbstractKart *previous_kart=NULL,
|
||||
bool disable_swatter_animation = false);
|
||||
virtual void rewind(BareNetworkString *buffer);
|
||||
void rewindTo(BareNetworkString *buffer);
|
||||
void saveState(BareNetworkString *buffer) const;
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "io/xml_node.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "utils/random_generator.hpp"
|
||||
|
||||
#include "utils/log.hpp" //TODO: remove after debugging is done
|
||||
|
||||
@ -90,7 +89,8 @@ Bowling::Bowling(AbstractKart *kart)
|
||||
Bowling::~Bowling()
|
||||
{
|
||||
// This will stop the sfx and delete the object.
|
||||
m_roll_sfx->deleteSFX();
|
||||
if (m_roll_sfx)
|
||||
m_roll_sfx->deleteSFX();
|
||||
|
||||
} // ~RubberBall
|
||||
|
||||
@ -175,7 +175,7 @@ bool Bowling::updateAndDelete(int ticks)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_roll_sfx->getStatus()==SFXBase::SFX_PLAYING)
|
||||
if (m_roll_sfx && m_roll_sfx->getStatus()==SFXBase::SFX_PLAYING)
|
||||
m_roll_sfx->setPosition(getXYZ());
|
||||
|
||||
return false;
|
||||
@ -207,6 +207,17 @@ bool Bowling::hit(AbstractKart* kart, PhysicalObject* obj)
|
||||
return was_real_hit;
|
||||
} // hit
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void Bowling::hideNodeWhenUndoDestruction()
|
||||
{
|
||||
Flyable::hideNodeWhenUndoDestruction();
|
||||
if (m_roll_sfx)
|
||||
{
|
||||
m_roll_sfx->deleteSFX();
|
||||
m_roll_sfx = NULL;
|
||||
}
|
||||
} // hideNodeWhenUndoDestruction
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Returns the hit effect object to use when this objects hits something.
|
||||
* \returns The hit effect object, or NULL if no hit effect should be played.
|
||||
|
@ -48,6 +48,7 @@ private:
|
||||
|
||||
/** A sound effect for rolling ball. */
|
||||
SFXBase *m_roll_sfx;
|
||||
virtual void hideNodeWhenUndoDestruction() OVERRIDE;
|
||||
|
||||
public:
|
||||
Bowling(AbstractKart* kart);
|
||||
|
@ -574,7 +574,8 @@ void Flyable::explode(AbstractKart *kart_hit, PhysicalObject *object,
|
||||
ExplosionAnimation::create(kart, getXYZ(), kart==kart_hit);
|
||||
if(kart==kart_hit && Track::getCurrentTrack()->isArena())
|
||||
{
|
||||
world->kartHit(kart->getWorldKartId());
|
||||
world->kartHit(kart->getWorldKartId(),
|
||||
m_owner->getWorldKartId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -638,6 +639,7 @@ void Flyable::restoreState(BareNetworkString *buffer, int count)
|
||||
m_body->setAngularVelocity(av);
|
||||
m_body->setInterpolationLinearVelocity(lv);
|
||||
m_body->setInterpolationAngularVelocity(av);
|
||||
setTrans(t);
|
||||
}
|
||||
uint16_t hit_and_ticks = buffer->getUInt16();
|
||||
m_has_hit_something = (hit_and_ticks >> 15) == 1;
|
||||
@ -700,6 +702,7 @@ void Flyable::hideNodeWhenUndoDestruction()
|
||||
#ifndef SERVER_ONLY
|
||||
m_node->setVisible(false);
|
||||
#endif
|
||||
moveToInfinity();
|
||||
} // hideNodeWhenUndoDestruction
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -712,11 +715,6 @@ void Flyable::handleUndoDestruction()
|
||||
|
||||
m_has_undone_destruction = true;
|
||||
|
||||
// If destroyed during rewind, than in theroy it should be safe to delete
|
||||
// without undo
|
||||
if (RewindManager::get()->isRewinding())
|
||||
return;
|
||||
|
||||
// We don't bother seeing the mesh during rewinding
|
||||
hideNodeWhenUndoDestruction();
|
||||
std::shared_ptr<Flyable> f = getShared<Flyable>();
|
||||
@ -728,9 +726,10 @@ void Flyable::handleUndoDestruction()
|
||||
{
|
||||
projectile_manager->addByUID(uid, f);
|
||||
},
|
||||
/*replay_function*/[uid]()
|
||||
/*replay_function*/[f, uid]()
|
||||
{
|
||||
projectile_manager->removeByUID(uid);
|
||||
f->moveToInfinity();
|
||||
}));
|
||||
} // handleUndoDestruction
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "graphics/sp/sp_mesh_node.hpp"
|
||||
#include "items/item_manager.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "modes/three_strikes_battle.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "tracks/arena_graph.hpp"
|
||||
@ -95,7 +94,7 @@ void ItemState::collected(const AbstractKart *kart)
|
||||
m_ticks_till_return = stk_config->time2Ticks(2.0f);
|
||||
}
|
||||
|
||||
if (dynamic_cast<ThreeStrikesBattle*>(World::getWorld()) != NULL)
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE)
|
||||
{
|
||||
m_ticks_till_return *= 3;
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "karts/controller/controller.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "physics/triangle_mesh.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
@ -503,15 +504,8 @@ void Powerup::hitBonusBox(const ItemState &item_state)
|
||||
|
||||
unsigned int n=1;
|
||||
PowerupManager::PowerupType new_powerup;
|
||||
|
||||
// Check if rubber ball is the current power up held by the kart. If so,
|
||||
// reset the bBallCollectTime to 0 before giving new powerup.
|
||||
if(m_type == PowerupManager::POWERUP_RUBBERBALL)
|
||||
powerup_manager->setBallCollectTicks(0);
|
||||
|
||||
World *world = World::getWorld();
|
||||
|
||||
|
||||
// Determine a 'random' number based on time, index of the item,
|
||||
// and position of the kart. The idea is that this process is
|
||||
// randomly enough to get the right distribution of the powerups,
|
||||
@ -548,9 +542,8 @@ void Powerup::hitBonusBox(const ItemState &item_state)
|
||||
// item. We multiply the item with a 'large' (more or less random)
|
||||
// number to spread the random values across the (typically 200)
|
||||
// weights used in the PowerupManager - same for the position.
|
||||
unsigned long random_number = item_state.getItemId()*31
|
||||
+ world->getTicksSinceStart() / 10
|
||||
+ position * 23;
|
||||
uint64_t random_number = item_state.getItemId() * 31 +
|
||||
world->getTicksSinceStart() / 10 + position * 23;
|
||||
|
||||
// Use this random number as a seed of a PRNG (based on the one in
|
||||
// bullet's btSequentialImpulseConstraintSolver) to avoid getting
|
||||
@ -567,6 +560,11 @@ void Powerup::hitBonusBox(const ItemState &item_state)
|
||||
|
||||
new_powerup = powerup_manager->getRandomPowerup(position, &n,
|
||||
random_number);
|
||||
// FIXME Disable switch and bubblegum for now in network
|
||||
if (NetworkConfig::get()->isNetworking() &&
|
||||
(new_powerup == PowerupManager::POWERUP_BUBBLEGUM ||
|
||||
new_powerup == PowerupManager::POWERUP_SWITCH))
|
||||
new_powerup = PowerupManager::POWERUP_BOWLING;
|
||||
|
||||
// Always add a new powerup in ITEM_MODE_NEW (or if the kart
|
||||
// doesn't have a powerup atm).
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "items/powerup_manager.hpp"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <irrlicht.h>
|
||||
@ -388,18 +389,18 @@ void PowerupManager::WeightsData::precomputeWeights()
|
||||
* \param random_number A random number used to 'randomly' select the item
|
||||
* that was picked.
|
||||
*/
|
||||
int PowerupManager::WeightsData::getRandomItem(int rank, unsigned int random_number)
|
||||
int PowerupManager::WeightsData::getRandomItem(int rank, uint64_t random_number)
|
||||
{
|
||||
// E.g. for battle mode with only one entry
|
||||
if(rank>(int)m_summed_weights_for_rank.size())
|
||||
rank = m_summed_weights_for_rank.size()-1;
|
||||
else if (rank<0) rank = 0; // E.g. battle mode, which has rank -1
|
||||
const std::vector<int> &summed_weights = m_summed_weights_for_rank[rank];
|
||||
const std::vector<unsigned> &summed_weights = m_summed_weights_for_rank[rank];
|
||||
// The last entry is the sum of all previous entries, i.e. the maximum
|
||||
// value
|
||||
#undef ITEM_DISTRIBUTION_DEBUG
|
||||
#ifdef ITEM_DISTRIBUTION_DEBUG
|
||||
int original_random_number = random_number;
|
||||
uint64_t original_random_number = random_number;
|
||||
#endif
|
||||
random_number = random_number % summed_weights.back();
|
||||
// Put the random number in range [1;max of summed weights],
|
||||
@ -415,7 +416,7 @@ int PowerupManager::WeightsData::getRandomItem(int rank, unsigned int random_num
|
||||
// We align with the beginning of the enum and return
|
||||
// We don't do more, because it would need to be decoded from enum later
|
||||
#ifdef ITEM_DISTRIBUTION_DEBUG
|
||||
Log::verbose("Powerup", "World %d rank %d random %d %d item %d",
|
||||
Log::verbose("Powerup", "World %d rank %d random %d %" PRIu64 " item %d",
|
||||
World::getWorld()->getTicksSinceStart(), rank, random_number,
|
||||
original_random_number, powerup);
|
||||
#endif
|
||||
@ -500,7 +501,7 @@ void PowerupManager::computeWeightsForRace(int num_karts)
|
||||
case RaceManager::MINOR_MODE_TIME_TRIAL: /* fall through */
|
||||
case RaceManager::MINOR_MODE_NORMAL_RACE: class_name="race"; break;
|
||||
case RaceManager::MINOR_MODE_FOLLOW_LEADER: class_name="ftl"; break;
|
||||
case RaceManager::MINOR_MODE_3_STRIKES: class_name="battle"; break;
|
||||
case RaceManager::MINOR_MODE_BATTLE: class_name="battle"; break;
|
||||
case RaceManager::MINOR_MODE_TUTORIAL: class_name="tutorial"; break;
|
||||
case RaceManager::MINOR_MODE_EASTER_EGG: /* fall through */
|
||||
case RaceManager::MINOR_MODE_OVERWORLD:
|
||||
@ -571,7 +572,7 @@ void PowerupManager::computeWeightsForRace(int num_karts)
|
||||
*/
|
||||
PowerupManager::PowerupType PowerupManager::getRandomPowerup(unsigned int pos,
|
||||
unsigned int *n,
|
||||
unsigned int random_number)
|
||||
uint64_t random_number)
|
||||
{
|
||||
int powerup = m_current_item_weights.getRandomItem(pos-1, random_number);
|
||||
if(powerup > POWERUP_LAST)
|
||||
|
@ -94,7 +94,7 @@ private:
|
||||
/** This field is only populated for the WeightData class that
|
||||
* is used during a race. It contains for each rank the summed
|
||||
* weights for easy lookup during a race. */
|
||||
std::vector < std::vector<int> > m_summed_weights_for_rank;
|
||||
std::vector < std::vector<unsigned> > m_summed_weights_for_rank;
|
||||
|
||||
public:
|
||||
// The friend declaration gives the PowerupManager access to the
|
||||
@ -107,7 +107,7 @@ private:
|
||||
void convertRankToSection(int rank, int *prev, int *next,
|
||||
float *weight);
|
||||
void precomputeWeights();
|
||||
int getRandomItem(int rank, unsigned int random_number);
|
||||
int getRandomItem(int rank, uint64_t random_number);
|
||||
// --------------------------------------------------------------------
|
||||
/** Sets the number of karts. */
|
||||
void setNumKarts(int num_karts) { m_num_karts = num_karts; }
|
||||
@ -143,9 +143,6 @@ private:
|
||||
/** The icon for each powerup. */
|
||||
Material* m_all_icons [POWERUP_MAX];
|
||||
|
||||
/** Last time the bouncing ball was collected */
|
||||
int m_rubber_ball_collect_ticks;
|
||||
|
||||
/** The mesh for each model (if the powerup has a model), e.g. a switch
|
||||
has none. */
|
||||
irr::scene::IMesh *m_all_meshes[POWERUP_MAX];
|
||||
@ -166,7 +163,7 @@ public:
|
||||
void LoadPowerup (PowerupType type, const XMLNode &node);
|
||||
PowerupManager::PowerupType
|
||||
getRandomPowerup(unsigned int pos, unsigned int *n,
|
||||
unsigned int random_number);
|
||||
uint64_t random_number);
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the icon(material) for a powerup. */
|
||||
Material* getIcon(int type) const {return m_all_icons [type];}
|
||||
@ -174,13 +171,6 @@ public:
|
||||
/** Returns the mesh for a certain powerup.
|
||||
* \param type Mesh type for which the model is returned. */
|
||||
irr::scene::IMesh *getMesh(int type) const {return m_all_meshes[type];}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the last time a rubber ball was collected. */
|
||||
int getBallCollectTicks() const {return m_rubber_ball_collect_ticks;}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Updates the last time at which a rubber ball was collected. */
|
||||
void setBallCollectTicks(int ticks) {m_rubber_ball_collect_ticks=ticks;}
|
||||
// ------------------------------------------------------------------------
|
||||
}; // class PowerupManager
|
||||
|
||||
extern PowerupManager* powerup_manager;
|
||||
|
@ -106,6 +106,7 @@ void RubberBall::additionalPhysicsProperties()
|
||||
// FIXME: what does the rubber ball do in case of battle mode??
|
||||
if(!world) return;
|
||||
|
||||
m_restoring_state = true;
|
||||
computeTarget();
|
||||
|
||||
// initialises the current graph node
|
||||
@ -197,6 +198,14 @@ void RubberBall::computeTarget()
|
||||
{
|
||||
LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld());
|
||||
|
||||
if (m_restoring_state)
|
||||
{
|
||||
// Update kart position from rewind data 1st time
|
||||
world->updateTrackSectors();
|
||||
world->updateRacePosition();
|
||||
m_restoring_state = false;
|
||||
}
|
||||
|
||||
for(unsigned int p = race_manager->getFinishedKarts()+1;
|
||||
p < world->getNumKarts()+1; p++)
|
||||
{
|
||||
@ -374,6 +383,11 @@ bool RubberBall::updateAndDelete(int ticks)
|
||||
return Flyable::updateAndDelete(ticks);
|
||||
}
|
||||
|
||||
// Update normal from rewind first
|
||||
const Vec3& normal =
|
||||
DriveGraph::get()->getNode(getCurrentGraphNode())->getNormal();
|
||||
TerrainInfo::update(getXYZ(), -normal);
|
||||
|
||||
// Update the target in case that the first kart was overtaken (or has
|
||||
// finished the race).
|
||||
computeTarget();
|
||||
@ -397,7 +411,7 @@ bool RubberBall::updateAndDelete(int ticks)
|
||||
bool close_to_ground = 2.0*m_previous_height < m_current_max_height;
|
||||
|
||||
float vertical_offset = close_to_ground ? 4.0f : 2.0f;
|
||||
|
||||
|
||||
// Update height of terrain (which isn't done as part of
|
||||
// Flyable::update for rubber balls.
|
||||
TerrainInfo::update(next_xyz + getNormal()*vertical_offset, -getNormal());
|
||||
@ -482,7 +496,7 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, int ticks)
|
||||
}
|
||||
|
||||
// If ball is close to the target, then explode
|
||||
if (diff.length() < m_target->getKartLength())
|
||||
if (diff.length() < m_target->getKartLength())
|
||||
hit((AbstractKart*)m_target);
|
||||
|
||||
assert(!std::isnan((*next_xyz)[0]));
|
||||
@ -691,6 +705,10 @@ void RubberBall::updateDistanceToTarget()
|
||||
{
|
||||
m_fast_ping = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fast_ping = false;
|
||||
}
|
||||
if(m_distance_to_target < m_st_target_distance &&
|
||||
height_diff < m_st_max_height_difference)
|
||||
{
|
||||
@ -785,13 +803,10 @@ BareNetworkString* RubberBall::saveState(std::vector<std::string>* ru)
|
||||
buffer->addFloat(m_t);
|
||||
buffer->addFloat(m_t_increase);
|
||||
buffer->addFloat(m_interval);
|
||||
buffer->addUInt8(m_fast_ping ? 1 : 0);
|
||||
buffer->addFloat(m_distance_to_target);
|
||||
buffer->addFloat(m_height_timer);
|
||||
buffer->addUInt32(m_delete_ticks);
|
||||
buffer->addFloat(m_current_max_height);
|
||||
buffer->addUInt8(m_aiming_at_target ? 1 : 0);
|
||||
buffer->addUInt32(m_tunnel_count);
|
||||
buffer->addUInt8(m_tunnel_count | (m_aiming_at_target ? (1 << 7) : 0));
|
||||
TrackSector::saveState(buffer);
|
||||
return buffer;
|
||||
} // saveState
|
||||
@ -800,6 +815,7 @@ BareNetworkString* RubberBall::saveState(std::vector<std::string>* ru)
|
||||
void RubberBall::restoreState(BareNetworkString *buffer, int count)
|
||||
{
|
||||
Flyable::restoreState(buffer, count);
|
||||
m_restoring_state = true;
|
||||
m_last_aimed_graph_node = buffer->getUInt32();
|
||||
m_control_points[0] = buffer->getVec3();
|
||||
m_control_points[1] = buffer->getVec3();
|
||||
@ -812,12 +828,11 @@ void RubberBall::restoreState(BareNetworkString *buffer, int count)
|
||||
m_t = buffer->getFloat();
|
||||
m_t_increase = buffer->getFloat();
|
||||
m_interval = buffer->getFloat();
|
||||
m_fast_ping = buffer->getUInt8() == 1;
|
||||
m_distance_to_target = buffer->getFloat();
|
||||
m_height_timer = buffer->getFloat();
|
||||
m_delete_ticks = buffer->getUInt32();
|
||||
m_current_max_height = buffer->getFloat();
|
||||
m_aiming_at_target = buffer->getUInt8() == 1;
|
||||
m_tunnel_count = buffer->getUInt32();
|
||||
uint8_t tunnel_and_aiming = buffer->getUInt8();
|
||||
m_tunnel_count = tunnel_and_aiming & 127;
|
||||
m_aiming_at_target = ((tunnel_and_aiming >> 7) & 1) == 1;
|
||||
TrackSector::rewindTo(buffer);
|
||||
} // restoreState
|
||||
|
@ -177,7 +177,7 @@ private:
|
||||
/** This variable counts how often a ball tunneled (in consecutive
|
||||
* frames). If a ball tunnels a certain number of times, it is
|
||||
* considered stuck and will be removed. */
|
||||
unsigned int m_tunnel_count;
|
||||
uint8_t m_tunnel_count;
|
||||
|
||||
/** A 'ping' sound effect to be played when the ball hits the ground. */
|
||||
SFXBase *m_ping_sfx;
|
||||
@ -185,6 +185,8 @@ private:
|
||||
/* Used by undo and redo the firing when rewind */
|
||||
Vec3 m_owner_init_pos, m_init_pos;
|
||||
|
||||
bool m_restoring_state;
|
||||
|
||||
void computeTarget();
|
||||
void updateDistanceToTarget();
|
||||
unsigned int getSuccessorToHitTarget(unsigned int node_index,
|
||||
|
@ -40,6 +40,8 @@
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "modes/soccer_world.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
|
||||
#define SWAT_POS_OFFSET core::vector3df(0.0, 0.2f, -0.4f)
|
||||
#define SWAT_ANGLE_MIN 45
|
||||
@ -56,14 +58,15 @@
|
||||
* attachment scene node).
|
||||
*/
|
||||
Swatter::Swatter(AbstractKart *kart, bool was_bomb,
|
||||
scene::ISceneNode* bomb_scene_node)
|
||||
: AttachmentPlugin(kart)
|
||||
scene::ISceneNode* bomb_scene_node, int ticks)
|
||||
: AttachmentPlugin(kart),
|
||||
m_swatter_start_ticks(World::getWorld()->getTicksSinceStart()),
|
||||
m_swatter_end_ticks(World::getWorld()->getTicksSinceStart() + ticks)
|
||||
{
|
||||
m_animation_phase = SWATTER_AIMING;
|
||||
m_discard_now = false;
|
||||
m_target = NULL;
|
||||
m_closest_kart = NULL;
|
||||
|
||||
m_bomb_scene_node = bomb_scene_node;
|
||||
m_swat_bomb_frame = 0.0f;
|
||||
|
||||
@ -162,19 +165,29 @@ void Swatter::updateGrahpics(float dt)
|
||||
*/
|
||||
bool Swatter::updateAndTestFinished(int ticks)
|
||||
{
|
||||
const int ticks_start = World::getWorld()->getTicksSinceStart();
|
||||
if (m_removed_bomb_ticks != std::numeric_limits<int>::max())
|
||||
{
|
||||
if (World::getWorld()->getTicksSinceStart() >= m_removed_bomb_ticks)
|
||||
if (ticks_start >= m_removed_bomb_ticks)
|
||||
return true;
|
||||
return false;
|
||||
} // if removing bomb
|
||||
|
||||
if (RewindManager::get()->isRewinding())
|
||||
return false;
|
||||
|
||||
if (!m_discard_now)
|
||||
{
|
||||
switch (m_animation_phase)
|
||||
{
|
||||
case SWATTER_AIMING:
|
||||
{
|
||||
// Avoid swatter near the start and the end lifetime of swatter
|
||||
// to make sure all clients know the existence of swatter each other
|
||||
if (ticks_start - m_swatter_start_ticks < 60 ||
|
||||
m_swatter_end_ticks - ticks_start < 60)
|
||||
return false;
|
||||
|
||||
chooseTarget();
|
||||
pointToTarget();
|
||||
if(!m_target || !m_closest_kart) break;
|
||||
@ -198,8 +211,7 @@ bool Swatter::updateAndTestFinished(int ticks)
|
||||
{
|
||||
// Start squashing
|
||||
m_animation_phase = SWATTER_TO_TARGET;
|
||||
m_start_swat_ticks =
|
||||
World::getWorld()->getTicksSinceStart() + 20;
|
||||
m_start_swat_ticks = ticks_start + 20;
|
||||
// Setup the animation
|
||||
m_scene_node->setCurrentFrame(0.0f);
|
||||
m_scene_node->setLoopMode(false);
|
||||
@ -207,14 +219,8 @@ bool Swatter::updateAndTestFinished(int ticks)
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
// Play swat sound
|
||||
const int wt = World::getWorld()->getTicksSinceStart();
|
||||
auto it = m_swat_sound_ticks.find(wt);
|
||||
if (it == m_swat_sound_ticks.end())
|
||||
{
|
||||
m_swat_sound->setPosition(swatter_pos);
|
||||
m_swat_sound->play();
|
||||
m_swat_sound_ticks.insert(wt);
|
||||
}
|
||||
m_swat_sound->setPosition(swatter_pos);
|
||||
m_swat_sound->play();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -223,17 +229,16 @@ bool Swatter::updateAndTestFinished(int ticks)
|
||||
{
|
||||
pointToTarget();
|
||||
// Did we just finish the first part of the movement?
|
||||
if (World::getWorld()->getTicksSinceStart() > m_start_swat_ticks)
|
||||
if (ticks_start > m_start_swat_ticks)
|
||||
{
|
||||
m_start_swat_ticks = std::numeric_limits<int>::max();
|
||||
// Squash the karts and items around and
|
||||
// change the current phase
|
||||
squashThingsAround();
|
||||
m_animation_phase = SWATTER_FROM_TARGET;
|
||||
const int end_ticks =
|
||||
World::getWorld()->getTicksSinceStart() + 60;
|
||||
const int end_ticks = ticks_start + 60;
|
||||
if (race_manager
|
||||
->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES ||
|
||||
->getMinorMode()==RaceManager::MINOR_MODE_BATTLE ||
|
||||
race_manager
|
||||
->getMinorMode()==RaceManager::MINOR_MODE_SOCCER)
|
||||
{
|
||||
@ -253,9 +258,9 @@ bool Swatter::updateAndTestFinished(int ticks)
|
||||
|
||||
if (m_discard_now)
|
||||
{
|
||||
return World::getWorld()->getTicksSinceStart() > m_end_swat_ticks;
|
||||
return ticks_start > m_end_swat_ticks;
|
||||
}
|
||||
else if (World::getWorld()->getTicksSinceStart() > m_end_swat_ticks)
|
||||
else if (ticks_start > m_end_swat_ticks)
|
||||
{
|
||||
m_animation_phase = SWATTER_AIMING;
|
||||
m_end_swat_ticks = std::numeric_limits<int>::max();
|
||||
@ -335,8 +340,23 @@ void Swatter::squashThingsAround()
|
||||
{
|
||||
const KartProperties *kp = m_kart->getKartProperties();
|
||||
|
||||
m_closest_kart->setSquash(kp->getSwatterSquashDuration(),
|
||||
kp->getSwatterSquashSlowdown());
|
||||
AbstractKart* closest_kart = m_closest_kart;
|
||||
float duration = kp->getSwatterSquashDuration();
|
||||
float slowdown = kp->getSwatterSquashSlowdown();
|
||||
closest_kart->setSquash(duration, slowdown);
|
||||
|
||||
// Locally add a event to replay the squash during rewind
|
||||
if (NetworkConfig::get()->isNetworking() &&
|
||||
NetworkConfig::get()->isClient())
|
||||
{
|
||||
RewindManager::get()->addRewindInfoEventFunction(new
|
||||
RewindInfoEventFunction(World::getWorld()->getTicksSinceStart(),
|
||||
/*undo_function*/[](){},
|
||||
/*replay_function*/[closest_kart, duration, slowdown]()
|
||||
{
|
||||
closest_kart->setSquash(duration, slowdown);
|
||||
}));
|
||||
}
|
||||
|
||||
// Handle achievement if the swatter is used by the current player
|
||||
if (m_kart->getController()->canGetAchievements())
|
||||
@ -357,7 +377,8 @@ void Swatter::squashThingsAround()
|
||||
if (m_closest_kart->isSquashed())
|
||||
{
|
||||
// The kart may not be squashed if it was protected by a bubblegum shield
|
||||
World::getWorld()->kartHit(m_closest_kart->getWorldKartId());
|
||||
World::getWorld()->kartHit(m_closest_kart->getWorldKartId(),
|
||||
m_kart->getWorldKartId());
|
||||
}
|
||||
|
||||
// TODO: squash items
|
||||
|
@ -74,14 +74,17 @@ private:
|
||||
/** For some reason the built-in animation system doesn't work correctly here?? */
|
||||
float m_swat_bomb_frame;
|
||||
|
||||
std::set<int> m_swat_sound_ticks;
|
||||
|
||||
int m_start_swat_ticks;
|
||||
|
||||
int m_end_swat_ticks;
|
||||
|
||||
const int m_swatter_start_ticks;
|
||||
|
||||
const int m_swatter_end_ticks;
|
||||
|
||||
public:
|
||||
Swatter(AbstractKart *kart, bool was_bomb,
|
||||
scene::ISceneNode* bomb_scene_node);
|
||||
scene::ISceneNode* bomb_scene_node, int ticks);
|
||||
virtual ~Swatter();
|
||||
void updateGrahpics(float dt) OVERRIDE;
|
||||
bool updateAndTestFinished(int ticks) OVERRIDE;
|
||||
|
@ -23,9 +23,12 @@
|
||||
#include "karts/kart_model.hpp"
|
||||
#include "karts/skidding.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
#include "physics/physics.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
/** Constructor. Note that kart can be NULL in case that the animation is
|
||||
* used for a basket ball in a cannon animation.
|
||||
* \param kart Pointer to the kart that is animated, or NULL if the
|
||||
@ -37,7 +40,11 @@ AbstractKartAnimation::AbstractKartAnimation(AbstractKart *kart,
|
||||
m_timer = 0;
|
||||
m_kart = kart;
|
||||
m_name = name;
|
||||
|
||||
m_end_transform = btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
m_end_transform.setOrigin(Vec3(std::numeric_limits<float>::max()));
|
||||
m_created_ticks = World::getWorld()->getTicksSinceStart();
|
||||
m_check_created_ticks = std::make_shared<int>(-1);
|
||||
m_confirmed_by_network = false;
|
||||
// Remove previous animation if there is one
|
||||
#ifndef DEBUG
|
||||
// Use this code in non-debug mode to avoid a memory leak (and messed
|
||||
@ -82,13 +89,19 @@ AbstractKartAnimation::~AbstractKartAnimation()
|
||||
if(m_timer < 0 && m_kart)
|
||||
{
|
||||
m_kart->getBody()->setAngularVelocity(btVector3(0,0,0));
|
||||
Vec3 linear_velocity = m_kart->getBody()->getLinearVelocity();
|
||||
btTransform transform = m_end_transform.getOrigin().x() ==
|
||||
std::numeric_limits<float>::max() ?
|
||||
m_kart->getBody()->getWorldTransform() : m_end_transform;
|
||||
m_kart->getBody()->setLinearVelocity(linear_velocity);
|
||||
m_kart->getBody()->proceedToTransform(transform);
|
||||
m_kart->setTrans(transform);
|
||||
Physics::getInstance()->addKart(m_kart);
|
||||
|
||||
if (RewindManager::get()->useLocalEvent())
|
||||
{
|
||||
AbstractKart* kart = m_kart;
|
||||
Vec3 linear_velocity = kart->getBody()->getLinearVelocity();
|
||||
Vec3 angular_velocity = kart->getBody()->getAngularVelocity();
|
||||
btTransform transform = kart->getBody()->getWorldTransform();
|
||||
RewindManager::get()->addRewindInfoEventFunction(new
|
||||
RewindInfoEventFunction(
|
||||
World::getWorld()->getTicksSinceStart(),
|
||||
@ -98,16 +111,49 @@ AbstractKartAnimation::~AbstractKartAnimation()
|
||||
},
|
||||
[kart, linear_velocity, angular_velocity, transform]()
|
||||
{
|
||||
Physics::getInstance()->addKart(kart);
|
||||
kart->getBody()->setLinearVelocity(linear_velocity);
|
||||
kart->getBody()->setAngularVelocity(angular_velocity);
|
||||
kart->getBody()->setLinearVelocity(linear_velocity);
|
||||
kart->getBody()->proceedToTransform(transform);
|
||||
kart->setTrans(transform);
|
||||
Physics::getInstance()->addKart(kart);
|
||||
}));
|
||||
}
|
||||
}
|
||||
} // ~AbstractKartAnimation
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AbstractKartAnimation::addNetworkAnimationChecker()
|
||||
{
|
||||
if (NetworkConfig::get()->isNetworking() &&
|
||||
NetworkConfig::get()->isClient())
|
||||
{
|
||||
std::weak_ptr<int> cct = m_check_created_ticks;
|
||||
RewindManager::get()->addRewindInfoEventFunction(new
|
||||
RewindInfoEventFunction(m_created_ticks,
|
||||
[](){},
|
||||
[](){},
|
||||
/*delete_function*/[cct]()
|
||||
{
|
||||
auto cct_sp = cct.lock();
|
||||
if (!cct_sp)
|
||||
return;
|
||||
*cct_sp = World::getWorld()->getTicksSinceStart();
|
||||
}));
|
||||
}
|
||||
} // addNetworkAnimationChecker
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AbstractKartAnimation::checkNetworkAnimationCreationSucceed()
|
||||
{
|
||||
if (!m_confirmed_by_network && *m_check_created_ticks != -1 &&
|
||||
World::getWorld()->getTicksSinceStart() > *m_check_created_ticks)
|
||||
{
|
||||
Log::warn("AbstractKartAnimation",
|
||||
"No animation has been created on server, remove locally.");
|
||||
m_timer = -1.0f;
|
||||
}
|
||||
} // checkNetworkAnimationCreationSucceed
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Updates the timer, and if it expires (<0), the kart animation will be
|
||||
* removed from the kart and this object will be deleted.
|
||||
|
@ -19,9 +19,12 @@
|
||||
#ifndef HEADER_ABSTRACT_KART_ANIMATION_HPP
|
||||
#define HEADER_ABSTRACT_KART_ANIMATION_HPP
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/vec3.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class AbstractKart;
|
||||
@ -40,6 +43,13 @@ class AbstractKartAnimation: public NoCopy
|
||||
private:
|
||||
/** Name of this animation, used for debug prints only. */
|
||||
std::string m_name;
|
||||
|
||||
std::shared_ptr<int> m_check_created_ticks;
|
||||
|
||||
int m_created_ticks;
|
||||
|
||||
bool m_confirmed_by_network;
|
||||
|
||||
protected:
|
||||
/** A pointer to the kart which is animated by this class. */
|
||||
AbstractKart *m_kart;
|
||||
@ -47,6 +57,10 @@ protected:
|
||||
/** Timer for the explosion. */
|
||||
float m_timer;
|
||||
|
||||
btTransform m_end_transform;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void addNetworkAnimationChecker();
|
||||
public:
|
||||
AbstractKartAnimation(AbstractKart *kart,
|
||||
const std::string &name);
|
||||
@ -59,6 +73,20 @@ public:
|
||||
/** To easily allow printing the name of the animation being used atm.
|
||||
* Used in AstractKart in case of an incorrect sequence of calls. */
|
||||
virtual const std::string &getName() const { return m_name; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool useEarlyEndTransform() const { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
const btTransform& getEndTransform() const { return m_end_transform; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setEndTransform(const btTransform& t)
|
||||
{
|
||||
if (!useEarlyEndTransform())
|
||||
return;
|
||||
m_confirmed_by_network = true;
|
||||
m_end_transform = t;
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
void checkNetworkAnimationCreationSucceed();
|
||||
|
||||
}; // AbstractKartAnimation
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
#include "LinearMath/btTransform.h"
|
||||
|
||||
/** The constructor for the cannon animation.
|
||||
/** The constructor for the cannon animation.
|
||||
* \param kart The kart to be animated. Can also be NULL if a basket ball
|
||||
* etc is animated (e.g. cannon animation).
|
||||
* \param ipo The IPO (blender interpolation curve) which the kart
|
||||
@ -169,11 +169,11 @@ void CannonAnimation::init(Ipo *ipo, const Vec3 &start_left,
|
||||
|
||||
// Compute the original heading of the kart. At the end of the cannon,
|
||||
// the kart should be parallel to the curve, but at the beginning it
|
||||
// the kart should be parallel to the curve and facing forwards, but
|
||||
// at the beginning it might not be. The initial rotation between the
|
||||
// tangent of the curce and the kart is stored as a m_delta_heading,
|
||||
// which will be applied to the curve orientation in update, but reduced
|
||||
// over time till it becomes 0 at the end of the curve. The effect is that
|
||||
// the kart should be parallel to the curve and facing forwards, but
|
||||
// at the beginning it might not be. The initial rotation between the
|
||||
// tangent of the curce and the kart is stored as a m_delta_heading,
|
||||
// which will be applied to the curve orientation in update, but reduced
|
||||
// over time till it becomes 0 at the end of the curve. The effect is that
|
||||
// initially (t=0) the kart will keep its (non-orhtogonal) rotation,
|
||||
// but smoothly this will adjusted until at the end the kart will be
|
||||
// facing forwards again.
|
||||
|
@ -80,6 +80,7 @@ public:
|
||||
const Vec3 &end_left, const Vec3 &end_right);
|
||||
virtual ~CannonAnimation();
|
||||
virtual void update(float dt);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool useEarlyEndTransform() const { return false; }
|
||||
}; // CannonAnimation
|
||||
#endif
|
||||
|
@ -87,7 +87,7 @@ AIBaseLapController::AIBaseLapController(AbstractKart *kart)
|
||||
: AIBaseController(kart)
|
||||
{
|
||||
|
||||
if (race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
if (race_manager->getMinorMode()!=RaceManager::MINOR_MODE_BATTLE &&
|
||||
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
|
||||
{
|
||||
m_world = dynamic_cast<LinearWorld*>(World::getWorld());
|
||||
|
@ -56,7 +56,7 @@ EndController::EndController(AbstractKart *kart,
|
||||
: AIBaseLapController(kart)
|
||||
{
|
||||
m_previous_controller = prev_controller;
|
||||
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_BATTLE &&
|
||||
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
|
||||
{
|
||||
// Overwrite the random selected default path from AIBaseLapController
|
||||
@ -104,7 +104,6 @@ EndController::EndController(AbstractKart *kart,
|
||||
#ifdef AI_DEBUG
|
||||
m_debug_sphere = irr_driver->getSceneManager()->addSphereSceneNode(1);
|
||||
#endif
|
||||
m_kart->setSlowdown(MaxSpeed::MS_DECREASE_AI, 0.3f, 2);
|
||||
|
||||
// Set the name of the previous controller as this controller name, otherwise
|
||||
// we get the incorrect name when printing statistics in profile mode.
|
||||
@ -132,7 +131,7 @@ void EndController::reset()
|
||||
|
||||
m_track_node = Graph::UNKNOWN_SECTOR;
|
||||
// In battle mode there is no quad graph, so nothing to do in this case
|
||||
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_BATTLE &&
|
||||
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
|
||||
{
|
||||
DriveGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
|
||||
@ -185,7 +184,7 @@ void EndController::update(int ticks)
|
||||
AIBaseLapController::update(ticks);
|
||||
|
||||
// In case of battle mode: don't do anything
|
||||
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES ||
|
||||
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_BATTLE ||
|
||||
race_manager->getMinorMode()==RaceManager::MINOR_MODE_SOCCER ||
|
||||
race_manager->getMinorMode()==RaceManager::MINOR_MODE_EASTER_EGG)
|
||||
{
|
||||
@ -195,6 +194,9 @@ void EndController::update(int ticks)
|
||||
m_controls->setBrake(m_kart->getSpeed()>0);
|
||||
return;
|
||||
}
|
||||
// Keep update slow down in case for network rewind overwrite it
|
||||
m_kart->setSlowdown(MaxSpeed::MS_DECREASE_AI, 0.3f, 2);
|
||||
|
||||
/*Get information that is needed by more than 1 of the handling funcs*/
|
||||
//Detect if we are going to crash with the track and/or kart
|
||||
calcSteps();
|
||||
|
@ -72,7 +72,8 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
|
||||
const Vec3 &explosion_position,
|
||||
bool direct_hit)
|
||||
: AbstractKartAnimation(kart, "ExplosionAnimation")
|
||||
{
|
||||
{
|
||||
m_end_transform = m_kart->getTrans();
|
||||
m_xyz = m_kart->getXYZ();
|
||||
m_orig_xyz = m_xyz;
|
||||
m_normal = m_kart->getNormal();
|
||||
@ -109,8 +110,8 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
|
||||
m_kart->showStarEffect(t);
|
||||
|
||||
m_kart->getAttachment()->clear();
|
||||
|
||||
}; // ExplosionAnimation
|
||||
addNetworkAnimationChecker();
|
||||
} // ExplosionAnimation
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
ExplosionAnimation::~ExplosionAnimation()
|
||||
|
@ -61,13 +61,10 @@
|
||||
#include "karts/rescue_animation.hpp"
|
||||
#include "karts/skidding.hpp"
|
||||
#include "main_loop.hpp"
|
||||
#include "modes/overworld.hpp"
|
||||
#include "modes/soccer_world.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "modes/capture_the_flag.hpp"
|
||||
#include "modes/linear_world.hpp"
|
||||
#include "modes/overworld.hpp"
|
||||
#include "modes/soccer_world.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/race_event_manager.hpp"
|
||||
#include "network/rewind_manager.hpp"
|
||||
@ -971,7 +968,7 @@ void Kart::finishedRace(float time, bool from_server)
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL ||
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER ||
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES ||
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE ||
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER ||
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_EASTER_EGG)
|
||||
{
|
||||
@ -1026,11 +1023,17 @@ void Kart::setRaceResult()
|
||||
}
|
||||
}
|
||||
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER ||
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES)
|
||||
race_manager->getMajorMode() == RaceManager::MAJOR_MODE_3_STRIKES)
|
||||
{
|
||||
// the kart wins if it isn't eliminated
|
||||
m_race_result = !this->isEliminated();
|
||||
}
|
||||
else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL)
|
||||
{
|
||||
// the top kart wins
|
||||
FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld());
|
||||
m_race_result = ffa->getKartAtPosition(1) == this;
|
||||
}
|
||||
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
|
||||
{
|
||||
SoccerWorld* sw = dynamic_cast<SoccerWorld*>(World::getWorld());
|
||||
@ -1080,9 +1083,10 @@ void Kart::collectedItem(ItemState *item_state)
|
||||
// slow down
|
||||
m_bubblegum_ticks =
|
||||
stk_config->time2Ticks(m_kart_properties->getBubblegumDuration());
|
||||
m_bubblegum_torque = ((rand()%2)
|
||||
? m_kart_properties->getBubblegumTorque()
|
||||
: -m_kart_properties->getBubblegumTorque());
|
||||
m_bubblegum_torque =
|
||||
((World::getWorld()->getTicksSinceStart() / 10) % 2 == 0) ?
|
||||
m_kart_properties->getBubblegumTorque() :
|
||||
-m_kart_properties->getBubblegumTorque();
|
||||
m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_BUBBLE,
|
||||
m_kart_properties->getBubblegumSpeedFraction() ,
|
||||
m_kart_properties->getBubblegumFadeInTicks(),
|
||||
@ -2953,7 +2957,7 @@ void Kart::updateGraphics(float dt)
|
||||
{
|
||||
m_squash_time -= dt;
|
||||
// If squasing time ends, reset the model
|
||||
if (m_squash_time <= 0.0f)
|
||||
if (m_squash_time <= 0.0f || !isSquashed())
|
||||
{
|
||||
m_squash_time = std::numeric_limits<float>::max();
|
||||
m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
|
||||
|
@ -464,7 +464,11 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
/** Makes a kart invulnerable for a certain amount of time. */
|
||||
virtual void setInvulnerableTicks(int ticks) OVERRIDE
|
||||
{
|
||||
{
|
||||
// The rest 2 bits are saving fire clicked and animation status for
|
||||
// rewind
|
||||
if (ticks > 16383)
|
||||
ticks = 16383;
|
||||
m_invulnerable_ticks = ticks;
|
||||
} // setInvulnerableTicks
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -552,15 +552,32 @@ bool KartProperties::isInGroup(const std::string &group) const
|
||||
} // isInGroups
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
float KartProperties::getAvgPower() const
|
||||
/** This function returns a weighted average of engine power divide by mass
|
||||
* for use as a single number summing-up acceleration's efficiency,
|
||||
* e.g. for use in kart selection. */
|
||||
float KartProperties::getAccelerationEfficiency() const
|
||||
{
|
||||
float sum = 0;
|
||||
std::vector<float> gear_power_increase = m_combined_characteristic->getGearPowerIncrease();
|
||||
float power = m_combined_characteristic->getEnginePower();
|
||||
for (unsigned int i = 0; i < gear_power_increase.size(); ++i)
|
||||
sum += gear_power_increase[i] * power;
|
||||
return sum / gear_power_increase.size();
|
||||
} // getAvgPower
|
||||
std::vector<float> gear_switch_ratio = m_combined_characteristic->getGearSwitchRatio();
|
||||
unsigned current_gear = 0;
|
||||
float sum = 0;
|
||||
float base_accel = m_combined_characteristic->getEnginePower()
|
||||
/ m_combined_characteristic->getMass();
|
||||
|
||||
// We evaluate acceleration at increments of 0.01x max speed
|
||||
// up to 1,1x max speed.
|
||||
// Acceleration at low speeds is made to matter more
|
||||
for (unsigned int i = 1; i<=110; i++)
|
||||
{
|
||||
sum += gear_power_increase[current_gear] * base_accel * (150-i);
|
||||
if (i/100 >= gear_switch_ratio[current_gear] &&
|
||||
(current_gear+1 < gear_power_increase.size() ))
|
||||
current_gear++;
|
||||
}
|
||||
|
||||
// 10395 is the sum of the (150-i) factors
|
||||
return (sum / 10395);
|
||||
} // getAccelerationEfficiency
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Script-generated content generated by tools/create_kart_properties.py getter
|
||||
@ -1220,3 +1237,4 @@ bool KartProperties::getSkidEnabled() const
|
||||
} // getSkidEnabled
|
||||
|
||||
/* <characteristics-end kpgetter> */
|
||||
|
||||
|
@ -367,7 +367,7 @@ public:
|
||||
} // getPhysicalWheelPosition
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
float getAvgPower() const;
|
||||
float getAccelerationEfficiency() const;
|
||||
|
||||
|
||||
// Script-generated content generated by tools/create_kart_properties.py defs
|
||||
@ -518,3 +518,4 @@ public:
|
||||
}; // KartProperties
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "items/attachment.hpp"
|
||||
#include "items/powerup.hpp"
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/abstract_kart_animation.hpp"
|
||||
#include "karts/controller/player_controller.hpp"
|
||||
#include "karts/kart_properties.hpp"
|
||||
#include "karts/max_speed.hpp"
|
||||
@ -78,11 +79,14 @@ void KartRewinder::saveTransform()
|
||||
// ----------------------------------------------------------------------------
|
||||
void KartRewinder::computeError()
|
||||
{
|
||||
if (!getKartAnimation())
|
||||
AbstractKartAnimation* ka = getKartAnimation();
|
||||
if (ka == NULL)
|
||||
{
|
||||
Moveable::checkSmoothing();
|
||||
m_skidding->checkSmoothing();
|
||||
}
|
||||
else
|
||||
ka->checkNetworkAnimationCreationSucceed();
|
||||
|
||||
float diff = fabsf(m_prev_steering - AbstractKart::getSteerPercent());
|
||||
if (diff > 0.05f)
|
||||
@ -110,14 +114,37 @@ BareNetworkString* KartRewinder::saveState(std::vector<std::string>* ru)
|
||||
const int MEMSIZE = 17*sizeof(float) + 9+3;
|
||||
|
||||
BareNetworkString *buffer = new BareNetworkString(MEMSIZE);
|
||||
const btRigidBody *body = getBody();
|
||||
|
||||
// 1) Physics values: transform and velocities
|
||||
// 1) Firing and related handling
|
||||
// -----------
|
||||
buffer->addUInt16(m_bubblegum_ticks);
|
||||
buffer->addUInt16(m_view_blocked_by_plunger);
|
||||
// m_invulnerable_ticks will not be negative
|
||||
AbstractKartAnimation* ka = getKartAnimation();
|
||||
bool has_animation = ka != NULL && ka->useEarlyEndTransform();
|
||||
uint16_t fire_and_invulnerable = (m_fire_clicked ? 1 << 15 : 0) |
|
||||
(has_animation ? 1 << 14 : 0) | m_invulnerable_ticks;
|
||||
buffer->addUInt16(fire_and_invulnerable);
|
||||
|
||||
// 2) Kart animation status (tells the end transformation) or
|
||||
// physics values (transform and velocities)
|
||||
// -------------------------------------------
|
||||
const btTransform &t = body->getWorldTransform();
|
||||
buffer->add(t.getOrigin());
|
||||
btQuaternion q = t.getRotation();
|
||||
buffer->add(q);
|
||||
btRigidBody *body = getBody();
|
||||
if (has_animation)
|
||||
{
|
||||
const btTransform& trans = ka->getEndTransform();
|
||||
buffer->add(trans.getOrigin());
|
||||
btQuaternion quat = trans.getRotation();
|
||||
buffer->add(quat);
|
||||
}
|
||||
else
|
||||
{
|
||||
const btTransform &t = body->getWorldTransform();
|
||||
buffer->add(t.getOrigin());
|
||||
btQuaternion q = t.getRotation();
|
||||
buffer->add(q);
|
||||
}
|
||||
|
||||
buffer->add(body->getLinearVelocity());
|
||||
buffer->add(body->getAngularVelocity());
|
||||
buffer->addFloat(m_vehicle->getMinSpeed());
|
||||
@ -130,34 +157,25 @@ BareNetworkString* KartRewinder::saveState(std::vector<std::string>* ru)
|
||||
buffer->addFloat(m_vehicle->getCentralImpulseTime());
|
||||
buffer->add(m_vehicle->getAdditionalImpulse());
|
||||
|
||||
// 2) Steering and other player controls
|
||||
// 3) Steering and other player controls
|
||||
// -------------------------------------
|
||||
getControls().saveState(buffer);
|
||||
getController()->saveState(buffer);
|
||||
|
||||
// 3) Attachment, powerup, nitro
|
||||
// 4) Attachment, powerup, nitro
|
||||
// -----------------------------
|
||||
getAttachment()->saveState(buffer);
|
||||
getPowerup()->saveState(buffer);
|
||||
buffer->addFloat(getEnergy());
|
||||
|
||||
// 4) Max speed info
|
||||
// 5) Max speed info
|
||||
// ------------------
|
||||
m_max_speed->saveState(buffer);
|
||||
|
||||
// 5) Skidding
|
||||
// 6) Skidding
|
||||
// -----------
|
||||
m_skidding->saveState(buffer);
|
||||
|
||||
// 6) Firing and related handling
|
||||
// -----------
|
||||
buffer->addUInt16(m_bubblegum_ticks);
|
||||
buffer->addUInt16(m_view_blocked_by_plunger);
|
||||
// m_invulnerable_ticks will not be negative
|
||||
uint16_t fire_and_invulnerable = (m_fire_clicked ? 1 << 15 : 0) |
|
||||
m_invulnerable_ticks;
|
||||
buffer->addUInt16(fire_and_invulnerable);
|
||||
|
||||
return buffer;
|
||||
} // saveState
|
||||
|
||||
@ -169,19 +187,36 @@ BareNetworkString* KartRewinder::saveState(std::vector<std::string>* ru)
|
||||
*/
|
||||
void KartRewinder::restoreState(BareNetworkString *buffer, int count)
|
||||
{
|
||||
// 1) Physics values: transform and velocities
|
||||
// -------------------------------------------
|
||||
|
||||
// 1) Firing and related handling
|
||||
// -----------
|
||||
m_bubblegum_ticks = buffer->getUInt16();
|
||||
m_view_blocked_by_plunger = buffer->getUInt16();
|
||||
uint16_t fire_and_invulnerable = buffer->getUInt16();
|
||||
m_fire_clicked = ((fire_and_invulnerable >> 15) & 1) == 1;
|
||||
bool has_animation = ((fire_and_invulnerable >> 14) & 1) == 1;
|
||||
m_invulnerable_ticks = fire_and_invulnerable & ~(1 << 14);
|
||||
|
||||
// 2) Kart animation status or transform and velocities
|
||||
// -----------
|
||||
btTransform t;
|
||||
t.setOrigin(buffer->getVec3());
|
||||
t.setRotation(buffer->getQuat());
|
||||
btRigidBody *body = getBody();
|
||||
Vec3 lv = buffer->getVec3();
|
||||
Vec3 av = buffer->getVec3();
|
||||
|
||||
if (has_animation)
|
||||
{
|
||||
AbstractKartAnimation* ka = getKartAnimation();
|
||||
if (ka)
|
||||
ka->setEndTransform(t);
|
||||
}
|
||||
|
||||
// Don't restore to phyics position if showing kart animation
|
||||
if (!getKartAnimation())
|
||||
{
|
||||
// Clear any forces applied (like by plunger or bubble gum torque)
|
||||
btRigidBody *body = getBody();
|
||||
body->clearForces();
|
||||
body->setLinearVelocity(lv);
|
||||
body->setAngularVelocity(av);
|
||||
@ -213,12 +248,12 @@ void KartRewinder::restoreState(BareNetworkString *buffer, int count)
|
||||
// (i.e. different terrain --> different slowdown).
|
||||
m_vehicle->updateAllWheelTransformsWS();
|
||||
|
||||
// 2) Steering and other controls
|
||||
// 3) Steering and other controls
|
||||
// ------------------------------
|
||||
getControls().rewindTo(buffer);
|
||||
getController()->rewindTo(buffer);
|
||||
|
||||
// 3) Attachment, powerup, nitro
|
||||
// 4) Attachment, powerup, nitro
|
||||
// ------------------------------
|
||||
getAttachment()->rewindTo(buffer);
|
||||
// Required for going back to anvil when rewinding
|
||||
@ -228,21 +263,14 @@ void KartRewinder::restoreState(BareNetworkString *buffer, int count)
|
||||
float nitro = buffer->getFloat();
|
||||
setEnergy(nitro);
|
||||
|
||||
// 4) Max speed info
|
||||
// 5) Max speed info
|
||||
// ------------------
|
||||
m_max_speed->rewindTo(buffer);
|
||||
|
||||
// 5) Skidding
|
||||
// 6) Skidding
|
||||
// -----------
|
||||
m_skidding->rewindTo(buffer);
|
||||
|
||||
// 6) Firing and related handling
|
||||
// -----------
|
||||
m_bubblegum_ticks = buffer->getUInt16();
|
||||
m_view_blocked_by_plunger = buffer->getUInt16();
|
||||
uint16_t fire_and_invulnerable = buffer->getUInt16();
|
||||
m_fire_clicked = (fire_and_invulnerable >> 15) == 1;
|
||||
m_invulnerable_ticks = fire_and_invulnerable & ~(1 << 15);
|
||||
} // restoreState
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -265,6 +293,7 @@ std::function<void()> KartRewinder::getLocalStateRestoreFunction()
|
||||
bool has_started = m_has_started;
|
||||
int brake_ticks = m_brake_ticks;
|
||||
int8_t min_nitro_ticks = m_min_nitro_ticks;
|
||||
float bubblegum_torque = m_bubblegum_torque;
|
||||
|
||||
// Attachment local state
|
||||
float initial_speed = getAttachment()->getInitialSpeed();
|
||||
@ -288,13 +317,14 @@ std::function<void()> KartRewinder::getLocalStateRestoreFunction()
|
||||
// Skidding local state
|
||||
float remaining_jump_time = m_skidding->m_remaining_jump_time;
|
||||
|
||||
return [has_started, brake_ticks, min_nitro_ticks,
|
||||
return [has_started, brake_ticks, min_nitro_ticks, bubblegum_torque,
|
||||
initial_speed, steer_val_l, steer_val_r, current_fraction,
|
||||
max_speed_fraction, remaining_jump_time, this]()
|
||||
{
|
||||
m_has_started = has_started;
|
||||
m_brake_ticks = brake_ticks;
|
||||
m_min_nitro_ticks = min_nitro_ticks;
|
||||
m_bubblegum_torque = bubblegum_torque;
|
||||
getAttachment()->setInitialSpeed(initial_speed);
|
||||
PlayerController* pc = dynamic_cast<PlayerController*>(m_controller);
|
||||
if (pc)
|
||||
|
@ -45,6 +45,15 @@
|
||||
RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
|
||||
: AbstractKartAnimation(kart, "RescueAnimation")
|
||||
{
|
||||
btTransform prev_trans = kart->getTrans();
|
||||
// Get the required final physicial transform for network, then reset back
|
||||
// to the original transform
|
||||
World::getWorld()->moveKartAfterRescue(kart);
|
||||
|
||||
m_end_transform = kart->getTrans();
|
||||
kart->getBody()->setCenterOfMassTransform(prev_trans);
|
||||
kart->setTrans(prev_trans);
|
||||
|
||||
m_referee = new Referee(*m_kart);
|
||||
m_kart->getNode()->addChild(m_referee->getSceneNode());
|
||||
m_timer = m_kart->getKartProperties()->getRescueDuration();
|
||||
@ -56,7 +65,7 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
|
||||
// Determine maximum rescue height with up-raycast
|
||||
float max_height = m_kart->getKartProperties()->getRescueHeight();
|
||||
float hit_dest = maximumHeight();
|
||||
|
||||
|
||||
max_height = std::min(hit_dest, max_height);
|
||||
m_velocity = max_height / m_timer;
|
||||
|
||||
@ -80,9 +89,9 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
|
||||
{
|
||||
m_des_rotation = m_orig_rotation;
|
||||
}
|
||||
|
||||
|
||||
// Add a hit unless it was auto-rescue
|
||||
if (race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES &&
|
||||
if (race_manager->getMinorMode()==RaceManager::MINOR_MODE_BATTLE &&
|
||||
!is_auto_rescue)
|
||||
{
|
||||
ThreeStrikesBattle *world=(ThreeStrikesBattle*)World::getWorld();
|
||||
@ -91,8 +100,8 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
|
||||
world->increaseRescueCount();
|
||||
}
|
||||
|
||||
}; // RescueAnimation
|
||||
|
||||
addNetworkAnimationChecker();
|
||||
} // RescueAnimation
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This object is automatically destroyed when the timer expires.
|
||||
@ -106,7 +115,9 @@ RescueAnimation::~RescueAnimation()
|
||||
m_referee = NULL;
|
||||
} // ~RescueAnimation
|
||||
|
||||
// Determine maximum rescue height with up-raycast
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Determine maximum rescue height with up-raycast
|
||||
*/
|
||||
float RescueAnimation::maximumHeight()
|
||||
{
|
||||
float hit_dest = 9999999.9f;
|
||||
@ -125,24 +136,22 @@ float RescueAnimation::maximumHeight()
|
||||
}
|
||||
}
|
||||
return hit_dest;
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
} // maximumHeight
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Updates the kart animation.
|
||||
* \param dt Time step size.
|
||||
* \return True if the explosion is still shown, false if it has finished.
|
||||
*/
|
||||
void RescueAnimation::update(float dt)
|
||||
{
|
||||
|
||||
if (m_timer <= (m_kart->getKartProperties()->getRescueDuration() * rescue_moment))
|
||||
{
|
||||
if (kart_on_track == false)
|
||||
if (m_kart_on_track == false)
|
||||
{
|
||||
kart_on_track = true;
|
||||
|
||||
World::getWorld()->moveKartAfterRescue(m_kart);
|
||||
|
||||
m_kart_on_track = true;
|
||||
m_kart->getBody()->setCenterOfMassTransform(m_end_transform);
|
||||
m_kart->setTrans(m_end_transform);
|
||||
for (unsigned int i = 0; i < Camera::getNumCameras(); i++)
|
||||
{
|
||||
CameraNormal* camera = dynamic_cast<CameraNormal*>(Camera::getCamera(i));
|
||||
|
@ -50,8 +50,8 @@ protected:
|
||||
/*0.75 means that for 3 quaters of the animation it'll be on track*/
|
||||
const float rescue_moment = 0.6F;
|
||||
/* Has the kart been moved onto the track */
|
||||
bool kart_on_track = false;
|
||||
|
||||
bool m_kart_on_track = false;
|
||||
|
||||
/** The referee during a rescue operation. */
|
||||
Referee *m_referee;
|
||||
|
||||
|
53
src/main.cpp
@ -612,6 +612,8 @@ void cmdLineHelp()
|
||||
" owner-less server.\n"
|
||||
" --soccer-timed Use time limit mode in network soccer game.\n"
|
||||
" --soccer-goals Use goals limit mode in network soccer game.\n"
|
||||
" --battle-mode=n Specify battle mode in netowrk, 0 is Free For All and\n"
|
||||
" 1 is Capture The Flag.\n"
|
||||
" --network-gp=n Specify number of tracks used in network grand prix.\n"
|
||||
" --no-validation Allow non validated and unencrypted connection in wan.\n"
|
||||
" --ranked Server will submit ranking to stk addons server.\n"
|
||||
@ -973,7 +975,7 @@ int handleCmdLine()
|
||||
if (!CommandLine::has("--track", &track))
|
||||
track = "temple";
|
||||
UserConfigParams::m_arena_ai_stats=true;
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_3_STRIKES);
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_BATTLE);
|
||||
std::vector<std::string> l;
|
||||
for (int i = 0; i < 8; i++)
|
||||
l.push_back("tux");
|
||||
@ -1029,7 +1031,7 @@ int handleCmdLine()
|
||||
break;
|
||||
case 1: race_manager->setMinorMode(RaceManager::MINOR_MODE_TIME_TRIAL);
|
||||
break;
|
||||
case 2: race_manager->setMinorMode(RaceManager::MINOR_MODE_3_STRIKES);
|
||||
case 2: race_manager->setMinorMode(RaceManager::MINOR_MODE_BATTLE);
|
||||
break;
|
||||
case 3: race_manager->setMinorMode(RaceManager::MINOR_MODE_SOCCER);
|
||||
break;
|
||||
@ -1263,6 +1265,8 @@ int handleCmdLine()
|
||||
|
||||
const bool is_soccer =
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER;
|
||||
const bool is_battle =
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE;
|
||||
if (CommandLine::has("--soccer-timed") && is_soccer)
|
||||
{
|
||||
LobbyProtocol::get<LobbyProtocol>()->getGameSetup()
|
||||
@ -1284,6 +1288,31 @@ int handleCmdLine()
|
||||
NetworkConfig::get()->setServerMode(race_manager->getMinorMode(),
|
||||
RaceManager::MAJOR_MODE_GRAND_PRIX);
|
||||
}
|
||||
else if (CommandLine::has("--battle-mode", &n) && is_battle)
|
||||
{
|
||||
switch (n)
|
||||
{
|
||||
case 0:
|
||||
NetworkConfig::get()->setServerMode(race_manager->getMinorMode(),
|
||||
RaceManager::MAJOR_MODE_FREE_FOR_ALL);
|
||||
race_manager->setMajorMode(RaceManager::MAJOR_MODE_FREE_FOR_ALL);
|
||||
break;
|
||||
case 1:
|
||||
NetworkConfig::get()->setServerMode(race_manager->getMinorMode(),
|
||||
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG);
|
||||
race_manager->setMajorMode(RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (is_battle)
|
||||
{
|
||||
Log::warn("main", "Set to ffa for battle server");
|
||||
NetworkConfig::get()->setServerMode(race_manager->getMinorMode(),
|
||||
RaceManager::MAJOR_MODE_FREE_FOR_ALL);
|
||||
race_manager->setMajorMode(RaceManager::MAJOR_MODE_FREE_FOR_ALL);
|
||||
}
|
||||
else if (is_soccer)
|
||||
{
|
||||
Log::warn("main", "Set to goal target for soccer server");
|
||||
@ -1298,6 +1327,24 @@ int handleCmdLine()
|
||||
race_manager->getMinorMode(), RaceManager::MAJOR_MODE_SINGLE);
|
||||
}
|
||||
|
||||
if (is_battle)
|
||||
{
|
||||
if (UserConfigParams::m_hit_limit_threshold < 0.0f &&
|
||||
UserConfigParams::m_time_limit_threshold_ffa < 0.0f)
|
||||
{
|
||||
Log::warn("main", "Reset invalid hit and time limit settings");
|
||||
UserConfigParams::m_hit_limit_threshold.revertToDefaults();
|
||||
UserConfigParams::m_time_limit_threshold_ffa.revertToDefaults();
|
||||
}
|
||||
if (UserConfigParams::m_capture_limit_threshold < 0.0f &&
|
||||
UserConfigParams::m_time_limit_threshold_ctf < 0.0f)
|
||||
{
|
||||
Log::warn("main", "Reset invalid Capture and time limit settings");
|
||||
UserConfigParams::m_capture_limit_threshold.revertToDefaults();
|
||||
UserConfigParams::m_time_limit_threshold_ctf.revertToDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
// The extra server info has to be set before server lobby started
|
||||
if (server_lobby)
|
||||
server_lobby->requestStart();
|
||||
@ -1394,7 +1441,7 @@ int handleCmdLine()
|
||||
race_manager->setDefaultAIKartList(l);
|
||||
// Add 1 for the player kart
|
||||
race_manager->setNumKarts(1);
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_3_STRIKES);
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_BATTLE);
|
||||
}
|
||||
else if (t->isSoccer())
|
||||
{
|
||||
|
@ -21,8 +21,10 @@
|
||||
|
||||
#include "audio/sfx_manager.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/central_settings.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/material_manager.hpp"
|
||||
#include "graphics/sp/sp_texture_manager.hpp"
|
||||
#include "guiengine/engine.hpp"
|
||||
#include "guiengine/message_queue.hpp"
|
||||
#include "guiengine/modaldialog.hpp"
|
||||
@ -339,6 +341,19 @@ void MainLoop::run()
|
||||
STKHost::get()->shutdown();
|
||||
// In case the user opened a race pause dialog
|
||||
GUIEngine::ModalDialog::dismiss();
|
||||
|
||||
#ifndef SERVER_ONLY
|
||||
if (CVS->isGLSL())
|
||||
{
|
||||
// Flush all command before delete world, avoid later access
|
||||
SP::SPTextureManager::get()
|
||||
->checkForGLCommand(true/*before_scene*/);
|
||||
// Reset screen in case the minimap was drawn
|
||||
glViewport(0, 0, irr_driver->getActualScreenSize().Width,
|
||||
irr_driver->getActualScreenSize().Height);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (World::getWorld())
|
||||
{
|
||||
race_manager->clearNetworkGrandPrixResult();
|
||||
|
34
src/modes/capture_the_flag.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2018 SuperTuxKart-Team
|
||||
//
|
||||
// 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 "modes/capture_the_flag.hpp"
|
||||
|
||||
#include "network/network_config.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Constructor. Sets up the clock mode etc.
|
||||
*/
|
||||
CaptureTheFlag::CaptureTheFlag() : FreeForAll()
|
||||
{
|
||||
|
||||
} // CaptureTheFlag
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CaptureTheFlag::~CaptureTheFlag()
|
||||
{
|
||||
} // ~CaptureTheFlag
|
36
src/modes/capture_the_flag.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2018 SuperTuxKart-Team
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef CAPTURE_THE_FLAG_HPP
|
||||
#define CAPTURE_THE_FLAG_HPP
|
||||
|
||||
#include "modes/free_for_all.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class CaptureTheFlag : public FreeForAll
|
||||
{
|
||||
private:
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
CaptureTheFlag();
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~CaptureTheFlag();
|
||||
}; // CaptureTheFlag
|
||||
|
||||
#endif
|
189
src/modes/free_for_all.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2018 SuperTuxKart-Team
|
||||
//
|
||||
// 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 "modes/free_for_all.hpp"
|
||||
#include "karts/kart.hpp"
|
||||
#include "karts/controller/controller.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/network_string.hpp"
|
||||
#include "network/protocols/game_events_protocol.hpp"
|
||||
#include "network/stk_host.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Constructor. Sets up the clock mode etc.
|
||||
*/
|
||||
FreeForAll::FreeForAll() : WorldWithRank()
|
||||
{
|
||||
if (race_manager->hasTimeTarget())
|
||||
{
|
||||
WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN,
|
||||
race_manager->getTimeTarget());
|
||||
}
|
||||
else
|
||||
{
|
||||
WorldStatus::setClockMode(CLOCK_CHRONO);
|
||||
}
|
||||
} // FreeForAll
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
FreeForAll::~FreeForAll()
|
||||
{
|
||||
} // ~FreeForAll
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FreeForAll::init()
|
||||
{
|
||||
WorldWithRank::init();
|
||||
m_display_rank = false;
|
||||
m_count_down_reached_zero = false;
|
||||
} // init
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when a battle is restarted.
|
||||
*/
|
||||
void FreeForAll::reset()
|
||||
{
|
||||
WorldWithRank::reset();
|
||||
if (race_manager->hasTimeTarget())
|
||||
{
|
||||
WorldStatus::setClockMode(WorldStatus::CLOCK_COUNTDOWN,
|
||||
race_manager->getTimeTarget());
|
||||
}
|
||||
else
|
||||
{
|
||||
WorldStatus::setClockMode(CLOCK_CHRONO);
|
||||
}
|
||||
m_scores.clear();
|
||||
m_scores.resize(m_karts.size(), 0);
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when the match time ends.
|
||||
*/
|
||||
void FreeForAll::countdownReachedZero()
|
||||
{
|
||||
// Prevent negative time in network soccer when finishing
|
||||
m_time_ticks = 0;
|
||||
m_time = 0.0f;
|
||||
m_count_down_reached_zero = true;
|
||||
} // countdownReachedZero
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when a kart is hit.
|
||||
* \param kart_id The world kart id of the kart that was hit.
|
||||
* \param hitter The world kart id of the kart who hit(-1 if none).
|
||||
*/
|
||||
void FreeForAll::kartHit(int kart_id, int hitter)
|
||||
{
|
||||
if (NetworkConfig::get()->isNetworking() &&
|
||||
NetworkConfig::get()->isClient())
|
||||
return;
|
||||
|
||||
if (isRaceOver()) return;
|
||||
|
||||
NetworkString p(PROTOCOL_GAME_EVENTS);
|
||||
p.setSynchronous(true);
|
||||
p.addUInt8(GameEventsProtocol::GE_BATTLE_KART_SCORE);
|
||||
if (kart_id == hitter || hitter == -1)
|
||||
p.addUInt8((uint8_t)kart_id).addUInt32(--m_scores[kart_id]);
|
||||
else
|
||||
p.addUInt8((uint8_t)hitter).addUInt32(++m_scores[hitter]);
|
||||
STKHost::get()->sendPacketToAllPeers(&p, true);
|
||||
} // kartHit
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FreeForAll::setKartScoreFromServer(NetworkString& ns)
|
||||
{
|
||||
int kart_id = ns.getUInt8();
|
||||
int score = ns.getUInt32();
|
||||
m_scores.at(kart_id) = score;
|
||||
} // setKartScoreFromServer
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Returns the internal identifier for this race.
|
||||
*/
|
||||
const std::string& FreeForAll::getIdent() const
|
||||
{
|
||||
return IDENT_STRIKES;
|
||||
} // getIdent
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void FreeForAll::update(int ticks)
|
||||
{
|
||||
WorldWithRank::update(ticks);
|
||||
WorldWithRank::updateTrack(ticks);
|
||||
|
||||
std::vector<std::pair<int, int> > ranks;
|
||||
for (unsigned i = 0; i < m_scores.size(); i++)
|
||||
ranks.emplace_back(i, m_scores[i]);
|
||||
std::sort(ranks.begin(), ranks.end(),
|
||||
[](const std::pair<int, int>& a, const std::pair<int, int>& b)
|
||||
{
|
||||
return a.second > b.second;
|
||||
});
|
||||
beginSetKartPositions();
|
||||
for (unsigned i = 0; i < ranks.size(); i++)
|
||||
setKartPosition(ranks[i].first, i + 1);
|
||||
endSetKartPositions();
|
||||
} // update
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** The battle is over if only one kart is left, or no player kart.
|
||||
*/
|
||||
bool FreeForAll::isRaceOver()
|
||||
{
|
||||
if (NetworkConfig::get()->isNetworking() &&
|
||||
NetworkConfig::get()->isClient())
|
||||
return false;
|
||||
|
||||
if (!getKartAtPosition(1))
|
||||
return false;
|
||||
int top_id = getKartAtPosition(1)->getWorldKartId();
|
||||
return (m_count_down_reached_zero && race_manager->hasTimeTarget()) ||
|
||||
m_scores[top_id] >= race_manager->getHitCaptureLimit();
|
||||
} // isRaceOver
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Returns the data to display in the race gui.
|
||||
*/
|
||||
void FreeForAll::getKartsDisplayInfo(
|
||||
std::vector<RaceGUIBase::KartIconDisplayInfo> *info)
|
||||
{
|
||||
const unsigned int kart_amount = getNumKarts();
|
||||
for (unsigned int i = 0; i < kart_amount ; i++)
|
||||
{
|
||||
RaceGUIBase::KartIconDisplayInfo& rank_info = (*info)[i];
|
||||
rank_info.lap = -1;
|
||||
rank_info.m_outlined_font = true;
|
||||
rank_info.m_text = m_karts[i]->getController()->getName() + L" (" +
|
||||
StringUtils::toWString(m_scores[i]) + L")";
|
||||
}
|
||||
} // getKartsDisplayInfo
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FreeForAll::terminateRace()
|
||||
{
|
||||
const unsigned int kart_amount = getNumKarts();
|
||||
for (unsigned int i = 0; i < kart_amount ; i++)
|
||||
{
|
||||
m_karts[i]->finishedRace(0.0f, true/*from_server*/);
|
||||
} // i<kart_amount
|
||||
WorldWithRank::terminateRace();
|
||||
} // terminateRace
|
68
src/modes/free_for_all.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2018 SuperTuxKart-Team
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef FREE_FOR_ALL_HPP
|
||||
#define FREE_FOR_ALL_HPP
|
||||
|
||||
#include "modes/world_with_rank.hpp"
|
||||
#include "states_screens/race_gui_base.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class NetworkString;
|
||||
|
||||
class FreeForAll : public WorldWithRank
|
||||
{
|
||||
private:
|
||||
bool m_count_down_reached_zero;
|
||||
|
||||
std::vector<int> m_scores;
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
FreeForAll();
|
||||
// ------------------------------------------------------------------------
|
||||
virtual ~FreeForAll();
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void init() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool isRaceOver() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void reset() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void getKartsDisplayInfo(
|
||||
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool raceHasLaps() OVERRIDE { return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual const std::string& getIdent() const OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void kartHit(int kart_id, int hitter = -1) OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void update(int ticks) OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void countdownReachedZero() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void terminateRace() OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
void setKartScoreFromServer(NetworkString& ns);
|
||||
// ------------------------------------------------------------------------
|
||||
int getKartScore(int kart_id) const { return m_scores.at(kart_id); }
|
||||
|
||||
}; // FreeForAll
|
||||
|
||||
|
||||
#endif
|
@ -169,34 +169,10 @@ void LinearWorld::update(int ticks)
|
||||
m_finish_timeout = std::numeric_limits<float>::max();
|
||||
}
|
||||
}
|
||||
const unsigned int kart_amount = getNumKarts();
|
||||
|
||||
// Do stuff specific to this subtype of race.
|
||||
// ------------------------------------------
|
||||
for(unsigned int n=0; n<kart_amount; n++)
|
||||
{
|
||||
KartInfo& kart_info = m_kart_info[n];
|
||||
AbstractKart* kart = m_karts[n].get();
|
||||
|
||||
// Nothing to do for karts that are currently being
|
||||
// rescued or eliminated
|
||||
if(kart->getKartAnimation()) continue;
|
||||
// If the kart is off road, and 'flying' over a reset plane
|
||||
// don't adjust the distance of the kart, to avoid a jump
|
||||
// in the position of the kart (e.g. while falling the kart
|
||||
// might get too close to another part of the track, shortly
|
||||
// jump to position one, then on reset fall back to last)
|
||||
if ((!getTrackSector(n)->isOnRoad() &&
|
||||
(!kart->getMaterial() ||
|
||||
kart->getMaterial()->isDriveReset())) &&
|
||||
!kart->isGhostKart())
|
||||
continue;
|
||||
getTrackSector(n)->update(kart->getFrontXYZ());
|
||||
kart_info.m_overall_distance = kart_info.m_finished_laps
|
||||
* Track::getCurrentTrack()->getTrackLength()
|
||||
+ getDistanceDownTrackForKart(kart->getWorldKartId(), true);
|
||||
} // for n
|
||||
|
||||
updateTrackSectors();
|
||||
// Run generic parent stuff that applies to all modes.
|
||||
// It especially updates the kart positions.
|
||||
// It MUST be done after the update of the distances
|
||||
@ -209,6 +185,7 @@ void LinearWorld::update(int ticks)
|
||||
WorldWithRank::updateTrack(ticks);
|
||||
updateRacePosition();
|
||||
|
||||
const unsigned int kart_amount = getNumKarts();
|
||||
for (unsigned int i=0; i<kart_amount; i++)
|
||||
{
|
||||
// ---------- update rank ------
|
||||
@ -251,6 +228,35 @@ void LinearWorld::update(int ticks)
|
||||
#endif
|
||||
} // update
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void LinearWorld::updateTrackSectors()
|
||||
{
|
||||
const unsigned int kart_amount = getNumKarts();
|
||||
for(unsigned int n=0; n<kart_amount; n++)
|
||||
{
|
||||
KartInfo& kart_info = m_kart_info[n];
|
||||
AbstractKart* kart = m_karts[n].get();
|
||||
|
||||
// Nothing to do for karts that are currently being
|
||||
// rescued or eliminated
|
||||
if(kart->getKartAnimation()) continue;
|
||||
// If the kart is off road, and 'flying' over a reset plane
|
||||
// don't adjust the distance of the kart, to avoid a jump
|
||||
// in the position of the kart (e.g. while falling the kart
|
||||
// might get too close to another part of the track, shortly
|
||||
// jump to position one, then on reset fall back to last)
|
||||
if ((!getTrackSector(n)->isOnRoad() &&
|
||||
(!kart->getMaterial() ||
|
||||
kart->getMaterial()->isDriveReset())) &&
|
||||
!kart->isGhostKart())
|
||||
continue;
|
||||
getTrackSector(n)->update(kart->getFrontXYZ());
|
||||
kart_info.m_overall_distance = kart_info.m_finished_laps
|
||||
* Track::getCurrentTrack()->getTrackLength()
|
||||
+ getDistanceDownTrackForKart(kart->getWorldKartId(), true);
|
||||
} // for n
|
||||
} // updateTrackSectors
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This updates all only graphical elements.It is only called once per
|
||||
* rendered frame, not once per time step.
|
||||
|
@ -124,7 +124,6 @@ protected:
|
||||
AlignedArray<KartInfo> m_kart_info;
|
||||
|
||||
virtual void checkForWrongDirection(unsigned int i, float dt);
|
||||
void updateRacePosition();
|
||||
virtual float estimateFinishTimeForKart(AbstractKart* kart) OVERRIDE;
|
||||
|
||||
public:
|
||||
@ -139,6 +138,8 @@ public:
|
||||
virtual void updateGraphics(float dt) OVERRIDE;
|
||||
float getDistanceDownTrackForKart(const int kart_id,
|
||||
bool account_for_checklines) const;
|
||||
void updateTrackSectors();
|
||||
void updateRacePosition();
|
||||
float getDistanceToCenterForKart(const int kart_id) const;
|
||||
float getEstimatedFinishTime(const int kart_id) const;
|
||||
int getLapForKart(const int kart_id) const;
|
||||
|
@ -457,6 +457,9 @@ void SoccerWorld::setBallHitter(unsigned int kart_id)
|
||||
*/
|
||||
bool SoccerWorld::isRaceOver()
|
||||
{
|
||||
if (m_unfair_team)
|
||||
return true;
|
||||
|
||||
if (race_manager->hasTimeTarget())
|
||||
{
|
||||
return m_count_down_reached_zero;
|
||||
|
@ -409,6 +409,9 @@ public:
|
||||
void handlePlayerGoalFromServer(const NetworkString& ns);
|
||||
// ------------------------------------------------------------------------
|
||||
void handleResetBallFromServer(const NetworkString& ns);
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool hasTeam() const OVERRIDE { return true; }
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
}; // SoccerWorld
|
||||
|
||||
|
@ -207,8 +207,9 @@ void ThreeStrikesBattle::kartAdded(AbstractKart* kart, scene::ISceneNode* node)
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Called when a kart is hit.
|
||||
* \param kart_id The world kart id of the kart that was hit.
|
||||
* \param hitter The world kart id of the kart who hit(-1 if none).
|
||||
*/
|
||||
void ThreeStrikesBattle::kartHit(const unsigned int kart_id)
|
||||
void ThreeStrikesBattle::kartHit(int kart_id, int hitter)
|
||||
{
|
||||
if (isRaceOver()) return;
|
||||
|
||||
@ -221,7 +222,7 @@ void ThreeStrikesBattle::kartHit(const unsigned int kart_id)
|
||||
return;
|
||||
}
|
||||
|
||||
assert(kart_id < m_karts.size());
|
||||
assert(kart_id < (int)m_karts.size());
|
||||
// make kart lose a life, ignore if in profiling mode
|
||||
if (!UserConfigParams::m_arena_ai_stats)
|
||||
m_kart_info[kart_id].m_lives--;
|
||||
@ -256,7 +257,7 @@ void ThreeStrikesBattle::kartHit(const unsigned int kart_id)
|
||||
{
|
||||
AbstractKart * const kart = getKart(i);
|
||||
if(kart->isEliminated() || kart->hasFinishedRace() ||
|
||||
kart->getWorldKartId()==kart_id) continue;
|
||||
kart->getWorldKartId()==(unsigned)kart_id) continue;
|
||||
if(m_kart_info[i].m_lives > max_lives)
|
||||
{
|
||||
leader = kart;
|
||||
@ -270,7 +271,7 @@ void ThreeStrikesBattle::kartHit(const unsigned int kart_id)
|
||||
for(unsigned int i=0; i<Camera::getNumCameras(); i++)
|
||||
{
|
||||
Camera *camera = Camera::getCamera(i);
|
||||
if(camera->getKart()->getWorldKartId()==kart_id)
|
||||
if(camera->getKart()->getWorldKartId()==(unsigned)kart_id)
|
||||
{
|
||||
camera->setMode(Camera::CM_NORMAL);
|
||||
camera->setKart(leader);
|
||||
|
@ -111,7 +111,7 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
virtual const std::string& getIdent() const OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void kartHit(const unsigned int kart_id) OVERRIDE;
|
||||
virtual void kartHit(int kart_id, int hitter = -1) OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void update(int ticks) OVERRIDE;
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -312,9 +312,7 @@ void World::reset()
|
||||
|
||||
// Reset all data structures that depend on number of karts.
|
||||
irr_driver->reset();
|
||||
|
||||
//Reset the Rubber Ball Collect Time to some negative value.
|
||||
powerup_manager->setBallCollectTicks(-100);
|
||||
m_unfair_team = false;
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -428,7 +426,7 @@ Controller* World::loadAIController(AbstractKart* kart)
|
||||
Controller *controller;
|
||||
int turn=0;
|
||||
|
||||
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES)
|
||||
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_BATTLE)
|
||||
turn=1;
|
||||
else if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_SOCCER)
|
||||
turn=2;
|
||||
|
@ -108,6 +108,8 @@ protected:
|
||||
|
||||
bool m_stop_music_when_dialog_open;
|
||||
|
||||
bool m_unfair_team;
|
||||
|
||||
/** Whether highscores should be used for this kind of race.
|
||||
* True by default, change to false in a child class to disable.
|
||||
*/
|
||||
@ -266,7 +268,7 @@ public:
|
||||
virtual void newLap(unsigned int kart_index) {}
|
||||
// ------------------------------------------------------------------------
|
||||
/** Called when a kart was hit by a projectile. */
|
||||
virtual void kartHit(const unsigned int kart_id) {};
|
||||
virtual void kartHit(int kart_id, int hitter = -1) {};
|
||||
// ------------------------------------------------------------------------
|
||||
virtual void onMouseClick(int x, int y) {};
|
||||
|
||||
@ -327,7 +329,11 @@ public:
|
||||
virtual void loadCustomModels() {}
|
||||
// ------------------------------------------------------------------------
|
||||
void eliminateKart(int kart_number, bool notify_of_elimination = true);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void setUnfairTeam(bool val) { m_unfair_team = val; }
|
||||
// ------------------------------------------------------------------------
|
||||
virtual bool hasTeam() const { return false; }
|
||||
// ------------------------------------------------------------------------
|
||||
/** Set the network mode (true if networked) */
|
||||
void setNetworkWorld(bool is_networked) { m_is_network_world = is_networked; }
|
||||
|
||||
|
@ -54,9 +54,19 @@ void GameSetup::update(bool remove_disconnected_players)
|
||||
if (!World::getWorld() ||
|
||||
World::getWorld()->getPhase() < WorldStatus::MUSIC_PHASE)
|
||||
return;
|
||||
int red_count = 0;
|
||||
int blue_count = 0;
|
||||
for (uint8_t i = 0; i < (uint8_t)m_players.size(); i++)
|
||||
{
|
||||
if (!m_players[i].expired())
|
||||
bool disconnected = m_players[i].expired();
|
||||
if (race_manager->getKartInfo(i).getSoccerTeam() == SOCCER_TEAM_RED &&
|
||||
!disconnected)
|
||||
red_count++;
|
||||
else if (race_manager->getKartInfo(i).getSoccerTeam() ==
|
||||
SOCCER_TEAM_BLUE && !disconnected)
|
||||
blue_count++;
|
||||
|
||||
if (!disconnected)
|
||||
continue;
|
||||
AbstractKart* k = World::getWorld()->getKart(i);
|
||||
if (!k->isEliminated())
|
||||
@ -72,6 +82,9 @@ void GameSetup::update(bool remove_disconnected_players)
|
||||
STKHost::get()->sendPacketToAllPeers(&p, true);
|
||||
}
|
||||
}
|
||||
if (m_players.size() != 1 && World::getWorld()->hasTeam() &&
|
||||
(red_count == 0 || blue_count == 0))
|
||||
World::getWorld()->setUnfairTeam(true);
|
||||
} // removePlayer
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -85,10 +98,16 @@ void GameSetup::loadWorld()
|
||||
PlayerManager::getCurrentPlayer()->setCurrentChallenge("");
|
||||
race_manager->setTimeTarget(0.0f);
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER ||
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES)
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE)
|
||||
{
|
||||
const bool is_ctf = race_manager->getMajorMode() ==
|
||||
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG;
|
||||
bool prev_val = UserConfigParams::m_random_arena_item;
|
||||
UserConfigParams::m_random_arena_item = m_reverse;
|
||||
if (is_ctf)
|
||||
UserConfigParams::m_random_arena_item = false;
|
||||
else
|
||||
UserConfigParams::m_random_arena_item = m_reverse;
|
||||
|
||||
race_manager->setReverseTrack(false);
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
|
||||
{
|
||||
@ -97,6 +116,11 @@ void GameSetup::loadWorld()
|
||||
else
|
||||
race_manager->setTimeTarget((float)m_laps * 60.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
race_manager->setHitCaptureTime(m_hit_capture_limit,
|
||||
m_battle_time_limit);
|
||||
}
|
||||
race_manager->startSingleRace(m_tracks.back(), -1,
|
||||
false/*from_overworld*/);
|
||||
UserConfigParams::m_random_arena_item = prev_val;
|
||||
|
@ -54,6 +54,10 @@ private:
|
||||
|
||||
int m_extra_server_info;
|
||||
|
||||
int m_hit_capture_limit;
|
||||
|
||||
float m_battle_time_limit;
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
GameSetup()
|
||||
@ -114,6 +118,8 @@ public:
|
||||
m_tracks.clear();
|
||||
m_laps = 0;
|
||||
m_reverse = false;
|
||||
m_hit_capture_limit = 0;
|
||||
m_battle_time_limit = 0.0f;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void setGrandPrixTrack(int tracks_no) { m_extra_server_info = tracks_no; }
|
||||
@ -159,6 +165,12 @@ public:
|
||||
void sortPlayersForGrandPrix();
|
||||
// ------------------------------------------------------------------------
|
||||
void sortPlayersForSoccer();
|
||||
// ------------------------------------------------------------------------
|
||||
void setHitCaptureTime(int hc, float time)
|
||||
{
|
||||
m_hit_capture_limit = hc;
|
||||
m_battle_time_limit = time;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
NetworkConfig *NetworkConfig::m_network_config = NULL;
|
||||
bool NetworkConfig::m_disable_lan = false;
|
||||
const uint8_t NetworkConfig::m_server_version = 1;
|
||||
const uint32_t NetworkConfig::m_server_version = 1;
|
||||
|
||||
/** \class NetworkConfig
|
||||
* This class is the interface between STK and the online code, particularly
|
||||
@ -65,6 +65,7 @@ NetworkConfig::NetworkConfig()
|
||||
m_server_port = UserConfigParams::m_random_server_port ?
|
||||
0 : stk_config->m_server_port;
|
||||
m_team_choosing = false;
|
||||
m_joined_server_version = 0;
|
||||
} // NetworkConfig
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -89,7 +90,7 @@ void NetworkConfig::setServerMode(RaceManager::MinorRaceModeType minor,
|
||||
else if (minor == RaceManager::MINOR_MODE_FOLLOW_LEADER)
|
||||
m_server_mode = 2;
|
||||
}
|
||||
else
|
||||
else if (major == RaceManager::MAJOR_MODE_SINGLE)
|
||||
{
|
||||
if (minor == RaceManager::MINOR_MODE_NORMAL_RACE)
|
||||
m_server_mode = 3;
|
||||
@ -97,10 +98,16 @@ void NetworkConfig::setServerMode(RaceManager::MinorRaceModeType minor,
|
||||
m_server_mode = 4;
|
||||
else if (minor == RaceManager::MINOR_MODE_FOLLOW_LEADER)
|
||||
m_server_mode = 5;
|
||||
else if (minor == RaceManager::MINOR_MODE_3_STRIKES)
|
||||
m_server_mode = 6;
|
||||
else if (minor == RaceManager::MINOR_MODE_SOCCER)
|
||||
m_server_mode = 7;
|
||||
m_server_mode = 6;
|
||||
}
|
||||
else if (major == RaceManager::MAJOR_MODE_FREE_FOR_ALL)
|
||||
{
|
||||
m_server_mode = 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_server_mode = 8;
|
||||
}
|
||||
} // setServerMode
|
||||
|
||||
@ -129,11 +136,14 @@ std::pair<RaceManager::MinorRaceModeType, RaceManager::MajorRaceModeType>
|
||||
return { RaceManager::MINOR_MODE_FOLLOW_LEADER,
|
||||
RaceManager::MAJOR_MODE_SINGLE };
|
||||
case 6:
|
||||
return { RaceManager::MINOR_MODE_3_STRIKES,
|
||||
RaceManager::MAJOR_MODE_SINGLE };
|
||||
case 7:
|
||||
return { RaceManager::MINOR_MODE_SOCCER,
|
||||
RaceManager::MAJOR_MODE_SINGLE };
|
||||
case 7:
|
||||
return { RaceManager::MINOR_MODE_BATTLE,
|
||||
RaceManager::MAJOR_MODE_FREE_FOR_ALL };
|
||||
case 8:
|
||||
return { RaceManager::MINOR_MODE_BATTLE,
|
||||
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG };
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -176,9 +186,13 @@ core::stringw NetworkConfig::getModeName(unsigned id)
|
||||
case 4:
|
||||
return _("Time Trial");
|
||||
case 6:
|
||||
return _("3 Strikes Battle");
|
||||
case 7:
|
||||
return _("Soccer");
|
||||
case 7:
|
||||
// I18n: Free for all means a deathmatch game with battle mode in
|
||||
// networking
|
||||
return _("Free-For-All");
|
||||
case 8:
|
||||
return _("Capture The Flag");
|
||||
default:
|
||||
return L"";
|
||||
}
|
||||
|
@ -121,13 +121,15 @@ private:
|
||||
|
||||
NetworkConfig();
|
||||
|
||||
uint32_t m_joined_server_version;
|
||||
|
||||
public:
|
||||
/** Stores the command line flag to disable lan detection (i.e. force
|
||||
* WAN code to be used when connection client and server). */
|
||||
static bool m_disable_lan;
|
||||
|
||||
/** Server version, will be advanced if there are protocol changes. */
|
||||
static const uint8_t m_server_version;
|
||||
static const uint32_t m_server_version;
|
||||
|
||||
/** Singleton get, which creates this object if necessary. */
|
||||
static NetworkConfig *get()
|
||||
@ -330,7 +332,10 @@ public:
|
||||
void setTeamChoosing(bool val) { m_team_choosing = val; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool hasTeamChoosing() const { return m_team_choosing; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
void setJoinedServerVersion(uint32_t v) { m_joined_server_version = v; }
|
||||
// ------------------------------------------------------------------------
|
||||
uint32_t getJoinedServerVersion() const { return m_joined_server_version; }
|
||||
}; // class NetworkConfig
|
||||
|
||||
#endif // HEADER_NETWORK_CONFIG
|
||||
|
@ -75,6 +75,11 @@ ClientLobby::ClientLobby(const TransportAddress& a, std::shared_ptr<Server> s)
|
||||
m_server_address = a;
|
||||
m_server = s;
|
||||
setHandleDisconnections(true);
|
||||
m_disconnected_msg[PDI_TIMEOUT] = _("Server connection timed out.");
|
||||
m_disconnected_msg[PDI_NORMAL] = _("Server has been shut down.");
|
||||
m_disconnected_msg[PDI_KICK] = _("You were kicked from the server.");
|
||||
m_disconnected_msg[PDI_BAD_CONNECTION] =
|
||||
_("Bad network connection is detected.");
|
||||
} // ClientLobby
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -140,6 +145,12 @@ bool ClientLobby::notifyEvent(Event* event)
|
||||
case LE_CHAT: handleChat(event); break;
|
||||
case LE_CONNECTION_ACCEPTED: connectionAccepted(event); break;
|
||||
case LE_SERVER_INFO: handleServerInfo(event); break;
|
||||
case LE_PLAYER_DISCONNECTED : disconnectedPlayer(event); break;
|
||||
case LE_CONNECTION_REFUSED: connectionRefused(event); break;
|
||||
case LE_VOTE: displayPlayerVote(event); break;
|
||||
case LE_SERVER_OWNERSHIP: becomingServerOwner(); break;
|
||||
case LE_BAD_TEAM: handleBadTeam(); break;
|
||||
case LE_BAD_CONNECTION: handleBadConnection(); break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
@ -161,12 +172,7 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
|
||||
message_type);
|
||||
switch(message_type)
|
||||
{
|
||||
case LE_PLAYER_DISCONNECTED : disconnectedPlayer(event); break;
|
||||
case LE_START_RACE: startGame(event); break;
|
||||
case LE_CONNECTION_REFUSED: connectionRefused(event); break;
|
||||
case LE_VOTE: displayPlayerVote(event); break;
|
||||
case LE_SERVER_OWNERSHIP: becomingServerOwner(); break;
|
||||
case LE_BAD_TEAM: handleBadTeam(); break;
|
||||
default: break;
|
||||
} // switch
|
||||
|
||||
@ -181,21 +187,8 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
|
||||
// So only signal that STKHost should exit, which will be tested
|
||||
// from the main thread.
|
||||
STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/);
|
||||
switch(event->getPeerDisconnectInfo())
|
||||
{
|
||||
case PDI_TIMEOUT:
|
||||
STKHost::get()->setErrorMessage(
|
||||
_("Server connection timed out."));
|
||||
break;
|
||||
case PDI_NORMAL:
|
||||
STKHost::get()->setErrorMessage(
|
||||
_("Server has been shut down."));
|
||||
break;
|
||||
case PDI_KICK:
|
||||
STKHost::get()->setErrorMessage(
|
||||
_("You were kicked from the server."));
|
||||
break;
|
||||
} // switch
|
||||
STKHost::get()->setErrorMessage(
|
||||
m_disconnected_msg.at(event->getPeerDisconnectInfo()));
|
||||
STKHost::get()->requestShutdown();
|
||||
return true;
|
||||
} // disconnection
|
||||
@ -246,6 +239,12 @@ void ClientLobby::addAllPlayers(Event* event)
|
||||
}
|
||||
uint32_t random_seed = data.getUInt32();
|
||||
ItemManager::updateRandomSeed(random_seed);
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE)
|
||||
{
|
||||
int hit_capture_limit = data.getUInt32();
|
||||
float time_limit = data.getFloat();
|
||||
m_game_setup->setHitCaptureTime(hit_capture_limit, time_limit);
|
||||
}
|
||||
configRemoteKart(players);
|
||||
loadWorld();
|
||||
// Switch to assign mode in case a player hasn't chosen any karts
|
||||
@ -267,7 +266,7 @@ void ClientLobby::update(int ticks)
|
||||
{
|
||||
NetworkString* ns = getNetworkString();
|
||||
ns->addUInt8(LE_CONNECTION_REQUESTED)
|
||||
.addUInt8(NetworkConfig::m_server_version);
|
||||
.addUInt32(NetworkConfig::m_server_version);
|
||||
|
||||
auto all_k = kart_properties_manager->getAllAvailableKarts();
|
||||
auto all_t = track_manager->getAllTrackIdentifiers();
|
||||
@ -332,6 +331,7 @@ void ClientLobby::update(int ticks)
|
||||
GUIEngine::ModalDialog::dismiss();
|
||||
if (StateManager::get()->getGameState() == GUIEngine::INGAME_MENU)
|
||||
StateManager::get()->enterGameState();
|
||||
World::getWorld()->enterRaceOverState();
|
||||
}
|
||||
break;
|
||||
case DONE:
|
||||
@ -413,12 +413,20 @@ void ClientLobby::displayPlayerVote(Event* event)
|
||||
core::stringw yes = _("Yes");
|
||||
core::stringw no = _("No");
|
||||
core::stringw vote_msg;
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES)
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE &&
|
||||
race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL)
|
||||
{
|
||||
//I18N: Vote message in network game from a player
|
||||
vote_msg = _("Track: %s,\nrandom item location: %s",
|
||||
track_readable, rev == 1 ? yes : no);
|
||||
}
|
||||
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE &&
|
||||
race_manager->getMajorMode() ==
|
||||
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG)
|
||||
{
|
||||
//I18N: Vote message in network game from a player
|
||||
vote_msg = _("Track: %s", track_readable);
|
||||
}
|
||||
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
|
||||
{
|
||||
if (m_game_setup->isSoccerGoalTarget())
|
||||
@ -491,6 +499,9 @@ void ClientLobby::connectionAccepted(Event* event)
|
||||
Log::info("ClientLobby", "The server accepted the connection.");
|
||||
STKHost::get()->setMyHostId(data.getUInt32());
|
||||
assert(!NetworkConfig::get()->isAddingNetworkPlayers());
|
||||
uint32_t server_version = data.getUInt32();
|
||||
NetworkConfig::get()->setJoinedServerVersion(server_version);
|
||||
assert(server_version != 0);
|
||||
m_state.store(CONNECTED);
|
||||
float auto_start_timer = data.getFloat();
|
||||
if (auto_start_timer != std::numeric_limits<float>::max())
|
||||
@ -530,8 +541,13 @@ void ClientLobby::handleServerInfo(Event* event)
|
||||
NetworkConfig::get()->setServerMode(u_data);
|
||||
auto game_mode = NetworkConfig::get()->getLocalGameMode();
|
||||
race_manager->setMinorMode(game_mode.first);
|
||||
// We use single mode in network even it's grand prix
|
||||
race_manager->setMajorMode(RaceManager::MAJOR_MODE_SINGLE);
|
||||
if (game_mode.first == RaceManager::MINOR_MODE_BATTLE)
|
||||
race_manager->setMajorMode(game_mode.second);
|
||||
else
|
||||
{
|
||||
// We use single mode in network even it's grand prix
|
||||
race_manager->setMajorMode(RaceManager::MAJOR_MODE_SINGLE);
|
||||
}
|
||||
|
||||
//I18N: In the networking lobby
|
||||
core::stringw mode_name = NetworkConfig::get()->getModeName(u_data);
|
||||
@ -621,6 +637,14 @@ void ClientLobby::handleBadTeam()
|
||||
MessageQueue::add(MessageQueue::MT_ERROR, msg);
|
||||
} // handleBadTeam
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ClientLobby::handleBadConnection()
|
||||
{
|
||||
SFXManager::get()->quickSound("anvil");
|
||||
core::stringw msg = _("Bad network connection is detected.");
|
||||
MessageQueue::add(MessageQueue::MT_ERROR, msg);
|
||||
} // handleBadConnection
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ClientLobby::becomingServerOwner()
|
||||
{
|
||||
|
@ -24,9 +24,12 @@
|
||||
#include "utils/cpp2011.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
enum PeerDisconnectInfo : unsigned int;
|
||||
|
||||
class BareNetworkString;
|
||||
class Server;
|
||||
|
||||
@ -46,6 +49,7 @@ private:
|
||||
void handleChat(Event* event);
|
||||
void handleServerInfo(Event* event);
|
||||
void handleBadTeam();
|
||||
void handleBadConnection();
|
||||
void becomingServerOwner();
|
||||
|
||||
void clearPlayers();
|
||||
@ -79,6 +83,8 @@ private:
|
||||
void finalizeConnectionRequest(NetworkString* header,
|
||||
BareNetworkString* rest, bool encrypt);
|
||||
|
||||
std::map<PeerDisconnectInfo, irr::core::stringw> m_disconnected_msg;
|
||||
|
||||
public:
|
||||
ClientLobby(const TransportAddress& a, std::shared_ptr<Server> s);
|
||||
virtual ~ClientLobby();
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "network/protocols/game_events_protocol.hpp"
|
||||
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "modes/free_for_all.hpp"
|
||||
#include "modes/soccer_world.hpp"
|
||||
#include "network/event.hpp"
|
||||
#include "network/game_setup.hpp"
|
||||
@ -44,6 +45,7 @@ bool GameEventsProtocol::notifyEvent(Event* event)
|
||||
return true;
|
||||
}
|
||||
uint8_t type = data.getUInt8();
|
||||
FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld());
|
||||
SoccerWorld* sw = dynamic_cast<SoccerWorld*>(World::getWorld());
|
||||
switch (type)
|
||||
{
|
||||
@ -65,6 +67,13 @@ bool GameEventsProtocol::notifyEvent(Event* event)
|
||||
sw->handlePlayerGoalFromServer(data);
|
||||
break;
|
||||
}
|
||||
case GE_BATTLE_KART_SCORE:
|
||||
{
|
||||
if (!ffa)
|
||||
throw std::invalid_argument("No free for all world");
|
||||
ffa->setKartScoreFromServer(data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Log::warn("GameEventsProtocol", "Unkown message type.");
|
||||
break;
|
||||
|
@ -14,7 +14,8 @@ public:
|
||||
GE_KART_FINISHED_RACE = 1,
|
||||
GE_PLAYER_DISCONNECT = 2,
|
||||
GE_RESET_BALL = 3,
|
||||
GE_PLAYER_GOAL = 4
|
||||
GE_PLAYER_GOAL = 4,
|
||||
GE_BATTLE_KART_SCORE = 5
|
||||
}; // GameEventType
|
||||
private:
|
||||
void eliminatePlayer(const NetworkString &ns);
|
||||
|
@ -61,7 +61,8 @@ public:
|
||||
LE_SERVER_OWNERSHIP,
|
||||
LE_KICK_HOST,
|
||||
LE_CHANGE_TEAM,
|
||||
LE_BAD_TEAM
|
||||
LE_BAD_TEAM,
|
||||
LE_BAD_CONNECTION
|
||||
};
|
||||
|
||||
enum RejectReason : uint8_t
|
||||
|
@ -158,7 +158,7 @@ void ServerLobby::setup()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RaceManager::MINOR_MODE_3_STRIKES:
|
||||
case RaceManager::MINOR_MODE_BATTLE:
|
||||
{
|
||||
auto it = m_available_kts.second.begin();
|
||||
while (it != m_available_kts.second.end())
|
||||
@ -199,7 +199,8 @@ void ServerLobby::setup()
|
||||
// the server are ready:
|
||||
resetPeersReady();
|
||||
m_peers_votes.clear();
|
||||
m_server_delay = 0.0;
|
||||
m_server_delay = std::numeric_limits<double>::max();
|
||||
m_server_max_ping = std::numeric_limits<double>::max();
|
||||
m_timeout.store(std::numeric_limits<float>::max());
|
||||
m_waiting_for_reset = false;
|
||||
|
||||
@ -249,7 +250,8 @@ void ServerLobby::handleChat(Event* event)
|
||||
//-----------------------------------------------------------------------------
|
||||
void ServerLobby::changeTeam(Event* event)
|
||||
{
|
||||
if (!NetworkConfig::get()->hasTeamChoosing())
|
||||
if (!NetworkConfig::get()->hasTeamChoosing() ||
|
||||
!race_manager->teamEnabled())
|
||||
return;
|
||||
if (!checkDataSize(event, 1)) return;
|
||||
NetworkString& data = event->data();
|
||||
@ -428,12 +430,34 @@ void ServerLobby::asynchronousUpdate()
|
||||
// Reset for next state usage
|
||||
resetPeersReady();
|
||||
signalRaceStartToClients();
|
||||
m_server_max_ping = StkTime::getRealTime() +
|
||||
((double)UserConfigParams::m_max_ping / 1000.0);
|
||||
break;
|
||||
}
|
||||
case WAIT_FOR_RACE_STARTED:
|
||||
// The function startedRaceOnClient() will trigger the
|
||||
// next state.
|
||||
{
|
||||
const bool ping_timed_out =
|
||||
m_server_max_ping < StkTime::getRealTime();
|
||||
if (checkPeersReady() || ping_timed_out)
|
||||
{
|
||||
for (auto p : m_peers_ready)
|
||||
{
|
||||
auto cur_peer = p.first.lock();
|
||||
if (!cur_peer)
|
||||
continue;
|
||||
if (ping_timed_out && p.second.second > m_server_max_ping)
|
||||
sendBadConnectionMessageToPeer(cur_peer);
|
||||
}
|
||||
m_state = DELAY_SERVER;
|
||||
const double jt =
|
||||
(double)UserConfigParams::m_jitter_tolerance / 1000.0;
|
||||
m_server_delay = StkTime::getRealTime() + jt;
|
||||
Log::verbose("ServerLobby",
|
||||
"Started delay at %lf to %lf with jitter tolerance %lf.",
|
||||
StkTime::getRealTime(), m_server_delay, jt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DELAY_SERVER:
|
||||
if (m_server_delay < StkTime::getRealTime())
|
||||
{
|
||||
@ -485,6 +509,12 @@ void ServerLobby::asynchronousUpdate()
|
||||
uint32_t random_seed = (uint32_t)StkTime::getTimeSinceEpoch();
|
||||
ItemManager::updateRandomSeed(random_seed);
|
||||
load_world->addUInt32(random_seed);
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE)
|
||||
{
|
||||
auto hcl = getHitCaptureLimit((float)players.size());
|
||||
load_world->addUInt32(hcl.first).addFloat(hcl.second);
|
||||
m_game_setup->setHitCaptureTime(hcl.first, hcl.second);
|
||||
}
|
||||
configRemoteKart(players);
|
||||
|
||||
// Reset for next state usage
|
||||
@ -501,6 +531,20 @@ void ServerLobby::asynchronousUpdate()
|
||||
|
||||
} // asynchronousUpdate
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void ServerLobby::sendBadConnectionMessageToPeer(std::shared_ptr<STKPeer> p)
|
||||
{
|
||||
const unsigned max_ping = UserConfigParams::m_max_ping;
|
||||
Log::warn("ServerLobby", "Peer %s cannot catch up with max ping %d, it"
|
||||
" started at %lf.", p->getAddress().toString().c_str(), max_ping,
|
||||
StkTime::getRealTime());
|
||||
NetworkString* msg = getNetworkString();
|
||||
msg->setSynchronous(true);
|
||||
msg->addUInt8(LE_BAD_CONNECTION);
|
||||
p->sendPacket(msg, /*reliable*/true);
|
||||
delete msg;
|
||||
} // sendBadConnectionMessageToPeer
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Simple finite state machine. Once this
|
||||
* is known, register the server and its address with the stk server so that
|
||||
@ -761,11 +805,12 @@ void ServerLobby::startSelection(const Event *event)
|
||||
}
|
||||
}
|
||||
|
||||
if (NetworkConfig::get()->hasTeamChoosing())
|
||||
auto players = m_game_setup->getConnectedPlayers();
|
||||
const unsigned player_count = players.size();
|
||||
if (NetworkConfig::get()->hasTeamChoosing() && race_manager->teamEnabled())
|
||||
{
|
||||
int red_count = 0;
|
||||
int blue_count = 0;
|
||||
auto players = m_game_setup->getConnectedPlayers();
|
||||
for (auto& player : players)
|
||||
{
|
||||
if (player->getTeam() == SOCCER_TEAM_RED)
|
||||
@ -775,10 +820,11 @@ void ServerLobby::startSelection(const Event *event)
|
||||
if (red_count != 0 && blue_count != 0)
|
||||
break;
|
||||
}
|
||||
if ((red_count == 0 || blue_count == 0) && players.size() != 1)
|
||||
if ((red_count == 0 || blue_count == 0) && player_count != 1)
|
||||
{
|
||||
Log::warn("ServerLobby", "Bad team choosing.");
|
||||
NetworkString* bt = getNetworkString();
|
||||
bt->setSynchronous(true);
|
||||
bt->addUInt8(LE_BAD_TEAM);
|
||||
sendMessageToPeers(bt, true/*reliable*/);
|
||||
delete bt;
|
||||
@ -818,6 +864,22 @@ void ServerLobby::startSelection(const Event *event)
|
||||
m_available_kts.second.erase(track_erase);
|
||||
}
|
||||
|
||||
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE &&
|
||||
race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL)
|
||||
{
|
||||
auto it = m_available_kts.second.begin();
|
||||
while (it != m_available_kts.second.end())
|
||||
{
|
||||
Track* t = track_manager->getTrack(*it);
|
||||
if (t->getMaxArenaPlayers() < player_count)
|
||||
{
|
||||
it = m_available_kts.second.erase(it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& all_k = m_available_kts.first;
|
||||
const auto& all_t = m_available_kts.second;
|
||||
ns->addUInt16((uint16_t)all_k.size()).addUInt16((uint16_t)all_t.size());
|
||||
@ -1215,6 +1277,7 @@ void ServerLobby::clientDisconnected(Event* event)
|
||||
return;
|
||||
|
||||
NetworkString* msg = getNetworkString(2);
|
||||
msg->setSynchronous(true);
|
||||
msg->addUInt8(LE_PLAYER_DISCONNECTED);
|
||||
msg->addUInt8((uint8_t)players_on_peer.size());
|
||||
for (auto p : players_on_peer)
|
||||
@ -1263,6 +1326,7 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
m_game_setup->isGrandPrixStarted())
|
||||
{
|
||||
NetworkString *message = getNetworkString(2);
|
||||
message->setSynchronous(true);
|
||||
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_BUSY);
|
||||
// send only to the peer that made the request and disconect it now
|
||||
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
|
||||
@ -1273,11 +1337,12 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
}
|
||||
|
||||
// Check server version
|
||||
int version = data.getUInt8();
|
||||
int version = data.getUInt32();
|
||||
if (version < stk_config->m_min_server_version ||
|
||||
version > stk_config->m_max_server_version)
|
||||
{
|
||||
NetworkString *message = getNetworkString(2);
|
||||
message->setSynchronous(true);
|
||||
message->addUInt8(LE_CONNECTION_REFUSED)
|
||||
.addUInt8(RR_INCOMPATIBLE_DATA);
|
||||
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
|
||||
@ -1325,6 +1390,7 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
tracks_erase.size() == m_available_kts.second.size())
|
||||
{
|
||||
NetworkString *message = getNetworkString(2);
|
||||
message->setSynchronous(true);
|
||||
message->addUInt8(LE_CONNECTION_REFUSED)
|
||||
.addUInt8(RR_INCOMPATIBLE_DATA);
|
||||
peer->cleanPlayerProfiles();
|
||||
@ -1359,6 +1425,7 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
if (is_banned)
|
||||
{
|
||||
NetworkString *message = getNetworkString(2);
|
||||
message->setSynchronous(true);
|
||||
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_BANNED);
|
||||
peer->cleanPlayerProfiles();
|
||||
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
|
||||
@ -1372,6 +1439,7 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
NetworkConfig::get()->getMaxPlayers())
|
||||
{
|
||||
NetworkString *message = getNetworkString(2);
|
||||
message->setSynchronous(true);
|
||||
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_TOO_MANY_PLAYERS);
|
||||
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
|
||||
peer->reset();
|
||||
@ -1392,6 +1460,7 @@ void ServerLobby::connectionRequested(Event* event)
|
||||
NetworkConfig::get()->isRankedServer()))
|
||||
{
|
||||
NetworkString* message = getNetworkString(2);
|
||||
message->setSynchronous(true);
|
||||
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_INVALID_PLAYER);
|
||||
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
|
||||
peer->reset();
|
||||
@ -1427,6 +1496,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
|
||||
if (password != NetworkConfig::get()->getPassword())
|
||||
{
|
||||
NetworkString *message = getNetworkString(2);
|
||||
message->setSynchronous(true);
|
||||
message->addUInt8(LE_CONNECTION_REFUSED)
|
||||
.addUInt8(RR_INCORRECT_PASSWORD);
|
||||
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
|
||||
@ -1441,6 +1511,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
|
||||
NetworkConfig::get()->isRankedServer())
|
||||
{
|
||||
NetworkString* message = getNetworkString(2);
|
||||
message->setSynchronous(true);
|
||||
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(RR_INVALID_PLAYER);
|
||||
peer->sendPacket(message, true/*reliable*/, false/*encrypted*/);
|
||||
peer->reset();
|
||||
@ -1461,7 +1532,8 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
|
||||
(peer, i == 0 && !online_name.empty() ? online_name : name,
|
||||
peer->getHostId(), default_kart_color, i == 0 ? online_id : 0,
|
||||
per_player_difficulty, (uint8_t)i, SOCCER_TEAM_NONE);
|
||||
if (NetworkConfig::get()->hasTeamChoosing())
|
||||
if (NetworkConfig::get()->hasTeamChoosing() &&
|
||||
race_manager->teamEnabled())
|
||||
player->setTeam((SoccerTeam)(peer->getHostId() % 2));
|
||||
peer->addPlayer(player);
|
||||
}
|
||||
@ -1481,6 +1553,7 @@ void ServerLobby::handleUnencryptedConnection(std::shared_ptr<STKPeer> peer,
|
||||
// connection success -- return the host id of peer
|
||||
float auto_start_timer = m_timeout.load();
|
||||
message_ack->addUInt8(LE_CONNECTION_ACCEPTED).addUInt32(peer->getHostId())
|
||||
.addUInt32(NetworkConfig::m_server_version)
|
||||
.addFloat(auto_start_timer == std::numeric_limits<float>::max() ?
|
||||
auto_start_timer : auto_start_timer - (float)StkTime::getRealTime());
|
||||
peer->sendPacket(message_ack);
|
||||
@ -1522,7 +1595,7 @@ void ServerLobby::updatePlayerList(bool force_update)
|
||||
pl->addUInt8(server_owner);
|
||||
pl->addUInt8(profile->getPerPlayerDifficulty());
|
||||
if (NetworkConfig::get()->hasTeamChoosing() &&
|
||||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
|
||||
race_manager->teamEnabled())
|
||||
pl->addUInt8(profile->getTeam());
|
||||
else
|
||||
pl->addUInt8(SOCCER_TEAM_NONE);
|
||||
@ -1564,6 +1637,7 @@ void ServerLobby::updateServerOwner()
|
||||
if (owner)
|
||||
{
|
||||
NetworkString* ns = getNetworkString();
|
||||
ns->setSynchronous(true);
|
||||
ns->addUInt8(LE_SERVER_OWNERSHIP);
|
||||
owner->sendPacket(ns);
|
||||
delete ns;
|
||||
@ -1641,6 +1715,7 @@ void ServerLobby::playerVote(Event* event)
|
||||
NetworkString other = NetworkString(PROTOCOL_LOBBY_ROOM);
|
||||
std::string name = StringUtils::wideToUtf8(event->getPeer()
|
||||
->getPlayerProfiles()[0]->getName());
|
||||
other.setSynchronous(true);
|
||||
other.addUInt8(LE_VOTE).addFloat(UserConfigParams::m_voting_timeout)
|
||||
.encodeString(name).addUInt32(event->getPeer()->getHostId());
|
||||
other += data;
|
||||
@ -1757,6 +1832,47 @@ std::tuple<std::string, uint8_t, bool, bool> ServerLobby::handleVote()
|
||||
true : false);
|
||||
} // handleVote
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
std::pair<int, float> ServerLobby::getHitCaptureLimit(float num_karts)
|
||||
{
|
||||
// Read user_config.hpp for formula
|
||||
int hit_capture_limit = std::numeric_limits<int>::max();
|
||||
float time_limit = 0.0f;
|
||||
if (race_manager->getMajorMode() ==
|
||||
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG)
|
||||
{
|
||||
if (UserConfigParams::m_capture_limit_threshold > 0.0f)
|
||||
{
|
||||
float val = fmaxf(2.0f, num_karts *
|
||||
UserConfigParams::m_capture_limit_threshold);
|
||||
hit_capture_limit = (int)val;
|
||||
}
|
||||
if (UserConfigParams::m_time_limit_threshold_ctf > 0.0f)
|
||||
{
|
||||
time_limit = fmaxf(2.0f, num_karts *
|
||||
(UserConfigParams::m_time_limit_threshold_ctf +
|
||||
UserConfigParams::m_flag_return_timemout / 60.f) * 60.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (UserConfigParams::m_hit_limit_threshold > 0.0f)
|
||||
{
|
||||
float val = fminf(num_karts *
|
||||
UserConfigParams::m_hit_limit_threshold, 40.0f);
|
||||
hit_capture_limit = (int)val;
|
||||
if (hit_capture_limit == 0)
|
||||
hit_capture_limit = 1;
|
||||
}
|
||||
if (UserConfigParams::m_time_limit_threshold_ffa > 0.0f)
|
||||
{
|
||||
time_limit = fmaxf(num_karts *
|
||||
UserConfigParams::m_time_limit_threshold_ffa, 2.0f) * 60.0f;
|
||||
}
|
||||
}
|
||||
return std::make_pair(hit_capture_limit, time_limit);
|
||||
} // getHitCaptureLimit
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Called from the RaceManager of the server when the world is loaded. Marks
|
||||
* the server to be ready to start the race.
|
||||
@ -1782,53 +1898,22 @@ void ServerLobby::finishedLoadingWorldClient(Event *event)
|
||||
/** Called when a notification from a client is received that the client has
|
||||
* started the race. Once all clients have informed the server that they
|
||||
* have started the race, the server can start. This makes sure that the
|
||||
* server's local time is behind all clients by (at least) their latency,
|
||||
* server's local time is behind all clients by at most max ping,
|
||||
* which in turn means that when the server simulates local time T all
|
||||
* messages from clients at their local time T should have arrived at
|
||||
* the server, which creates smoother play experience.
|
||||
*/
|
||||
void ServerLobby::startedRaceOnClient(Event *event)
|
||||
{
|
||||
if (m_state.load() != WAIT_FOR_RACE_STARTED)
|
||||
{
|
||||
sendBadConnectionMessageToPeer(event->getPeerSP());
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<STKPeer> peer = event->getPeerSP();
|
||||
m_peers_ready.at(peer) = std::make_pair(true, StkTime::getRealTime());
|
||||
Log::info("ServerLobby", "Peer %d has started race at %lf",
|
||||
peer->getHostId(), StkTime::getRealTime());
|
||||
|
||||
if (checkPeersReady())
|
||||
{
|
||||
std::vector<std::pair<STKPeer*, double> > mapping;
|
||||
for (auto p : m_peers_ready)
|
||||
{
|
||||
auto peer = p.first.lock();
|
||||
if (!peer)
|
||||
continue;
|
||||
mapping.emplace_back(peer.get(), p.second.second);
|
||||
}
|
||||
std::sort(mapping.begin(), mapping.end(),
|
||||
[](const std::pair<STKPeer*, double>& a,
|
||||
const std::pair<STKPeer*, double>& b)->bool
|
||||
{
|
||||
return a.second > b.second;
|
||||
});
|
||||
for (unsigned i = 0; i < mapping.size(); i++)
|
||||
{
|
||||
// Server delay is 0.1, so it's around 12 ticks
|
||||
// (0.1 * 120 (physics fps)) for the highest ping client
|
||||
if (i == 0)
|
||||
GameProtocol::lock()->addInitialTicks(mapping[0].first, 12);
|
||||
else
|
||||
{
|
||||
const double diff = mapping[0].second - mapping[i].second;
|
||||
assert(diff >= 0.0);
|
||||
GameProtocol::lock()->addInitialTicks(mapping[i].first,
|
||||
12 + stk_config->time2Ticks((float)diff));
|
||||
}
|
||||
}
|
||||
m_state = DELAY_SERVER;
|
||||
m_server_delay = StkTime::getRealTime() + 0.1;
|
||||
Log::verbose("ServerLobby", "Started delay at %lf set delay to %lf",
|
||||
StkTime::getRealTime(), m_server_delay);
|
||||
}
|
||||
Log::info("ServerLobby", "Peer %s has started race at %lf",
|
||||
peer->getAddress().toString().c_str(), StkTime::getRealTime());
|
||||
} // startedRaceOnClient
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -90,12 +90,12 @@ private:
|
||||
std::map<std::weak_ptr<STKPeer>, std::tuple<std::string, uint8_t, bool>,
|
||||
std::owner_less<std::weak_ptr<STKPeer> > > m_peers_votes;
|
||||
|
||||
/** Keeps track of an artificial server delay (which makes sure that the
|
||||
* data from all clients has arrived when the server computes a certain
|
||||
* timestep.(. It stores the real time since epoch + delta (atm 0.1
|
||||
* seconds), which is the real time at which the server should start. */
|
||||
/** Keeps track of an artificial server delay, which is used to compensate
|
||||
* for network jitter. */
|
||||
double m_server_delay;
|
||||
|
||||
double m_server_max_ping;
|
||||
|
||||
bool m_has_created_server_id_file;
|
||||
|
||||
/** It indicates if this server is unregistered with the stk server. */
|
||||
@ -218,7 +218,8 @@ private:
|
||||
double getModeSpread();
|
||||
double scalingValueForTime(double time);
|
||||
void checkRaceFinished();
|
||||
|
||||
void sendBadConnectionMessageToPeer(std::shared_ptr<STKPeer> p);
|
||||
std::pair<int, float> getHitCaptureLimit(float num_karts);
|
||||
public:
|
||||
ServerLobby();
|
||||
virtual ~ServerLobby();
|
||||
|
@ -66,6 +66,9 @@ RewindManager::RewindManager()
|
||||
*/
|
||||
RewindManager::~RewindManager()
|
||||
{
|
||||
for (RewindInfoEventFunction* rief : m_pending_rief)
|
||||
delete rief;
|
||||
m_pending_rief.clear();
|
||||
} // ~RewindManager
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -176,7 +176,7 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const
|
||||
if (len > 0)
|
||||
{
|
||||
BareNetworkString s(buffer, len);
|
||||
int version = s.getUInt8();
|
||||
int version = s.getUInt32();
|
||||
if (version < stk_config->m_max_server_version ||
|
||||
version > stk_config->m_max_server_version)
|
||||
{
|
||||
|
@ -753,6 +753,20 @@ void STKHost::mainLoop()
|
||||
{
|
||||
m_peer_pings.getData()[p.second->getHostId()] =
|
||||
p.second->getPing();
|
||||
const unsigned ap = p.second->getAveragePing();
|
||||
const unsigned max_ping = UserConfigParams::m_max_ping;
|
||||
if (UserConfigParams::m_kick_high_ping_players &&
|
||||
p.second->isValidated() && ap > max_ping)
|
||||
{
|
||||
Log::info("STKHost", "%s with ping %d is higher than"
|
||||
" %d ms, kick.",
|
||||
p.second->getAddress().toString().c_str(),
|
||||
ap, max_ping);
|
||||
std::lock_guard<std::mutex> lock(m_enet_cmd_mutex);
|
||||
m_enet_cmd.emplace_back(p.second->getENetPeer(),
|
||||
(ENetPacket*)NULL, PDI_BAD_CONNECTION,
|
||||
ECT_DISCONNECT);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto it = m_peers.begin(); it != m_peers.end();)
|
||||
@ -993,7 +1007,7 @@ void STKHost::handleDirectSocketRequest(Network* direct_socket,
|
||||
// Send the answer, consisting of server name, max players,
|
||||
// current players
|
||||
BareNetworkString s((int)name.size()+1+11);
|
||||
s.addUInt8(NetworkConfig::m_server_version);
|
||||
s.addUInt32(NetworkConfig::m_server_version);
|
||||
s.encodeString(name);
|
||||
s.addUInt8(NetworkConfig::get()->getMaxPlayers());
|
||||
s.addUInt8((uint8_t)sl->getGameSetup()->getPlayerCount());
|
||||
|
@ -17,9 +17,11 @@
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "network/stk_peer.hpp"
|
||||
#include "config/stk_config.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
#include "network/crypto.hpp"
|
||||
#include "network/event.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/network_string.hpp"
|
||||
#include "network/stk_host.hpp"
|
||||
#include "network/transport_address.hpp"
|
||||
@ -37,6 +39,7 @@ STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
|
||||
m_host_id = host_id;
|
||||
m_connected_time = (float)StkTime::getRealTime();
|
||||
m_validated.store(false);
|
||||
m_average_ping = 0;
|
||||
} // STKPeer
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -150,10 +153,23 @@ bool STKPeer::isSamePeer(const ENetPeer* peer) const
|
||||
/** Returns the ping to this peer from host, it waits for 3 seconds for a
|
||||
* stable ping returned by enet measured in ms.
|
||||
*/
|
||||
uint32_t STKPeer::getPing() const
|
||||
uint32_t STKPeer::getPing()
|
||||
{
|
||||
if ((float)StkTime::getRealTime() - m_connected_time < 3.0f)
|
||||
return 0;
|
||||
if (NetworkConfig::get()->isServer())
|
||||
{
|
||||
// Average ping in 5 seconds
|
||||
const unsigned ap = stk_config->m_network_state_frequeny * 5;
|
||||
m_previous_pings.push_back(m_enet_peer->roundTripTime);
|
||||
while (m_previous_pings.size() > ap)
|
||||
{
|
||||
m_previous_pings.pop_front();
|
||||
m_average_ping =
|
||||
(uint32_t)(std::accumulate(m_previous_pings.begin(),
|
||||
m_previous_pings.end(), 0) / m_previous_pings.size());
|
||||
}
|
||||
}
|
||||
return m_enet_peer->roundTripTime;
|
||||
} // getPing
|
||||
|
||||
|
@ -30,7 +30,9 @@
|
||||
#include <enet/enet.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
@ -45,6 +47,7 @@ enum PeerDisconnectInfo : unsigned int
|
||||
PDI_TIMEOUT = 0, //!< Timeout disconnected (default in enet).
|
||||
PDI_NORMAL = 1, //!< Normal disconnction with acknowledgement
|
||||
PDI_KICK = 2, //!< Kick disconnection
|
||||
PDI_BAD_CONNECTION = 3, //!< Bad connection disconnection
|
||||
}; // PeerDisconnectInfo
|
||||
|
||||
/*! \class STKPeer
|
||||
@ -76,6 +79,10 @@ protected:
|
||||
|
||||
std::unique_ptr<Crypto> m_crypto;
|
||||
|
||||
std::deque<uint32_t> m_previous_pings;
|
||||
|
||||
uint32_t m_average_ping;
|
||||
|
||||
public:
|
||||
STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id);
|
||||
// ------------------------------------------------------------------------
|
||||
@ -152,12 +159,15 @@ public:
|
||||
void setPingInterval(uint32_t interval)
|
||||
{ enet_peer_ping_interval(m_enet_peer, interval); }
|
||||
// ------------------------------------------------------------------------
|
||||
uint32_t getPing() const;
|
||||
uint32_t getPing();
|
||||
// ------------------------------------------------------------------------
|
||||
Crypto* getCrypto() const { return m_crypto.get(); }
|
||||
// ------------------------------------------------------------------------
|
||||
void setCrypto(std::unique_ptr<Crypto>&& c);
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
uint32_t getAveragePing() const { return m_average_ping; }
|
||||
// ------------------------------------------------------------------------
|
||||
ENetPeer* getENetPeer() const { return m_enet_peer; }
|
||||
}; // STKPeer
|
||||
|
||||
#endif // STK_PEER_HPP
|
||||
|
@ -32,10 +32,12 @@
|
||||
#include "karts/abstract_kart.hpp"
|
||||
#include "karts/controller/controller.hpp"
|
||||
#include "karts/kart_properties_manager.hpp"
|
||||
#include "modes/capture_the_flag.hpp"
|
||||
#include "modes/cutscene_world.hpp"
|
||||
#include "modes/demo_world.hpp"
|
||||
#include "modes/easter_egg_hunt.hpp"
|
||||
#include "modes/follow_the_leader.hpp"
|
||||
#include "modes/free_for_all.hpp"
|
||||
#include "modes/overworld.hpp"
|
||||
#include "modes/profile_world.hpp"
|
||||
#include "modes/standard_race.hpp"
|
||||
@ -76,6 +78,7 @@ RaceManager::RaceManager()
|
||||
m_started_from_overworld = false;
|
||||
m_have_kart_last_position_on_overworld = false;
|
||||
m_num_local_players = 0;
|
||||
m_hit_capture_limit = 0;
|
||||
setMaxGoal(0);
|
||||
setTimeTarget(0.0f);
|
||||
setReverseTrack(false);
|
||||
@ -521,8 +524,15 @@ void RaceManager::startNextRace()
|
||||
World::setWorld(new StandardRace());
|
||||
else if(m_minor_mode==MINOR_MODE_TUTORIAL)
|
||||
World::setWorld(new TutorialWorld());
|
||||
else if(m_minor_mode==MINOR_MODE_3_STRIKES)
|
||||
World::setWorld(new ThreeStrikesBattle());
|
||||
else if(m_minor_mode==MINOR_MODE_BATTLE)
|
||||
{
|
||||
if (m_major_mode == MAJOR_MODE_3_STRIKES)
|
||||
World::setWorld(new ThreeStrikesBattle());
|
||||
else if (m_major_mode == MAJOR_MODE_FREE_FOR_ALL)
|
||||
World::setWorld(new FreeForAll());
|
||||
else if (m_major_mode == MAJOR_MODE_CAPTURE_THE_FLAG)
|
||||
World::setWorld(new CaptureTheFlag());
|
||||
}
|
||||
else if(m_minor_mode==MINOR_MODE_SOCCER)
|
||||
World::setWorld(new SoccerWorld());
|
||||
else if(m_minor_mode==MINOR_MODE_OVERWORLD)
|
||||
@ -918,7 +928,8 @@ void RaceManager::startSingleRace(const std::string &track_ident,
|
||||
|
||||
if (num_laps != -1) setNumLaps( num_laps );
|
||||
|
||||
setMajorMode(RaceManager::MAJOR_MODE_SINGLE);
|
||||
if (m_minor_mode != MINOR_MODE_BATTLE)
|
||||
setMajorMode(RaceManager::MAJOR_MODE_SINGLE);
|
||||
|
||||
setCoinTarget( 0 ); // Might still be set from a previous challenge
|
||||
|
||||
|
@ -89,7 +89,11 @@ public:
|
||||
enum MajorRaceModeType
|
||||
{
|
||||
MAJOR_MODE_GRAND_PRIX,
|
||||
MAJOR_MODE_SINGLE
|
||||
MAJOR_MODE_SINGLE,
|
||||
MAJOR_MODE_FREE_FOR_ALL,
|
||||
MAJOR_MODE_CAPTURE_THE_FLAG,
|
||||
MAJOR_MODE_3_STRIKES
|
||||
|
||||
};
|
||||
|
||||
// quick method to tell the difference between battle modes and race modes
|
||||
@ -112,7 +116,7 @@ public:
|
||||
MINOR_MODE_OVERWORLD = LINEAR_RACE(3, false),
|
||||
MINOR_MODE_TUTORIAL = LINEAR_RACE(4, false),
|
||||
|
||||
MINOR_MODE_3_STRIKES = BATTLE_ARENA(0),
|
||||
MINOR_MODE_BATTLE = BATTLE_ARENA(0),
|
||||
MINOR_MODE_SOCCER = BATTLE_ARENA(1),
|
||||
MINOR_MODE_CUTSCENE = BATTLE_ARENA(2),
|
||||
MINOR_MODE_EASTER_EGG = EASTER_EGG(0)
|
||||
@ -138,7 +142,7 @@ public:
|
||||
case MINOR_MODE_NORMAL_RACE: return IDENT_STD;
|
||||
case MINOR_MODE_TIME_TRIAL: return IDENT_TTRIAL;
|
||||
case MINOR_MODE_FOLLOW_LEADER: return IDENT_FTL;
|
||||
case MINOR_MODE_3_STRIKES: return IDENT_STRIKES;
|
||||
case MINOR_MODE_BATTLE: return IDENT_STRIKES;
|
||||
case MINOR_MODE_EASTER_EGG: return IDENT_EASTER;
|
||||
case MINOR_MODE_SOCCER: return IDENT_SOCCER;
|
||||
default: assert(false);
|
||||
@ -157,7 +161,7 @@ public:
|
||||
case MINOR_MODE_NORMAL_RACE: return "/gui/mode_normal.png";
|
||||
case MINOR_MODE_TIME_TRIAL: return "/gui/mode_tt.png";
|
||||
case MINOR_MODE_FOLLOW_LEADER: return "/gui/mode_ftl.png";
|
||||
case MINOR_MODE_3_STRIKES: return "/gui/mode_3strikes.png";
|
||||
case MINOR_MODE_BATTLE: return "/gui/mode_3strikes.png";
|
||||
case MINOR_MODE_EASTER_EGG: return "/gui/mode_easter.png";
|
||||
case MINOR_MODE_SOCCER: return "/gui/mode_soccer.png";
|
||||
default: assert(false); return NULL;
|
||||
@ -179,7 +183,7 @@ public:
|
||||
//I18N: Game mode
|
||||
case MINOR_MODE_FOLLOW_LEADER: return _("Follow the Leader");
|
||||
//I18N: Game mode
|
||||
case MINOR_MODE_3_STRIKES: return _("3 Strikes Battle");
|
||||
case MINOR_MODE_BATTLE: return _("3 Strikes Battle");
|
||||
//I18N: Game mode
|
||||
case MINOR_MODE_EASTER_EGG: return _("Egg Hunt");
|
||||
//I18N: Game mode
|
||||
@ -197,7 +201,7 @@ public:
|
||||
case MINOR_MODE_NORMAL_RACE: return true;
|
||||
case MINOR_MODE_TIME_TRIAL: return true;
|
||||
case MINOR_MODE_FOLLOW_LEADER: return true;
|
||||
case MINOR_MODE_3_STRIKES: return true;
|
||||
case MINOR_MODE_BATTLE: return true;
|
||||
case MINOR_MODE_EASTER_EGG: return false;
|
||||
case MINOR_MODE_SOCCER: return true;
|
||||
default: assert(false); return false;
|
||||
@ -216,7 +220,7 @@ public:
|
||||
if (name==IDENT_STD ) return MINOR_MODE_NORMAL_RACE;
|
||||
else if (name==IDENT_TTRIAL ) return MINOR_MODE_TIME_TRIAL;
|
||||
else if (name==IDENT_FTL ) return MINOR_MODE_FOLLOW_LEADER;
|
||||
else if (name==IDENT_STRIKES) return MINOR_MODE_3_STRIKES;
|
||||
else if (name==IDENT_STRIKES) return MINOR_MODE_BATTLE;
|
||||
else if (name==IDENT_EASTER ) return MINOR_MODE_EASTER_EGG;
|
||||
else if (name==IDENT_SOCCER) return MINOR_MODE_SOCCER;
|
||||
|
||||
@ -336,7 +340,7 @@ private:
|
||||
int m_coin_target;
|
||||
float m_time_target;
|
||||
int m_goal_target;
|
||||
|
||||
int m_hit_capture_limit;
|
||||
void startNextRace(); // start a next race
|
||||
|
||||
friend bool operator< (const KartStatus& left, const KartStatus& right)
|
||||
@ -518,7 +522,7 @@ public:
|
||||
case MINOR_MODE_NORMAL_RACE: return "normal";
|
||||
case MINOR_MODE_TIME_TRIAL: return "time-trial";
|
||||
case MINOR_MODE_FOLLOW_LEADER: return "follow-the-leader";
|
||||
case MINOR_MODE_3_STRIKES: return "battle";
|
||||
case MINOR_MODE_BATTLE: return "battle";
|
||||
case MINOR_MODE_EASTER_EGG: return "egg-hunt";
|
||||
case MINOR_MODE_SOCCER: return "soccer";
|
||||
default: assert(false); return "";
|
||||
@ -538,7 +542,7 @@ public:
|
||||
*/
|
||||
int getNumLaps() const
|
||||
{
|
||||
if(m_minor_mode==MINOR_MODE_3_STRIKES ||
|
||||
if(m_minor_mode==MINOR_MODE_BATTLE ||
|
||||
m_minor_mode==MINOR_MODE_FOLLOW_LEADER ||
|
||||
m_minor_mode==MINOR_MODE_SOCCER ||
|
||||
m_minor_mode==MINOR_MODE_EASTER_EGG )
|
||||
@ -737,7 +741,7 @@ public:
|
||||
// and each World may set m_use_highscores to true or false.
|
||||
// The reason for this duplication is that we might want to know
|
||||
// whether to display highscores without creating a World.
|
||||
return m_minor_mode != MINOR_MODE_3_STRIKES &&
|
||||
return m_minor_mode != MINOR_MODE_BATTLE &&
|
||||
m_minor_mode != MINOR_MODE_SOCCER &&
|
||||
m_minor_mode != MINOR_MODE_FOLLOW_LEADER;
|
||||
} // modeHasHighscore
|
||||
@ -837,6 +841,20 @@ public:
|
||||
void configGrandPrixResultFromNetwork(NetworkString& ns);
|
||||
// ------------------------------------------------------------------------
|
||||
void clearNetworkGrandPrixResult();
|
||||
// ------------------------------------------------------------------------
|
||||
void setHitCaptureTime(int hc, float time)
|
||||
{
|
||||
m_hit_capture_limit = hc;
|
||||
m_time_target = time;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
int getHitCaptureLimit() const { return m_hit_capture_limit; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool teamEnabled() const
|
||||
{
|
||||
return m_minor_mode == MINOR_MODE_SOCCER ||
|
||||
m_major_mode == MAJOR_MODE_CAPTURE_THE_FLAG;
|
||||
}
|
||||
|
||||
}; // RaceManager
|
||||
|
||||
|
@ -252,6 +252,8 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay, int ca
|
||||
return false;
|
||||
}
|
||||
|
||||
rd.m_track = t;
|
||||
|
||||
fgets(s, 1023, fd);
|
||||
if (sscanf(s, "laps: %u", &rd.m_laps) != 1)
|
||||
{
|
||||
|
@ -20,6 +20,7 @@
|
||||
#define HEADER_REPLAY__PLAY_HPP
|
||||
|
||||
#include "replay/replay_base.hpp"
|
||||
#include "tracks/track.hpp"
|
||||
|
||||
#include "irrString.h"
|
||||
#include <algorithm>
|
||||
@ -57,6 +58,7 @@ public:
|
||||
public:
|
||||
std::string m_filename;
|
||||
std::string m_track_name;
|
||||
Track* m_track;
|
||||
std::string m_minor_mode;
|
||||
core::stringw m_stk_version;
|
||||
core::stringw m_user_name;
|
||||
@ -76,7 +78,7 @@ public:
|
||||
switch (m_sort_order)
|
||||
{
|
||||
case SO_TRACK:
|
||||
return m_track_name < r.m_track_name;
|
||||
return m_track->getSortName() < r.m_track->getSortName();
|
||||
break;
|
||||
case SO_KART_NUM:
|
||||
return m_kart_list.size() < r.m_kart_list.size();
|
||||
@ -133,8 +135,8 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
void sortReplay(bool reverse)
|
||||
{
|
||||
(reverse ? std::sort(m_replay_file_list.rbegin(),
|
||||
m_replay_file_list.rend()) : std::sort(m_replay_file_list.begin(),
|
||||
(reverse ? std::stable_sort(m_replay_file_list.rbegin(),
|
||||
m_replay_file_list.rend()) : std::stable_sort(m_replay_file_list.begin(),
|
||||
m_replay_file_list.end()));
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -162,10 +162,6 @@ void AddonsScreen::init()
|
||||
|
||||
m_reloading = false;
|
||||
|
||||
m_sort_desc = false;
|
||||
m_sort_default = true;
|
||||
m_sort_col = 0;
|
||||
|
||||
getWidget<GUIEngine::RibbonWidget>("category")->setActive(false);
|
||||
|
||||
if(UserConfigParams::logAddons())
|
||||
@ -232,7 +228,7 @@ void AddonsScreen::tearDown()
|
||||
* updated.
|
||||
* \param type Must be 'kart' or 'track'.
|
||||
*/
|
||||
void AddonsScreen::loadList()
|
||||
void AddonsScreen::loadList(bool sort_desc)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
// Get the filter by words.
|
||||
@ -287,7 +283,7 @@ void AddonsScreen::loadList()
|
||||
|
||||
sorted_list.push_back(&addon);
|
||||
}
|
||||
sorted_list.insertionSort(/*start=*/0, m_sort_desc);
|
||||
sorted_list.insertionSort(/*start=*/0, sort_desc);
|
||||
|
||||
GUIEngine::ListWidget* w_list =
|
||||
getWidget<GUIEngine::ListWidget>("list_addons");
|
||||
@ -420,33 +416,20 @@ void AddonsScreen::loadList()
|
||||
} // loadList
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void AddonsScreen::onColumnClicked(int column_id)
|
||||
void AddonsScreen::onColumnClicked(int column_id, bool sort_desc, bool sort_default)
|
||||
{
|
||||
if (m_sort_col != column_id)
|
||||
{
|
||||
m_sort_desc = false;
|
||||
m_sort_default = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_sort_default) m_sort_desc = !m_sort_desc;
|
||||
m_sort_default = !m_sort_desc && !m_sort_default;
|
||||
}
|
||||
|
||||
m_sort_col = column_id;
|
||||
|
||||
switch(column_id)
|
||||
{
|
||||
case 0:
|
||||
Addon::setSortOrder(m_sort_default ? Addon::SO_DEFAULT : Addon::SO_NAME);
|
||||
Addon::setSortOrder(sort_default ? Addon::SO_DEFAULT : Addon::SO_NAME);
|
||||
break;
|
||||
case 1:
|
||||
Addon::setSortOrder(m_sort_default ? Addon::SO_DEFAULT : Addon::SO_DATE);
|
||||
Addon::setSortOrder(sort_default ? Addon::SO_DEFAULT : Addon::SO_DATE);
|
||||
break;
|
||||
default: assert(0); break;
|
||||
} // switch
|
||||
/** \brief Toggle the sort order after column click **/
|
||||
loadList();
|
||||
loadList(sort_desc && !sort_default);
|
||||
} // onColumnClicked
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -73,13 +73,6 @@ private:
|
||||
|
||||
bool m_reloading;
|
||||
|
||||
/** \brief To check (and set) if sort order is descending **/
|
||||
bool m_sort_desc;
|
||||
|
||||
bool m_sort_default;
|
||||
|
||||
int m_sort_col;
|
||||
|
||||
/** List of date filters **/
|
||||
std::vector<DateFilter> m_date_filters;
|
||||
|
||||
@ -88,7 +81,7 @@ private:
|
||||
public:
|
||||
|
||||
/** Load the addons into the main list.*/
|
||||
void loadList();
|
||||
void loadList(bool sort_desc = false);
|
||||
|
||||
/** \brief implement callback from parent class GUIEngine::Screen */
|
||||
virtual void loadedFromFile() OVERRIDE;
|
||||
@ -102,7 +95,7 @@ public:
|
||||
/** \brief implement callback from parent class GUIEngine::Screen */
|
||||
virtual void beforeAddingWidget() OVERRIDE;
|
||||
|
||||
virtual void onColumnClicked(int columnId) OVERRIDE;
|
||||
virtual void onColumnClicked(int column_id, bool sort_desc, bool sort_default) OVERRIDE;
|
||||
|
||||
virtual void init() OVERRIDE;
|
||||
virtual void tearDown() OVERRIDE;
|
||||
|
@ -46,6 +46,8 @@ CreateServerScreen::CreateServerScreen() : Screen("online/create_server.stkgui")
|
||||
|
||||
void CreateServerScreen::loadedFromFile()
|
||||
{
|
||||
m_prev_mode = 0;
|
||||
m_prev_value = 0;
|
||||
m_name_widget = getWidget<TextBoxWidget>("name");
|
||||
assert(m_name_widget != NULL);
|
||||
|
||||
@ -105,8 +107,8 @@ void CreateServerScreen::init()
|
||||
// -- Game modes
|
||||
RibbonWidget* gamemode = getWidget<RibbonWidget>("gamemode");
|
||||
assert(gamemode != NULL);
|
||||
gamemode->setSelection(0, PLAYER_ID_GAME_MASTER);
|
||||
updateMoreOption(0);
|
||||
gamemode->setSelection(m_prev_mode, PLAYER_ID_GAME_MASTER);
|
||||
updateMoreOption(m_prev_mode);
|
||||
} // init
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -133,7 +135,9 @@ void CreateServerScreen::eventCallback(Widget* widget, const std::string& name,
|
||||
{
|
||||
const int selection =
|
||||
m_game_mode_widget->getSelection(PLAYER_ID_GAME_MASTER);
|
||||
m_prev_value = 0;
|
||||
updateMoreOption(selection);
|
||||
m_prev_mode = selection;
|
||||
}
|
||||
|
||||
} // eventCallback
|
||||
@ -157,7 +161,23 @@ void CreateServerScreen::updateMoreOption(int game_mode)
|
||||
{
|
||||
m_more_options_spinner->addLabel(StringUtils::toWString(i));
|
||||
}
|
||||
m_more_options_spinner->setValue(0);
|
||||
m_more_options_spinner->setValue(m_prev_value);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
m_more_options_text->setVisible(true);
|
||||
m_more_options_spinner->setVisible(true);
|
||||
m_more_options_spinner->clearLabels();
|
||||
//I18N: In the create server screen, show various battle mode available
|
||||
m_more_options_text->setText(_("Battle mode"), false);
|
||||
m_more_options_spinner->setVisible(true);
|
||||
m_more_options_spinner->clearLabels();
|
||||
//I18N: In the create server screen for battle server
|
||||
m_more_options_spinner->addLabel(_("Free-For-All"));
|
||||
//I18N: In the create server screen for battle server
|
||||
m_more_options_spinner->addLabel(_("Capture The Flag"));
|
||||
m_more_options_spinner->setValue(m_prev_value);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
@ -173,7 +193,7 @@ void CreateServerScreen::updateMoreOption(int game_mode)
|
||||
m_more_options_spinner->addLabel(_("Time limit"));
|
||||
//I18N: In the create server screen for soccer server
|
||||
m_more_options_spinner->addLabel(_("Goals limit"));
|
||||
m_more_options_spinner->setValue(0);
|
||||
m_more_options_spinner->setValue(m_prev_value);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -195,9 +215,6 @@ void CreateServerScreen::onUpdate(float delta)
|
||||
if(!STKHost::existHost())
|
||||
return;
|
||||
|
||||
//FIXME If we really want a gui, we need to decide what else to do here
|
||||
// For now start the (wrong i.e. client) lobby, to prevent to create
|
||||
// a server more than once.
|
||||
NetworkingLobby::getInstance()->push();
|
||||
} // onUpdate
|
||||
|
||||
@ -315,20 +332,31 @@ void CreateServerScreen::createServer()
|
||||
if (m_more_options_spinner->isVisible())
|
||||
{
|
||||
int esi = m_more_options_spinner->getValue();
|
||||
if (gamemode_widget->getSelection(PLAYER_ID_GAME_MASTER)
|
||||
!= 3/*is soccer*/)
|
||||
{
|
||||
// Grand prix track count
|
||||
if (esi > 0)
|
||||
server_cfg << " --network-gp=" << esi;
|
||||
}
|
||||
else
|
||||
if (gamemode_widget->getSelection(PLAYER_ID_GAME_MASTER) ==
|
||||
3/*is soccer*/)
|
||||
{
|
||||
if (esi == 0)
|
||||
server_cfg << " --soccer-timed";
|
||||
else
|
||||
server_cfg << " --soccer-goals";
|
||||
}
|
||||
else if (gamemode_widget->getSelection(PLAYER_ID_GAME_MASTER) ==
|
||||
2/*is battle*/)
|
||||
{
|
||||
server_cfg << " --battle-mode=" << esi;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Grand prix track count
|
||||
if (esi > 0)
|
||||
server_cfg << " --network-gp=" << esi;
|
||||
}
|
||||
m_prev_mode = gamemode_widget->getSelection(PLAYER_ID_GAME_MASTER);
|
||||
m_prev_value = esi;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_prev_mode = m_prev_value = 0;
|
||||
}
|
||||
|
||||
SeparateProcess* sp =
|
||||
|
@ -33,6 +33,8 @@ class CreateServerScreen : public GUIEngine::Screen,
|
||||
public GUIEngine::ScreenSingleton<CreateServerScreen>
|
||||
{
|
||||
private:
|
||||
int m_prev_mode, m_prev_value;
|
||||
|
||||
friend class GUIEngine::ScreenSingleton<CreateServerScreen>;
|
||||
|
||||
CreateServerScreen();
|
||||
|
@ -32,7 +32,6 @@ using namespace GUIEngine;
|
||||
*/
|
||||
GhostReplaySelection::GhostReplaySelection() : Screen("ghost_replay_selection.stkgui")
|
||||
{
|
||||
m_sort_desc = true;
|
||||
m_is_comparing = false;
|
||||
m_replay_to_compare_uid = 0;
|
||||
} // GhostReplaySelection
|
||||
@ -51,6 +50,7 @@ void GhostReplaySelection::refresh(bool forced_update, bool update_columns)
|
||||
{
|
||||
if (ReplayPlay::get()->getNumReplayFile() == 0 || forced_update)
|
||||
ReplayPlay::get()->loadAllReplayFile();
|
||||
defaultSort();
|
||||
loadList();
|
||||
|
||||
// Allow to disable a comparison, but not to start one
|
||||
@ -136,7 +136,6 @@ void GhostReplaySelection::init()
|
||||
*/
|
||||
void GhostReplaySelection::loadList()
|
||||
{
|
||||
ReplayPlay::get()->sortReplay(m_sort_desc);
|
||||
m_replay_list_widget->clear();
|
||||
|
||||
if (ReplayPlay::get()->getNumReplayFile() == 0)
|
||||
@ -424,8 +423,17 @@ void GhostReplaySelection::onConfirm()
|
||||
/** Change the sort order if a column was clicked.
|
||||
* \param column_id ID of the column that was clicked.
|
||||
*/
|
||||
void GhostReplaySelection::onColumnClicked(int column_id)
|
||||
void GhostReplaySelection::onColumnClicked(int column_id, bool sort_desc, bool sort_default)
|
||||
{
|
||||
// Begin by resorting the list to default
|
||||
defaultSort();
|
||||
|
||||
if (sort_default)
|
||||
{
|
||||
loadList();
|
||||
return;
|
||||
}
|
||||
|
||||
int diff_difficulty = m_same_difficulty ? 1 : 0;
|
||||
int diff_linear = m_active_mode_is_linear ? 0 : 1;
|
||||
|
||||
@ -457,13 +465,26 @@ void GhostReplaySelection::onColumnClicked(int column_id)
|
||||
else
|
||||
assert(0);
|
||||
|
||||
printf("column_id is %d\n", column_id);
|
||||
ReplayPlay::get()->sortReplay(sort_desc);
|
||||
|
||||
/** \brief Toggle the sort order after column click **/
|
||||
m_sort_desc = !m_sort_desc;
|
||||
loadList();
|
||||
} // onColumnClicked
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Apply the default sorting to the replay list
|
||||
*/
|
||||
void GhostReplaySelection::defaultSort()
|
||||
{
|
||||
ReplayPlay::setSortOrder(ReplayPlay::SO_TIME);
|
||||
ReplayPlay::get()->sortReplay(/* decreasing order */ false);
|
||||
ReplayPlay::setSortOrder(ReplayPlay::SO_LAPS);
|
||||
ReplayPlay::get()->sortReplay(/* decreasing order */ false);
|
||||
ReplayPlay::setSortOrder(ReplayPlay::SO_REV);
|
||||
ReplayPlay::get()->sortReplay(/* decreasing order */ false);
|
||||
ReplayPlay::setSortOrder(ReplayPlay::SO_TRACK);
|
||||
ReplayPlay::get()->sortReplay(/* decreasing order */ false);
|
||||
} // defaultSort
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
bool GhostReplaySelection::onEscapePressed()
|
||||
{
|
||||
|
@ -54,7 +54,6 @@ private:
|
||||
bool m_same_difficulty;
|
||||
bool m_same_version;
|
||||
bool m_best_times;
|
||||
bool m_sort_desc;
|
||||
bool m_is_comparing;
|
||||
bool m_active_mode_is_linear;
|
||||
RaceManager::MinorRaceModeType m_active_mode;
|
||||
@ -62,6 +61,7 @@ private:
|
||||
// Using the UID guarantees exact matchess
|
||||
uint64_t m_replay_to_compare_uid;
|
||||
|
||||
void defaultSort();
|
||||
|
||||
public:
|
||||
|
||||
@ -89,7 +89,7 @@ public:
|
||||
/** \brief implement callback from parent class GUIEngine::Screen */
|
||||
virtual void beforeAddingWidget() OVERRIDE;
|
||||
|
||||
virtual void onColumnClicked(int columnId) OVERRIDE;
|
||||
virtual void onColumnClicked(int column_id, bool sort_desc, bool sort_default) OVERRIDE;
|
||||
|
||||
virtual void init() OVERRIDE;
|
||||
|
||||
|
@ -34,7 +34,8 @@ using namespace irr::gui;
|
||||
using namespace Online;
|
||||
|
||||
int OnlineProfileFriends::m_sort_column = 0;
|
||||
bool OnlineProfileFriends::m_sort_increasing = true;
|
||||
bool OnlineProfileFriends::m_sort_desc = false;
|
||||
bool OnlineProfileFriends::m_sort_default = true;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
/** Constructor for a display of all friends.
|
||||
@ -81,8 +82,6 @@ void OnlineProfileFriends::beforeAddingWidget()
|
||||
void OnlineProfileFriends::init()
|
||||
{
|
||||
OnlineProfileBase::init();
|
||||
m_sort_column = 0;
|
||||
m_sort_increasing = true;
|
||||
m_profile_tabs->select( m_friends_tab->m_properties[PROP_ID],
|
||||
PLAYER_ID_GAME_MASTER );
|
||||
m_profile_tabs->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
|
||||
@ -120,15 +119,11 @@ void OnlineProfileFriends::eventCallback(Widget* widget,
|
||||
} // eventCallback
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void OnlineProfileFriends::onColumnClicked(int column_id)
|
||||
void OnlineProfileFriends::onColumnClicked(int column_id, bool sort_desc, bool sort_default)
|
||||
{
|
||||
if (column_id == m_sort_column)
|
||||
m_sort_increasing = !m_sort_increasing;
|
||||
else
|
||||
{
|
||||
m_sort_increasing = true;
|
||||
}
|
||||
m_sort_column = column_id;
|
||||
m_sort_desc = sort_desc;
|
||||
m_sort_default = sort_default;
|
||||
displayResults();
|
||||
} // onColumnClicked
|
||||
|
||||
@ -140,20 +135,14 @@ bool OnlineProfileFriends::compareFriends(int f1, int f2)
|
||||
switch (m_sort_column)
|
||||
{
|
||||
case 0: // sort by name
|
||||
if (m_sort_increasing)
|
||||
return p1->getUserName().lower_ignore_case(p2->getUserName());
|
||||
else
|
||||
return p2->getUserName().lower_ignore_case(p1->getUserName());
|
||||
return p1->getUserName().lower_ignore_case(p2->getUserName());
|
||||
case 1: // sort by date
|
||||
{
|
||||
OnlineProfile::RelationInfo *r1 = p1->getRelationInfo();
|
||||
OnlineProfile::RelationInfo *r2 = p2->getRelationInfo();
|
||||
// While we are comparing dates that are strings, they are normalised
|
||||
// i.e. contain leading 0 etc, so a string compare works as expected.
|
||||
if (m_sort_increasing)
|
||||
return r1->getDate().lower_ignore_case(r2->getDate());
|
||||
else
|
||||
return r2->getDate().lower_ignore_case(r1->getDate());
|
||||
return r1->getDate().lower_ignore_case(r2->getDate());
|
||||
}
|
||||
case 2: // sort by online status
|
||||
{
|
||||
@ -162,16 +151,12 @@ bool OnlineProfileFriends::compareFriends(int f1, int f2)
|
||||
// In case of same online status, sort by name
|
||||
if (r1->isOnline() == r2->isOnline())
|
||||
{
|
||||
if (m_sort_increasing)
|
||||
return p1->getUserName().lower_ignore_case(p2->getUserName());
|
||||
else
|
||||
return p2->getUserName().lower_ignore_case(p1->getUserName());
|
||||
return p1->getUserName().lower_ignore_case(p2->getUserName());
|
||||
}
|
||||
else
|
||||
if (m_sort_increasing)
|
||||
return r1->isOnline() < r2->isOnline();
|
||||
else
|
||||
return r2->isOnline() < r1->isOnline();
|
||||
{
|
||||
return r1->isOnline() < r2->isOnline();
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
@ -187,7 +172,8 @@ void OnlineProfileFriends::displayResults()
|
||||
{
|
||||
m_friends_list_widget->clear();
|
||||
OnlineProfile::IDList friends = m_visiting_profile->getFriends();
|
||||
std::sort(friends.begin(), friends.end(), compareFriends);
|
||||
(m_sort_desc && !m_sort_default) ? std::sort(friends.rbegin(), friends.rend(), compareFriends)
|
||||
: std::sort(friends.begin(), friends.end(), compareFriends);
|
||||
for (unsigned int i = 0; i < friends.size(); i++)
|
||||
{
|
||||
std::vector<ListWidget::ListCell> row;
|
||||
|
@ -48,11 +48,13 @@ private:
|
||||
GUIEngine::TextBoxWidget *m_search_box_widget;
|
||||
|
||||
bool m_waiting_for_friends;
|
||||
|
||||
/** Which column to use for sorting. */
|
||||
static int m_sort_column;
|
||||
|
||||
/** True is sorting should be increasing. */
|
||||
static bool m_sort_increasing;
|
||||
static bool m_sort_desc;
|
||||
|
||||
static bool m_sort_default;
|
||||
|
||||
void displayResults();
|
||||
static bool compareFriends(int f1, int f2);
|
||||
@ -72,7 +74,7 @@ public:
|
||||
|
||||
virtual void onUpdate(float delta) OVERRIDE;
|
||||
virtual void beforeAddingWidget() OVERRIDE;
|
||||
virtual void onColumnClicked(int columnId) OVERRIDE;
|
||||
virtual void onColumnClicked(int column_id, bool sort_desc, bool sort_default) OVERRIDE;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Triggers a reload of the friend list next time this menu is shown. */
|
||||
|
@ -22,6 +22,7 @@
|
||||
using namespace irr;
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
#include "challenges/unlock_manager.hpp"
|
||||
#include "config/user_config.hpp"
|
||||
@ -85,7 +86,7 @@ RaceGUI::RaceGUI()
|
||||
m_negative_timer_additional_width = area.Width;
|
||||
|
||||
if (race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER ||
|
||||
race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES ||
|
||||
race_manager->getMinorMode()==RaceManager::MINOR_MODE_BATTLE ||
|
||||
race_manager->getNumLaps() > 9)
|
||||
m_lap_width = font->getDimension(L"99/99").Width;
|
||||
else
|
||||
@ -381,8 +382,10 @@ void RaceGUI::drawGlobalTimer()
|
||||
bool use_digit_font = true;
|
||||
|
||||
float elapsed_time = World::getWorld()->getTime();
|
||||
if (!race_manager->hasTimeTarget() || race_manager
|
||||
->getMinorMode()==RaceManager::MINOR_MODE_SOCCER)
|
||||
if (!race_manager->hasTimeTarget() ||
|
||||
race_manager ->getMinorMode()==RaceManager::MINOR_MODE_SOCCER ||
|
||||
race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL ||
|
||||
race_manager->getMajorMode() == RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG)
|
||||
{
|
||||
sw = core::stringw (
|
||||
StringUtils::timeToString(elapsed_time).c_str() );
|
||||
@ -710,6 +713,28 @@ void RaceGUI::drawRank(const AbstractKart *kart,
|
||||
float min_ratio, int meter_width,
|
||||
int meter_height, float dt)
|
||||
{
|
||||
static video::SColor color = video::SColor(255, 255, 255, 255);
|
||||
// Draw hit or capture limit in network game
|
||||
if ((race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL ||
|
||||
race_manager->getMajorMode() == RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG) &&
|
||||
race_manager->getHitCaptureLimit() != std::numeric_limits<int>::max())
|
||||
{
|
||||
gui::ScalableFont* font = GUIEngine::getHighresDigitFont();
|
||||
font->setScale(min_ratio * 1.0f);
|
||||
font->setShadow(video::SColor(255, 128, 0, 0));
|
||||
std::ostringstream oss;
|
||||
oss << race_manager->getHitCaptureLimit();
|
||||
|
||||
core::recti pos;
|
||||
pos.LowerRightCorner = core::vector2di(int(offset.X + 0.64f*meter_width),
|
||||
int(offset.Y - 0.49f*meter_height));
|
||||
pos.UpperLeftCorner = core::vector2di(int(offset.X + 0.64f*meter_width),
|
||||
int(offset.Y - 0.49f*meter_height));
|
||||
|
||||
font->draw(oss.str().c_str(), pos, color, true, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw rank
|
||||
WorldWithRank *world = dynamic_cast<WorldWithRank*>(World::getWorld());
|
||||
if (!world || !world->displayRank())
|
||||
@ -775,7 +800,6 @@ void RaceGUI::drawRank(const AbstractKart *kart,
|
||||
pos.UpperLeftCorner = core::vector2di(int(offset.X + 0.64f*meter_width),
|
||||
int(offset.Y - 0.49f*meter_height));
|
||||
|
||||
static video::SColor color = video::SColor(255, 255, 255, 255);
|
||||
font->draw(oss.str().c_str(), pos, color, true, true);
|
||||
font->setScale(1.0f);
|
||||
} // drawRank
|
||||
|
@ -769,7 +769,7 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
|
||||
if (kart->getPosition() == -1)//if position is not set
|
||||
{
|
||||
//we use karts ordered by id only
|
||||
//(needed for beginning of MINOR_MODE_3_STRIKES)
|
||||
//(needed for beginning of MINOR_MODE_BATTLE)
|
||||
kart= world->getKart(position-1);
|
||||
}
|
||||
|
||||
@ -781,7 +781,7 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
|
||||
int lap = info.lap;
|
||||
|
||||
// In battle mode mode there is no distance along track etc.
|
||||
if( minor_mode==RaceManager::MINOR_MODE_3_STRIKES ||
|
||||
if( minor_mode==RaceManager::MINOR_MODE_BATTLE ||
|
||||
minor_mode==RaceManager::MINOR_MODE_EASTER_EGG)
|
||||
{
|
||||
x = x_base;
|
||||
@ -845,14 +845,21 @@ void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_kart_display_infos[kart_id].m_text.size() > 0)
|
||||
if (info.m_text.size() > 0)
|
||||
{
|
||||
core::rect<s32> pos(x+ICON_PLAYER_WIDTH, y+5,
|
||||
x+ICON_PLAYER_WIDTH, y+5);
|
||||
core::stringw s=info.m_text.c_str();
|
||||
|
||||
font->draw(s.c_str(), pos, info.m_color, false, false, NULL,
|
||||
true /* ignore RTL */);
|
||||
if (info.m_outlined_font)
|
||||
{
|
||||
GUIEngine::getOutlineFont()->draw(info.m_text, pos,
|
||||
GUIEngine::getSkin()->getColor("font::normal"), false,
|
||||
false, NULL, true/*ignore RTL*/);
|
||||
}
|
||||
else
|
||||
{
|
||||
font->draw(info.m_text, pos, info.m_color, false, false, NULL,
|
||||
true/*ignore RTL*/);
|
||||
}
|
||||
}
|
||||
|
||||
if (info.special_title.size() > 0)
|
||||
|
@ -66,6 +66,8 @@ public:
|
||||
|
||||
/** Current lap of this kart, or -1 if irrelevant. */
|
||||
int lap;
|
||||
|
||||
bool m_outlined_font = false;
|
||||
}; // KartIconDisplayInfo
|
||||
|
||||
private:
|
||||
|
@ -42,9 +42,9 @@
|
||||
#include "karts/kart_properties_manager.hpp"
|
||||
#include "modes/cutscene_world.hpp"
|
||||
#include "modes/demo_world.hpp"
|
||||
#include "modes/free_for_all.hpp"
|
||||
#include "modes/overworld.hpp"
|
||||
#include "modes/soccer_world.hpp"
|
||||
#include "modes/world_with_rank.hpp"
|
||||
#include "network/network_config.hpp"
|
||||
#include "network/stk_host.hpp"
|
||||
#include "network/protocols/client_lobby.hpp"
|
||||
@ -523,6 +523,7 @@ void RaceResultGUI::backToLobby()
|
||||
m_width_kart_name = 0;
|
||||
float max_finish_time = 0;
|
||||
|
||||
FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld());
|
||||
for (unsigned int position = first_position;
|
||||
position <= race_manager->getNumberOfKarts() - sta; position++)
|
||||
{
|
||||
@ -543,6 +544,13 @@ void RaceResultGUI::backToLobby()
|
||||
{
|
||||
ri->m_finish_time_string = core::stringw(_("Eliminated"));
|
||||
}
|
||||
else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL ||
|
||||
race_manager->getMajorMode() == RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG)
|
||||
{
|
||||
assert(ffa);
|
||||
ri->m_finish_time_string =
|
||||
StringUtils::toWString(ffa->getKartScore(kart->getWorldKartId()));
|
||||
}
|
||||
else
|
||||
{
|
||||
const float time = kart->getFinishTime();
|
||||
|
@ -65,6 +65,7 @@ void RaceSetupScreen::init()
|
||||
RibbonWidget* w = getWidget<RibbonWidget>("difficulty");
|
||||
assert( w != NULL );
|
||||
|
||||
race_manager->setMajorMode(RaceManager::MAJOR_MODE_SINGLE);
|
||||
if (UserConfigParams::m_difficulty == RaceManager::DIFFICULTY_BEST &&
|
||||
PlayerManager::getCurrentPlayer()->isLocked("difficulty_best"))
|
||||
{
|
||||
@ -108,10 +109,10 @@ void RaceSetupScreen::init()
|
||||
}
|
||||
|
||||
irr::core::stringw name4 = irr::core::stringw(
|
||||
RaceManager::getNameOf(RaceManager::MINOR_MODE_3_STRIKES)) + L"\n";
|
||||
RaceManager::getNameOf(RaceManager::MINOR_MODE_BATTLE)) + L"\n";
|
||||
//FIXME: avoid duplicating descriptions from the help menu!
|
||||
name4 += _("Hit others with weapons until they lose all their lives.");
|
||||
w2->addItem( name4, IDENT_STRIKES, RaceManager::getIconOf(RaceManager::MINOR_MODE_3_STRIKES));
|
||||
w2->addItem( name4, IDENT_STRIKES, RaceManager::getIconOf(RaceManager::MINOR_MODE_BATTLE));
|
||||
|
||||
irr::core::stringw name5 = irr::core::stringw(
|
||||
RaceManager::getNameOf(RaceManager::MINOR_MODE_SOCCER)) + L"\n";
|
||||
@ -223,7 +224,8 @@ void RaceSetupScreen::eventCallback(Widget* widget, const std::string& name,
|
||||
}
|
||||
else if (selectedMode == IDENT_STRIKES)
|
||||
{
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_3_STRIKES);
|
||||
race_manager->setMinorMode(RaceManager::MINOR_MODE_BATTLE);
|
||||
race_manager->setMajorMode(RaceManager::MAJOR_MODE_3_STRIKES);
|
||||
UserConfigParams::m_game_mode = CONFIG_CODE_3STRIKES;
|
||||
ArenasScreen::getInstance()->push();
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ void ServerSelection::beforeAddingWidget()
|
||||
void ServerSelection::init()
|
||||
{
|
||||
Screen::init();
|
||||
m_sort_desc = true;
|
||||
m_sort_desc = false;
|
||||
/** Triggers the loading of the server list in the servers manager. */
|
||||
refresh(true);
|
||||
} // init
|
||||
@ -200,10 +200,18 @@ void ServerSelection::loadList(unsigned sort_case)
|
||||
/** Change the sort order if a column was clicked.
|
||||
* \param column_id ID of the column that was clicked.
|
||||
*/
|
||||
void ServerSelection::onColumnClicked(int column_id)
|
||||
void ServerSelection::onColumnClicked(int column_id, bool sort_desc, bool sort_default)
|
||||
{
|
||||
m_sort_desc = !m_sort_desc;
|
||||
loadList(column_id);
|
||||
if (sort_default)
|
||||
{
|
||||
m_sort_desc = false;
|
||||
loadList(/* distance */ 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sort_desc = sort_desc;
|
||||
loadList(column_id);
|
||||
}
|
||||
} // onColumnClicked
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -308,5 +316,5 @@ void ServerSelection::copyFromServersManager()
|
||||
{
|
||||
return a->isPasswordProtected() != m_private_server->getState();
|
||||
}), m_servers.end());
|
||||
loadList(0);
|
||||
loadList(/* distance */ 5);
|
||||
} // copyFromServersManager
|
||||
|
@ -80,7 +80,7 @@ public:
|
||||
/** \brief implement callback from parent class GUIEngine::Screen */
|
||||
virtual void beforeAddingWidget() OVERRIDE;
|
||||
|
||||
virtual void onColumnClicked(int columnId) OVERRIDE;
|
||||
virtual void onColumnClicked(int column_id, bool sort_desc, bool sort_default) OVERRIDE;
|
||||
|
||||
virtual void init() OVERRIDE;
|
||||
|
||||
|