Merge with latest master

This commit is contained in:
Alayan 2018-10-05 17:55:02 +02:00
commit fc969982da
81 changed files with 2396 additions and 1786 deletions

View File

@ -27,7 +27,7 @@
<label proportion="1" align="center" text_align="right" I18N="In the multitouch settings screen" text="Buttons scale"/> <label proportion="1" align="center" text_align="right" I18N="In the multitouch settings screen" text="Buttons scale"/>
<div proportion="1" align="center" height="fit" layout="horizontal-row" > <div proportion="1" align="center" height="fit" layout="horizontal-row" >
<spacer width="40" height="10" /> <spacer width="40" height="10" />
<gauge id="scale" proportion="1" min_value="50" max_value="150"/> <gauge id="scale" proportion="1" min_value="80" max_value="160"/>
</div> </div>
</div> </div>

View File

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<stkgui> <stkgui>
<div y="2%" width="100%" height="96%" layout="vertical-row"> <div x="1%" y="2%" width="98%" height="96%" layout="vertical-row">
<div width="100%" height="40%" layout="vertical-row"> <div width="100%" height="40%" layout="vertical-row">
<div width="100%" height="25%" layout="vertical-row" > <div width="100%" height="25%" layout="vertical-row" >
<label id="name" width="100%" text_align="center"/> <label id="name" width="100%" text_align="center"/>
</div> </div>
<!-- This is filled in programmatically --> <!-- This is filled in programmatically -->
<box width="98%" height="75%" align="center" layout="vertical-row" padding="1"> <box width="100%" height="75%" align="center" layout="vertical-row" padding="1">
<list id="current_replay_info" x="0" y="0" width="100%" height="100%"/> <list id="current_replay_info" x="0" y="0" width="100%" height="100%"/>
</box> </box>
</div> </div>
@ -18,8 +18,8 @@
</div> </div>
<div width="64%" height="100%" layout="vertical-row"> <div width="64%" height="100%" layout="vertical-row">
<div width="95%" align="center" layout="vertical-row" height="fit"> <div width="95%" align="center" layout="vertical-row" height="50%">
<div width="100%" height="40" layout="horizontal-row" > <div width="100%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="record-race" I18N="Ghost replay info action" text_align="left"/> <checkbox width="fit" id="record-race" I18N="Ghost replay info action" text_align="left"/>
<spacer width="10"/> <spacer width="10"/>
<label proportion="1" id="record-race-text" height="100%" text_align="left" I18N="Ghost replay info action" text="Record the race for ghost replay"/> <label proportion="1" id="record-race-text" height="100%" text_align="left" I18N="Ghost replay info action" text="Record the race for ghost replay"/>
@ -36,7 +36,7 @@
</div> </div>
</div> </div>
<div width="95%" height="50%" align="center"> <div width="95%" height="40%" align="center">
<buttonbar id="actions" x="0" y="0" height="100%" width="100%" align="center"> <buttonbar id="actions" x="0" y="0" height="100%" width="100%" align="center">
<icon-button id="start" width="128" height="128" <icon-button id="start" width="128" height="128"
icon="gui/icons/green_check.png" icon="gui/icons/green_check.png"

View File

@ -13,50 +13,55 @@
<list id="replay_list" x="0" y="0" width="100%" height="100%"/> <list id="replay_list" x="0" y="0" width="100%" height="100%"/>
</box> </box>
<tabs id="race_mode" height="6%" max_height="110" x="2%" width="98%" align="center"> <tabs id="race_mode" height="6%" max_height="110" x="1%" width="98%" align="center">
<icon-button id="tab_time_trial" width="128" height="128" icon="gui/icons/mode_tt.png" <icon-button id="tab_time_trial" width="128" height="128" icon="gui/icons/mode_tt.png"
I18N="In the ghost replay selection screen" text="Time trial"/> I18N="In the ghost replay selection screen" text="Time trial"/>
<icon-button id="tab_egg_hunt" width="128" height="128" icon="gui/icons/mode_easter.png" <icon-button id="tab_egg_hunt" width="128" height="128" icon="gui/icons/mode_easter.png"
I18N="In the ghost replay selection screen" text="Egg hunt"/> I18N="In the ghost replay selection screen" text="Egg hunt"/>
</tabs> </tabs>
<spacer width="100%" height="1.5%" /> <spacer width="100%" height="2%" />
<div width="99%" align="center" layout="horizontal-row" height="fit"> <div width="98%" align="center" layout="horizontal-row" height="fit">
<div proportion="1" height="fit" layout="horizontal-row" > <div width="60%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="best_times_toggle" text_align="left"/> <checkbox width="fit" id="best_times_toggle" text_align="left"/>
<spacer width="2%" height="fit"/> <spacer width="2%" height="fit"/>
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show the best times"/> <label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show the best times"/>
</div> </div>
<div proportion="1" height="fit" layout="horizontal-row" > <div width="40%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="compare_toggle" text_align="left"/> <checkbox width="fit" id="compare_toggle" text_align="left"/>
<spacer width="2%" height="fit"/> <spacer width="2%" height="fit"/>
<label height="100%" id="compare-toggle-text" text_align="left" I18N="In the ghost replay selection screen" text="Compare replay"/> <label height="100%" id="compare-toggle-text" text_align="left" I18N="In the ghost replay selection screen" text="Compare replay"/>
</div> </div>
</div> </div>
<div width="99%" align="center" layout="horizontal-row" height="fit"> <div width="98%" align="center" layout="horizontal-row" height="fit">
<div proportion="2" height="fit" layout="horizontal-row" > <div width="60%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="replay_difficulty_toggle" text_align="left"/> <checkbox width="fit" id="replay_difficulty_toggle" text_align="left"/>
<spacer width="2%" height="fit"/> <spacer width="2%" height="fit"/>
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show replays matching the current difficulty"/> <label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show replays matching the current difficulty"/>
</div> </div>
<div proportion="1" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="replay_multiplayer_toggle" text_align="left"/>
<spacer width="2%" height="fit"/>
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Hide multiplayer replays"/>
</div>
</div> </div>
<div width="99%" align="center" layout="horizontal-row" height="fit"> <div width="98%" align="center" layout="horizontal-row" height="fit">
<div proportion="1" height="fit" layout="horizontal-row" > <div width="60%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="replay_version_toggle" text_align="left"/> <checkbox width="fit" id="replay_version_toggle" text_align="left"/>
<spacer width="1%" height="fit" /> <spacer width="2%" height="fit" />
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show replays matching the current version"/> <label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Only show replays matching the current version"/>
</div> </div>
</div> </div>
<button x="1%" id="record-ghost" I18N="In the ghost replay selection screen" text="Record a ghost replay"/> <div width="98%" align="center" layout="horizontal-row" height="fit">
<div width="60%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="replay_multiplayer_toggle" text_align="left"/>
<spacer width="2%" height="fit"/>
<label height="100%" text_align="left" I18N="In the ghost replay selection screen" text="Hide multiplayer replays"/>
</div>
<div width="40%" height="fit" layout="horizontal-row" >
<spacer proportion="1" height="5"/>
<button width="fit" id="record-ghost" I18N="In the ghost replay selection screen" text="Record a ghost replay"/>
</div>
</div>
</div> </div>
</stkgui> </stkgui>

View File

@ -17,7 +17,7 @@
<checkbox width="fit" id="private_server" text_align="left"/> <checkbox width="fit" id="private_server" text_align="left"/>
<spacer width="10"/> <spacer width="10"/>
<label proportion="1" height="100%" text_align="left" <label proportion="1" height="100%" text_align="left"
I18N="In the server selection screen" text="Show only private server(s)"/> I18N="In the server selection screen" text="Show private server(s)"/>
<checkbox width="fit" id="game_started" text_align="left"/> <checkbox width="fit" id="game_started" text_align="left"/>
<spacer width="10"/> <spacer width="10"/>
<label proportion="1" height="100%" text_align="left" <label proportion="1" height="100%" text_align="left"

View File

@ -67,7 +67,7 @@
The actual turn radius is piece-wise linearly interpolated. This The actual turn radius is piece-wise linearly interpolated. This
allows for tighter turning at lower speeds, and also avoids that allows for tighter turning at lower speeds, and also avoids that
the kart becomes too hard to control at high speed (speeds of the kart becomes too hard to control at high speed (speeds of
higher than 23 can only be reached with powerups). higher than 25 can only be reached with powerups).
time-full-steer: This is the amount of change in steering depending time-full-steer: This is the amount of change in steering depending
on current steering. So if the steering is between 0 and 0.5, on current steering. So if the steering is between 0 and 0.5,
the time-for-steering-change is 0.15. If the current steering is the time-for-steering-change is 0.15. If the current steering is

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
<!-- Minimum and maxium server versions that be be read by this binary. <!-- Minimum and maxium server versions that be be read by this binary.
Older versions will be ignored. --> Older versions will be ignored. -->
<server-version min="1" max="1"/> <server-version min="2" max="2"/>
<!-- Maximum number of karts to be used at the same time. This limit <!-- Maximum number of karts to be used at the same time. This limit
can easily be increased, but some tracks might not have valid start can easily be increased, but some tracks might not have valid start
@ -114,7 +114,7 @@
that the bit is to be set to 0, otherwise the bit will be set. that the bit is to be set to 0, otherwise the bit will be set.
This field takes two This field takes two
values: the first value is 'and'ed with bullet's default values values: the first value is 'and'ed with bullet's default values
(i.e. it can be used to unset bullet defaults), the second value (i.e. it can be used to unset bullet defaults), the second value
is 'or'ed (i.e. is used to set a bit). A value of -1 for 'and' is 'or'ed (i.e. is used to set a bit). A value of -1 for 'and'
means to keep all bits. The valid names are listed in stk_config.cpp means to keep all bits. The valid names are listed in stk_config.cpp
@ -125,7 +125,7 @@
smooth-angle-limit="0.65" smooth-angle-limit="0.65"
fps="120" fps="120"
default-track-friction="0.5" default-track-friction="0.5"
default-moveable-friction="0.5" default-moveable-friction="0.5"
solver-iterations="4" solver-iterations="4"
solver-split-impulse="true" solver-split-impulse="true"
solver-split-impulse-threshold="-0.00001" solver-split-impulse-threshold="-0.00001"
@ -149,14 +149,14 @@
<replay max-frames="12000" delta-t="0.200" delta-speed="0.6" <replay max-frames="12000" delta-t="0.200" delta-speed="0.6"
delta-steering="0.35" /> delta-steering="0.35" />
<!-- Determines the minimap related values. <!-- Determines the minimap related values.
size: The size of the minimap (scaled afterwards) 480 = full screen height) size: The size of the minimap (scaled afterwards) 480 = full screen height)
ai-icon: The size of the icons for the AI karts on the minimap. ai-icon: The size of the icons for the AI karts on the minimap.
player-icon: The size of the icons for the player karts. --> player-icon: The size of the icons for the player karts. -->
<minimap size="180.0" ai-icon="16.0" player-icon="20.0"/> <minimap size="180.0" ai-icon="16.0" player-icon="20.0"/>
<urls donate="https://supertuxkart.net/Donate" <urls donate="https://supertuxkart.net/Donate"
password-reset="http://addons.supertuxkart.net/password-reset.php" /> password-reset="http://addons.supertuxkart.net/password-reset.php" />
<!-- Skidmark data: maximum number of skid marks, and <!-- Skidmark data: maximum number of skid marks, and
@ -201,7 +201,7 @@
away if there is an explosion. --> away if there is an explosion. -->
<explosion impulse-objects="500.0" /> <explosion impulse-objects="500.0" />
<!-- Networking <!-- Networking
state-frequency: how many states the server will send per second. state-frequency: how many states the server will send per second.
steering-reduction: Reduce a remote kart's steering by this factor steering-reduction: Reduce a remote kart's steering by this factor
each frame. This helps reduces oversteering by high latency each frame. This helps reduces oversteering by high latency

View File

@ -133,6 +133,20 @@ namespace scene
} }
} }
virtual void recursiveUpdateAbsolutePosition()
{
if (IsVisible)
{
// update absolute position
updateAbsolutePosition();
// perform the post render process on all children
ISceneNodeList::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
(*it)->recursiveUpdateAbsolutePosition();
}
}
//! Renders the node. //! Renders the node.
virtual void render() = 0; virtual void render() = 0;

View File

@ -249,6 +249,10 @@ void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs)
LastTimeMs = timeMs; LastTimeMs = timeMs;
IAnimatedMeshSceneNode::OnAnimate(timeMs); IAnimatedMeshSceneNode::OnAnimate(timeMs);
// For up-to-date current frame bone-child attachment
for (u32 n=0;n<JointChildSceneNodes.size();++n)
JointChildSceneNodes[n]->recursiveUpdateAbsolutePosition();
} }

View File

@ -186,6 +186,9 @@ void CIrrDeviceAndroid::createVideoModeList()
int width = ANativeWindow_getWidth(Android->window); int width = ANativeWindow_getWidth(Android->window);
int height = ANativeWindow_getHeight(Android->window); int height = ANativeWindow_getHeight(Android->window);
os::Printer::log("Window width:", core::stringc(width).c_str(), ELL_DEBUG);
os::Printer::log("Window height:", core::stringc(height).c_str(), ELL_DEBUG);
if (width > 0 && height > 0) if (width > 0 && height > 0)
{ {

View File

@ -762,7 +762,7 @@ namespace UserConfigParams
&m_network_group, "Use random port for server connection " &m_network_group, "Use random port for server connection "
"(check stk_config.xml for default value)")); "(check stk_config.xml for default value)"));
PARAM_PREFIX BoolUserConfigParam m_lobby_chat PARAM_PREFIX BoolUserConfigParam m_lobby_chat
PARAM_DEFAULT(BoolUserConfigParam(false, "lobby-chat", PARAM_DEFAULT(BoolUserConfigParam(true, "lobby-chat",
&m_network_group, "Enable chatting in networking lobby, if off than " &m_network_group, "Enable chatting in networking lobby, if off than "
"no chat message will be displayed from any players.")); "no chat message will be displayed from any players."));
PARAM_PREFIX IntUserConfigParam m_max_players PARAM_PREFIX IntUserConfigParam m_max_players

View File

@ -23,6 +23,7 @@
#include <sstream> #include <sstream>
#include "config/user_config.hpp" #include "config/user_config.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/material.hpp" #include "graphics/material.hpp"
#include "graphics/particle_kind_manager.hpp" #include "graphics/particle_kind_manager.hpp"
#include "graphics/sp/sp_texture_manager.hpp" #include "graphics/sp/sp_texture_manager.hpp"
@ -57,9 +58,10 @@ MaterialManager::MaterialManager()
MaterialManager::~MaterialManager() MaterialManager::~MaterialManager()
{ {
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
SP::SPTextureManager::get()->stopThreads(); if (CVS->isGLSL())
SP::SPTextureManager::get()->stopThreads();
#endif #endif
for(unsigned int i=0; i<m_materials.size(); i++) for(unsigned int i=0; i<m_materials.size(); i++)
{ {
delete m_materials[i]; delete m_materials[i];

View File

@ -272,7 +272,9 @@ PlayerKartWidget::~PlayerKartWidget()
if (m_kart_name->getIrrlichtElement() != NULL) if (m_kart_name->getIrrlichtElement() != NULL)
m_kart_name->getIrrlichtElement()->remove(); m_kart_name->getIrrlichtElement()->remove();
getCurrentScreen()->manualRemoveWidget(this);
if (getCurrentScreen() != NULL)
getCurrentScreen()->manualRemoveWidget(this);
#ifdef DEBUG #ifdef DEBUG
m_magic_number = 0xDEADBEEF; m_magic_number = 0xDEADBEEF;

View File

@ -631,3 +631,26 @@ std::string AssetsAndroid::getDataPath()
return ""; return "";
} }
//-----------------------------------------------------------------------------
/** Get a path for internal lib directory
* \return Path for internal lib directory or empty string when failed
*/
std::string AssetsAndroid::getLibPath()
{
#ifdef ANDROID
AndroidApplicationInfo application_info =
CIrrDeviceAndroid::getApplicationInfo(global_android_app->activity);
std::string lib_path = application_info.native_lib_dir;
if (access(lib_path.c_str(), R_OK) != 0)
{
lib_path = "";
}
return lib_path;
#endif
return "";
}

View File

@ -43,6 +43,7 @@ public:
void init(); void init();
static std::string getDataPath(); static std::string getDataPath();
static std::string getLibPath();
}; };

View File

@ -110,7 +110,8 @@ Attachment::~Attachment()
*/ */
void Attachment::set(AttachmentType type, int ticks, void Attachment::set(AttachmentType type, int ticks,
AbstractKart *current_kart, AbstractKart *current_kart,
bool disable_swatter_animation) bool disable_swatter_animation,
bool set_by_rewind_parachute)
{ {
// Don't override currently player swatter removing bomb animation // Don't override currently player swatter removing bomb animation
Swatter* s = dynamic_cast<Swatter*>(m_plugin); Swatter* s = dynamic_cast<Swatter*>(m_plugin);
@ -179,7 +180,8 @@ void Attachment::set(AttachmentType type, int ticks,
// A parachute can be attached as result of the usage of an item. In this // A parachute can be attached as result of the usage of an item. In this
// case we have to save the current kart speed so that it can be detached // case we have to save the current kart speed so that it can be detached
// by slowing down. // by slowing down.
if(m_type==ATTACH_PARACHUTE) // if set by rewind the parachute ticks is already correct
if (m_type == ATTACH_PARACHUTE && !set_by_rewind_parachute)
{ {
const KartProperties *kp = m_kart->getKartProperties(); const KartProperties *kp = m_kart->getKartProperties();
float speed_mult; float speed_mult;
@ -306,19 +308,16 @@ void Attachment::rewindTo(BareNetworkString *buffer)
// Attaching an object can be expensive (loading new models, ...) // Attaching an object can be expensive (loading new models, ...)
// so avoid doing this if there is no change in attachment type // so avoid doing this if there is no change in attachment type
// Don't use set to reset a model on local player if it's already cleared if (m_type == new_type)
// (or m_initial_speed is redone / model is re-shown again when rewinding)
if (m_type == new_type || m_type == ATTACH_NOTHING)
{ {
setTicksLeft(ticks_left); setTicksLeft(ticks_left);
if (m_type != new_type && new_type != ATTACH_SWATTER)
m_type = new_type;
return; return;
} }
set(new_type, ticks_left, m_previous_owner, set(new_type, ticks_left, m_previous_owner,
new_type == ATTACH_SWATTER && !is_removing_bomb new_type == ATTACH_SWATTER && !is_removing_bomb
/*disable_swatter_animation*/); /*disable_swatter_animation*/,
new_type == ATTACH_PARACHUTE);
} // rewindTo } // rewindTo
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -353,6 +352,12 @@ void Attachment::hitBanana(ItemState *item_state)
return; return;
} }
// Make it consistent with attachment rewind when eating banana with bomb
// see if (m_type == ATTACH_BOMB && m_kart->getKartAnimation() != NULL)
// in 515
if (m_kart->getKartAnimation())
return;
AttachmentType new_attachment = ATTACH_NOTHING; AttachmentType new_attachment = ATTACH_NOTHING;
const KartProperties *kp = m_kart->getKartProperties(); const KartProperties *kp = m_kart->getKartProperties();
// Use this as a basic random number to make sync with server easier. // Use this as a basic random number to make sync with server easier.
@ -529,13 +534,19 @@ void Attachment::update(int ticks)
m_node->setVisible((division & 0x1) == 0); m_node->setVisible((division & 0x1) == 0);
} }
if(m_plugin) if (m_plugin)
{ {
bool discard = m_plugin->updateAndTestFinished(ticks); int discard_ticks = m_plugin->updateAndTestFinished(ticks);
if(discard) if (discard_ticks != -1)
{ {
clear(); // also removes the plugin // Save it for rewinding
return; m_ticks_left =
discard_ticks - World::getWorld()->getTicksSinceStart();
if (m_ticks_left <= 0)
{
clear(); // also removes the plugin
return;
}
} }
} }

View File

@ -116,7 +116,8 @@ public:
void handleCollisionWithKart(AbstractKart *other); void handleCollisionWithKart(AbstractKart *other);
void set (AttachmentType type, int ticks, void set (AttachmentType type, int ticks,
AbstractKart *previous_kart=NULL, AbstractKart *previous_kart=NULL,
bool disable_swatter_animation = false); bool disable_swatter_animation = false,
bool set_by_rewind_parachute = false);
void rewindTo(BareNetworkString *buffer); void rewindTo(BareNetworkString *buffer);
void saveState(BareNetworkString *buffer) const; void saveState(BareNetworkString *buffer) const;

View File

@ -53,8 +53,9 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Updates a plugin. This is called once each time frame. If the /** Updates a plugin. This is called once each time frame. If the
* function returns true, the attachment is discarded. */ * function returns a non-negative number, the attachment is discarded
virtual bool updateAndTestFinished(int ticks) = 0; * when world ticks >= that number. */
virtual int updateAndTestFinished(int ticks) = 0;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Called when the animation of the Attachment's node is done. */ /** Called when the animation of the Attachment's node is done. */

View File

@ -224,6 +224,10 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the type of flyable. */ /** Returns the type of flyable. */
PowerupManager::PowerupType getType() const {return m_type;} PowerupManager::PowerupType getType() const {return m_type;}
// ------------------------------------------------------------------------
/** Returns the owner's kart */
AbstractKart *getOwner() const { return m_owner;}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the owner's kart */ /** Returns the owner's kart */
AbstractKart *getOwner() const { return m_owner;} AbstractKart *getOwner() const { return m_owner;}

View File

@ -37,6 +37,25 @@
#include <ISceneManager.h> #include <ISceneManager.h>
// ----------------------------------------------------------------------------
/** Constructor.
* \param type Type of the item.
* \param owner If not NULL it is the kart that dropped this item; NULL
* indicates an item that's part of the track.
* \param id Index of this item in the array of all items.
*/
ItemState::ItemState(ItemType type, const AbstractKart *owner, int id)
{
setType(type);
m_item_id = id;
m_previous_owner = owner;
m_used_up_counter = -1;
if (owner)
setDeactivatedTicks(stk_config->time2Ticks(1.5f));
else
setDeactivatedTicks(0);
} // ItemState(ItemType)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets the disappear counter depending on type. */ /** Sets the disappear counter depending on type. */
void ItemState::setDisappearCounter() void ItemState::setDisappearCounter()
@ -52,9 +71,23 @@ void ItemState::setDisappearCounter()
} // switch } // switch
} // setDisappearCounter } // setDisappearCounter
// -----------------------------------------------------------------------
/** Initialises an item.
* \param type Type for this item.
*/
void ItemState::initItem(ItemType type, const Vec3& xyz)
{
m_xyz = xyz;
m_original_type = ITEM_NONE;
m_ticks_till_return = 0;
setDisappearCounter();
} // initItem
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Update the state of the item, called once per physics frame. /** Update the state of the item, called once per physics frame.
* \param ticks Number of ticks to simulate (typically 1). * \param ticks Number of ticks to simulate. While this value is 1 when
* called during the normal game loop, during a rewind this value
* can be (much) larger than 1.
*/ */
void ItemState::update(int ticks) void ItemState::update(int ticks)
{ {
@ -107,12 +140,17 @@ void ItemState::collected(const AbstractKart *kart)
* \param normal The normal upon which the item is placed (so that it can * \param normal The normal upon which the item is placed (so that it can
* be aligned properly with the ground). * be aligned properly with the ground).
* \param mesh The mesh to be used for this item. * \param mesh The mesh to be used for this item.
* \param owner 'Owner' of this item, i.e. the kart that drops it. This is
* used to deactivate this item for the owner, i.e. avoid that a kart
* 'collects' its own bubble gum. NULL means no owner, and the item
* can be collected immediatley by any kart.
* \param is_predicted True if the creation of the item is predicted by * \param is_predicted True if the creation of the item is predicted by
* a client. Only used in networking. * a client. Only used in networking.
*/ */
Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal, Item::Item(ItemType type, const Vec3& xyz, const Vec3& normal,
scene::IMesh* mesh, scene::IMesh* lowres_mesh, bool is_predicted) scene::IMesh* mesh, scene::IMesh* lowres_mesh,
: ItemState(type) const AbstractKart *owner, bool is_predicted)
: ItemState(type, owner)
{ {
assert(type != ITEM_TRIGGER); // use other constructor for that assert(type != ITEM_TRIGGER); // use other constructor for that
@ -190,7 +228,6 @@ Item::Item(const Vec3& xyz, float distance, TriggerItemListener* trigger)
void Item::initItem(ItemType type, const Vec3 &xyz) void Item::initItem(ItemType type, const Vec3 &xyz)
{ {
ItemState::initItem(type, xyz); ItemState::initItem(type, xyz);
m_previous_owner = NULL;
m_rotate = (getType()!=ITEM_BUBBLEGUM) && m_rotate = (getType()!=ITEM_BUBBLEGUM) &&
(getType()!=ITEM_TRIGGER ); (getType()!=ITEM_TRIGGER );
// Now determine in which quad this item is, and its distance // Now determine in which quad this item is, and its distance
@ -334,17 +371,6 @@ void Item::reset()
} }
} // reset } // reset
//-----------------------------------------------------------------------------
/** Sets which karts dropped an item. This is used to avoid that a kart is
* affected by its own items.
* \param parent Kart that dropped the item.
*/
void Item::setParent(const AbstractKart* parent)
{
m_previous_owner = parent;
ItemState::setDeactivatedTicks(stk_config->time2Ticks(1.5f));
} // setParent
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Updated the item - rotates it, takes care of items coming back into /** Updated the item - rotates it, takes care of items coming back into
* the game after it has been collected. * the game after it has been collected.

View File

@ -122,35 +122,25 @@ private:
* and then converting this value to a Vec3. */ * and then converting this value to a Vec3. */
Vec3 m_xyz; Vec3 m_xyz;
protected:
/** The 'owner' of the item, i.e. the kart that dropped this item. /** The 'owner' of the item, i.e. the kart that dropped this item.
* Is NULL if the item is part of the track. */ * Is NULL if the item is part of the track. */
const AbstractKart *m_previous_owner; const AbstractKart *m_previous_owner;
protected:
friend class ItemManager; friend class ItemManager;
friend class NetworkItemManager; friend class NetworkItemManager;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual void setType(ItemType type) { m_type = type; } virtual void setType(ItemType type) { m_type = type; }
public: public:
/** Constructor. ItemState(ItemType type, const AbstractKart *owner=NULL, int id = -1);
* \param type Type of the item. void initItem(ItemType type, const Vec3& xyz);
* \param id Index of this item in the array of all items. void update(int ticks);
* \param kart_id If !=-1 the kart that dropped this item; -1 void setDisappearCounter();
* indicates an item that's part of the track. */ virtual void collected(const AbstractKart *kart);
ItemState(ItemType type, int id=-1, AbstractKart *kart=NULL)
{
setType(type);
m_item_id = id;
m_previous_owner = kart;
} // ItemState(ItemType)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual ~ItemState() {} virtual ~ItemState() {}
void setDisappearCounter();
void update(int ticks);
virtual void collected(const AbstractKart *kart);
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
void reset() void reset()
@ -166,19 +156,6 @@ public:
} }
} // reset } // reset
// -----------------------------------------------------------------------
/** Initialises an item.
* \param type Type for this item.
*/
void initItem(ItemType type, const Vec3& xyz)
{
m_xyz = xyz;
m_original_type = ITEM_NONE;
m_deactive_ticks = 0;
m_ticks_till_return = 0;
setDisappearCounter();
} // initItem
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Switches an item to be of a different type. Used for the switch /** Switches an item to be of a different type. Used for the switch
* powerup. * powerup.
@ -329,20 +306,19 @@ private:
public: public:
Item(ItemType type, const Vec3& xyz, const Vec3& normal, Item(ItemType type, const Vec3& xyz, const Vec3& normal,
scene::IMesh* mesh, scene::IMesh* lowres_mesh, scene::IMesh* mesh, scene::IMesh* lowres_mesh,
const AbstractKart *owner,
bool is_predicted=false); bool is_predicted=false);
Item(const Vec3& xyz, float distance, Item(const Vec3& xyz, float distance,
TriggerItemListener* trigger); TriggerItemListener* trigger);
virtual ~Item (); virtual ~Item ();
void updateGraphics(float dt); void updateGraphics(float dt);
virtual void collected(const AbstractKart *kart) OVERRIDE; virtual void collected(const AbstractKart *kart) OVERRIDE;
void setParent(const AbstractKart* parent);
void reset(); void reset();
void switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh); void switchTo(ItemType type, scene::IMesh *mesh,
scene::IMesh *lowmesh);
void switchBack(); void switchBack();
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------
/** Returns true if the Kart is close enough to hit this item, the item is /** Returns true if the Kart is close enough to hit this item, the item is
* not deactivated anymore, and it wasn't placed by this kart (this is * not deactivated anymore, and it wasn't placed by this kart (this is
* e.g. used to avoid that a kart hits a bubble gum it just dropped). * e.g. used to avoid that a kart hits a bubble gum it just dropped).
@ -352,7 +328,7 @@ public:
*/ */
bool hitKart(const Vec3 &xyz, const AbstractKart *kart=NULL) const bool hitKart(const Vec3 &xyz, const AbstractKart *kart=NULL) const
{ {
if (m_previous_owner == kart && getDeactivatedTicks() > 0) if (getPreviousOwner() == kart && getDeactivatedTicks() > 0)
return false; return false;
Vec3 lc = quatRotate(m_original_rotation, xyz - getXYZ()); Vec3 lc = quatRotate(m_original_rotation, xyz - getXYZ());
// Don't be too strict if the kart is a bit above the item // Don't be too strict if the kart is a bit above the item
@ -373,7 +349,8 @@ protected:
bool hitLine(const core::line3df &line, bool hitLine(const core::line3df &line,
const AbstractKart *kart=NULL) const const AbstractKart *kart=NULL) const
{ {
if (m_previous_owner == kart && getDeactivatedTicks() >0) return false; if (getPreviousOwner() == kart && getDeactivatedTicks() > 0)
return false;
Vec3 closest = line.getClosestPoint(getXYZ().toIrrVector()); Vec3 closest = line.getClosestPoint(getXYZ().toIrrVector());
return hitKart(closest, kart); return hitKart(closest, kart);

View File

@ -31,6 +31,7 @@
*/ */
ItemEventInfo::ItemEventInfo(BareNetworkString *buffer, int *count) ItemEventInfo::ItemEventInfo(BareNetworkString *buffer, int *count)
{ {
m_ticks_till_return = 0;
m_type = (EventType)buffer->getUInt8(); m_type = (EventType)buffer->getUInt8();
m_ticks = buffer->getTime(); m_ticks = buffer->getTime();
m_kart_id = buffer->getInt8(); m_kart_id = buffer->getInt8();
@ -41,7 +42,11 @@ ItemEventInfo::ItemEventInfo(BareNetworkString *buffer, int *count)
m_xyz = buffer->getVec3(); m_xyz = buffer->getVec3();
*count -= 12; *count -= 12;
} }
else if (m_type == IEI_COLLECT)
{
m_ticks_till_return = buffer->getUInt16();
*count -= 2;
}
} // ItemEventInfo(BareNetworkString, int *count) } // ItemEventInfo(BareNetworkString, int *count)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -55,7 +60,6 @@ void ItemEventInfo::saveState(BareNetworkString *buffer)
.addUInt16(m_index); .addUInt16(m_index);
if(m_type == IEI_NEW) if(m_type == IEI_NEW)
buffer->add(m_xyz); buffer->add(m_xyz);
else if (m_type == IEI_COLLECT)
buffer->addUInt16(m_ticks_till_return);
} // saveState } // saveState

View File

@ -21,6 +21,7 @@
#include "items/item.hpp" #include "items/item.hpp"
#include "utils/vec3.hpp" #include "utils/vec3.hpp"
#include "utils/types.hpp"
#include <assert.h> #include <assert.h>
@ -51,13 +52,20 @@ private:
/** In case of new items the position of the new item. */ /** In case of new items the position of the new item. */
Vec3 m_xyz; Vec3 m_xyz;
/** Ticks for the item to return, atm used by collecting banana
* with bomb to delay the return for banana. */
int16_t m_ticks_till_return;
public: public:
/** Constructor for collecting an existing item. /** Constructor for collecting an existing item.
* \param ticks Time of the event. * \param ticks Time of the event.
* \param item_id The index of the item that was collected. * \param item_id The index of the item that was collected.
* \param kart_id the kart that collected the item. */ * \param kart_id the kart that collected the item.
ItemEventInfo(int ticks, int index, int kart_id) * \param ttr Ticks till return after being collected. */
: m_ticks(ticks), m_index(index), m_kart_id(kart_id)
ItemEventInfo(int ticks, int index, int kart_id, int16_t ttr)
: m_ticks(ticks), m_index(index), m_kart_id(kart_id),
m_ticks_till_return(ttr)
{ {
m_type = IEI_COLLECT; m_type = IEI_COLLECT;
} // ItemEventInfo(collected existing item) } // ItemEventInfo(collected existing item)
@ -69,14 +77,15 @@ public:
*/ */
ItemEventInfo(int ticks, ItemState::ItemType type, int index, ItemEventInfo(int ticks, ItemState::ItemType type, int index,
int kart_id, const Vec3 &xyz) int kart_id, const Vec3 &xyz)
: m_ticks(ticks), m_index(index), m_kart_id(kart_id), m_xyz(xyz) : m_ticks(ticks), m_index(index), m_kart_id(kart_id), m_xyz(xyz),
m_ticks_till_return(0)
{ {
m_type = IEI_NEW; m_type = IEI_NEW;
} // ItemEventInfo(new item) } // ItemEventInfo(new item)
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/** Constructor for switching items. */ /** Constructor for switching items. */
ItemEventInfo(int ticks) : m_ticks(ticks) ItemEventInfo(int ticks) : m_ticks(ticks), m_ticks_till_return(0)
{ {
m_type = IEI_SWITCH; m_type = IEI_SWITCH;
} // ItemEventInfo(switch) } // ItemEventInfo(switch)
@ -116,6 +125,9 @@ public:
return m_xyz; return m_xyz;
} // getXYZ } // getXYZ
// -------------------------------------------------------------------- // --------------------------------------------------------------------
/** Returns the ticks till return, used only by collection events. */
int getTicksTillReturn() const { return m_ticks_till_return; }
// --------------------------------------------------------------------
/** Returns the type of this item. Note at this stage only bubble gums /** Returns the type of this item. Note at this stage only bubble gums
* can be created during a race. */ * can be created during a race. */
ItemState::ItemType getNewItemType() const ItemState::ItemType getNewItemType() const

View File

@ -226,13 +226,15 @@ unsigned int ItemManager::insertItem(Item *item)
// previously deleted entry, otherwise at the end. // previously deleted entry, otherwise at the end.
int index = -1; int index = -1;
for(index=(int)m_all_items.size()-1; index>=0 && m_all_items[index]; index--) {} for(index=(int)m_all_items.size()-1; index>=0 && m_all_items[index]; index--) {}
if (index == -1)
if(index==-1) index = (int)m_all_items.size(); {
index = (int)m_all_items.size();
if(index<(int)m_all_items.size())
m_all_items[index] = item;
else
m_all_items.push_back(item); m_all_items.push_back(item);
}
else
{
m_all_items[index] = item;
}
item->setItemId(index); item->setItemId(index);
// Now insert into the appropriate quad list, if there is a quad list // Now insert into the appropriate quad list, if there is a quad list
@ -286,9 +288,7 @@ Item* ItemManager::dropNewItem(ItemState::ItemType type,
} }
Item* item = new Item(type, pos, normal, m_item_mesh[mesh_type], Item* item = new Item(type, pos, normal, m_item_mesh[mesh_type],
m_item_lowres_mesh[mesh_type]); m_item_lowres_mesh[mesh_type], /*prev_owner*/kart);
if(kart != NULL) item->setParent(kart);
insertItem(item); insertItem(item);
if(m_switch_ticks>=0) if(m_switch_ticks>=0)
{ {
@ -318,7 +318,7 @@ Item* ItemManager::placeItem(ItemState::ItemType type, const Vec3& xyz,
ItemState::ItemType mesh_type = type; ItemState::ItemType mesh_type = type;
Item* item = new Item(type, xyz, normal, m_item_mesh[mesh_type], Item* item = new Item(type, xyz, normal, m_item_mesh[mesh_type],
m_item_lowres_mesh[mesh_type]); m_item_lowres_mesh[mesh_type], /*prev_owner*/NULL);
insertItem(item); insertItem(item);
if (m_switch_ticks >= 0) if (m_switch_ticks >= 0)
@ -354,7 +354,7 @@ Item* ItemManager::placeTrigger(const Vec3& xyz, float distance,
* \param item The item that was collected. * \param item The item that was collected.
* \param kart The kart that collected the item. * \param kart The kart that collected the item.
*/ */
void ItemManager::collectedItem(Item *item, AbstractKart *kart) void ItemManager::collectedItem(ItemState *item, AbstractKart *kart)
{ {
assert(item); assert(item);
// Spare tire karts don't collect items // Spare tire karts don't collect items

View File

@ -133,7 +133,7 @@ public:
void updateGraphics (float dt); void updateGraphics (float dt);
void checkItemHit (AbstractKart* kart); void checkItemHit (AbstractKart* kart);
void reset (); void reset ();
virtual void collectedItem (Item *item, AbstractKart *kart); virtual void collectedItem (ItemState *item, AbstractKart *kart);
void switchItems (); void switchItems ();
bool areItemSwitched() { return (m_switch_ticks > 0); } bool areItemSwitched() { return (m_switch_ticks > 0); }
bool randomItemsForArena(const AlignedArray<btTransform>& pos); bool randomItemsForArena(const AlignedArray<btTransform>& pos);

View File

@ -95,17 +95,18 @@ void NetworkItemManager::initClientConfirmState()
* \param item The item that was collected. * \param item The item that was collected.
* \param kart The kart that collected the item. * \param kart The kart that collected the item.
*/ */
void NetworkItemManager::collectedItem(Item *item, AbstractKart *kart) void NetworkItemManager::collectedItem(ItemState *item, AbstractKart *kart)
{ {
if(NetworkConfig::get()->isServer()) if(NetworkConfig::get()->isServer())
{ {
ItemManager::collectedItem(item, kart);
// The server saves the collected item as item event info // The server saves the collected item as item event info
m_item_events.lock(); m_item_events.lock();
m_item_events.getData().emplace_back(World::getWorld()->getTicksSinceStart(), m_item_events.getData().emplace_back(World::getWorld()->getTicksSinceStart(),
item->getItemId(), item->getItemId(),
kart->getWorldKartId()); kart->getWorldKartId(),
item->getTicksTillReturn());
m_item_events.unlock(); m_item_events.unlock();
ItemManager::collectedItem(item, kart);
} }
else else
{ {
@ -142,7 +143,7 @@ Item* NetworkItemManager::dropNewItem(ItemState::ItemType type,
kart->getXYZ() ); kart->getXYZ() );
m_item_events.unlock(); m_item_events.unlock();
return item; return item;
} // newItem } // dropNewItem
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Called by the GameProtocol when a confirmation for an item event is /** Called by the GameProtocol when a confirmation for an item event is
@ -237,9 +238,9 @@ void NetworkItemManager::forwardTime(int ticks)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Restores the state of the items to the current world time. It takes the /** Restores the state of the items to the current world time. It takes the
* last saved * last saved confirmed state, applies any updates from the server, and
* then syncs up the confirmed state to the in-race items.
* using exactly 'count' bytes of the message. * It uses exactly 'count' bytes of the message.
* \param buffer the state content. * \param buffer the state content.
* \param count Number of bytes used for this state. * \param count Number of bytes used for this state.
*/ */
@ -267,6 +268,7 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
// From here the replay can happen. // From here the replay can happen.
// 1) Remove predicted items: // 1) Remove predicted items:
// --------------------------
for (unsigned int i=0; i<m_all_items.size(); i++) for (unsigned int i=0; i<m_all_items.size(); i++)
{ {
Item *item = m_all_items[i]; Item *item = m_all_items[i];
@ -274,20 +276,22 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
{ {
deleteItem(item); deleteItem(item);
} }
} } // for i in m_all_items
// 2) Apply all events to current confirmed state: // 2) Apply all events to current confirmed state:
// -----------------------------------------------
World *world = World::getWorld();
int current_time = m_confirmed_state_time; int current_time = m_confirmed_state_time;
bool has_state = count > 0; bool has_state = count > 0;
while(count > 0) while(count > 0)
{ {
// 1) Decode the event in the message // 2.1) Decode the event in the message
// ---------------------------------- // ------------------------------------
ItemEventInfo iei(buffer, &count); ItemEventInfo iei(buffer, &count);
// 2) If the event needs to be applied, forward // 2.2) If the event needs to be applied, forward
// the time to the time of this event: // the time to the time of this event:
// -------------------------------------------- // ----------------------------------------------
int dt = iei.getTicks() - current_time; int dt = iei.getTicks() - current_time;
// Skip an event that are 'in the past' (i.e. have been sent again by // Skip an event that are 'in the past' (i.e. have been sent again by
// the server because it has not yet received confirmation from all // the server because it has not yet received confirmation from all
@ -297,13 +301,35 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
// Forward the saved state: // Forward the saved state:
if (dt>0) forwardTime(dt); if (dt>0) forwardTime(dt);
// TODO: apply the various events types, atm only collection is supported: // TODO: apply the various events types, atm only collection
// and new items are supported.
if(iei.isItemCollection()) if(iei.isItemCollection())
{ {
int index = iei.getIndex(); int index = iei.getIndex();
// An item on the track was collected: // An item on the track was collected:
AbstractKart *kart = World::getWorld()->getKart(iei.getKartId()); AbstractKart *kart = world->getKart(iei.getKartId());
m_confirmed_state[index]->collected(kart);
// The world clock was set by the RewindManager to be the time
// of the state we are rewinding to. But this confirmed collection
// event happened in the past (we are replaying item events since
// the last confirmed state in order to get a new confirmed state).
// So we need to reset the clock to the time at which this event
// happened so that (e.g.) kart can use the right time (for
// example, bubble gum torque depends on time, and would be wrong
// otherwise resulting in stuttering).
int old_time = world->getTicksSinceStart(); // Save time we rewind to
world->setTicksForRewind(iei.getTicks()); // Set time of event
if (m_confirmed_state[index] != NULL)
{
m_confirmed_state[index]->collected(kart);// Collect item
// Reset till ticks return from state (required for eating banana with bomb)
int ttr = iei.getTicksTillReturn();
m_confirmed_state[index]->setTicksTillReturn(ttr);
}
world->setTicksForRewind(old_time); // Set time to rewind-to
if (m_confirmed_state[index]->isUsedUp()) if (m_confirmed_state[index]->isUsedUp())
{ {
delete m_confirmed_state[index]; delete m_confirmed_state[index];
@ -312,9 +338,9 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
} }
else if(iei.isNewItem()) else if(iei.isNewItem())
{ {
AbstractKart *kart = World::getWorld()->getKart(iei.getKartId()); AbstractKart *kart = world->getKart(iei.getKartId());
ItemState *is = new ItemState(iei.getNewItemType(), iei.getIndex(), ItemState *is = new ItemState(iei.getNewItemType(), kart,
kart); iei.getIndex() );
is->initItem(iei.getNewItemType(), iei.getXYZ()); is->initItem(iei.getNewItemType(), iei.getXYZ());
if (m_confirmed_state.size() <= is->getItemId()) if (m_confirmed_state.size() <= is->getItemId())
{ {
@ -325,29 +351,34 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
if (m_confirmed_state[is->getItemId()] == NULL) if (m_confirmed_state[is->getItemId()] == NULL)
m_confirmed_state[is->getItemId()] = is; m_confirmed_state[is->getItemId()] = is;
else else
{
*m_confirmed_state[is->getItemId()] = *is; *m_confirmed_state[is->getItemId()] = *is;
delete is;
}
} }
} }
current_time = iei.getTicks(); current_time = iei.getTicks();
} // while count >0 } // while count >0
// Inform the server which events have been received. // Inform the server which events have been received (if there has
// been any updates - no need to send messages if nothing has changed)
if (has_state) if (has_state)
{ {
if (auto gp = GameProtocol::lock()) if (auto gp = GameProtocol::lock())
gp->sendItemEventConfirmation(World::getWorld()->getTicksSinceStart()); gp->sendItemEventConfirmation(world->getTicksSinceStart());
} }
// Forward the confirmed item state to the world time: // Forward the confirmed item state to the world time:
int dt = World::getWorld()->getTicksSinceStart() - current_time; int dt = world->getTicksSinceStart() - current_time;
if(dt>0) forwardTime(dt); if(dt>0) forwardTime(dt);
// Restore the state to the current world time: // 3. Restore the state to the current world time:
// ============================================ // ===============================================
for(unsigned int i=0; i<m_confirmed_state.size(); i++) for(unsigned int i=0; i<m_confirmed_state.size(); i++)
{ {
Item *item = m_all_items[i]; Item *item = i < m_all_items.size() ? m_all_items[i] : NULL;
const ItemState *is = m_confirmed_state[i]; const ItemState *is = m_confirmed_state[i];
if (is && item) if (is && item)
*(ItemState*)item = *is; *(ItemState*)item = *is;
@ -356,18 +387,33 @@ void NetworkItemManager::restoreState(BareNetworkString *buffer, int count)
Vec3 xyz = is->getXYZ(); Vec3 xyz = is->getXYZ();
Item *item_new = dropNewItem(is->getType(), is->getPreviousOwner(), Item *item_new = dropNewItem(is->getType(), is->getPreviousOwner(),
&xyz); &xyz);
if (i != item_new->getItemId())
{
// The server has this item at a different index in the list
// of all items, which means the client has an incorrect
// item at the index given by the server - delete that item
Log::info("nim", "about to delete item with not matching index i %d item %d",
i, item_new->getItemId());
if(m_all_items[i])
deleteItem(m_all_items[i]);
m_all_items[item_new->getItemId()] = NULL;
m_all_items[i] = item_new;
item_new->setItemId(i);
}
item_new->setPredicted(false); item_new->setPredicted(false);
item_new->setItemId(i); item_new->setDeactivatedTicks(is->getDeactivatedTicks());
m_all_items[i] = item_new;
*((ItemState*)m_all_items[i]) = *is; *((ItemState*)m_all_items[i]) = *is;
} }
else if (!is && item) else if (!is && item)
{ {
Log::info("nim", "About to delete item index %d i %d",
item->getItemId(), i);
deleteItem(m_all_items[i]); deleteItem(m_all_items[i]);
} }
} }
// Now we save the current local // Now we save the current local
m_confirmed_state_time = World::getWorld()->getTicksSinceStart(); m_confirmed_state_time = world->getTicksSinceStart();
} // restoreState } // restoreState

View File

@ -73,7 +73,7 @@ public:
virtual void reset() OVERRIDE; virtual void reset() OVERRIDE;
virtual void setItemConfirmationTime(std::weak_ptr<STKPeer> peer, virtual void setItemConfirmationTime(std::weak_ptr<STKPeer> peer,
int ticks) OVERRIDE; int ticks) OVERRIDE;
virtual void collectedItem(Item *item, AbstractKart *kart) OVERRIDE; virtual void collectedItem(ItemState *item, AbstractKart *kart) OVERRIDE;
virtual Item* dropNewItem(ItemState::ItemType type, const AbstractKart *kart, virtual Item* dropNewItem(ItemState::ItemType type, const AbstractKart *kart,
const Vec3 *xyz=NULL) OVERRIDE; const Vec3 *xyz=NULL) OVERRIDE;
virtual BareNetworkString* saveState(std::vector<std::string>* ru) virtual BareNetworkString* saveState(std::vector<std::string>* ru)

View File

@ -256,7 +256,7 @@ BareNetworkString* Plunger::saveState(std::vector<std::string>* ru)
BareNetworkString* buffer = Flyable::saveState(ru); BareNetworkString* buffer = Flyable::saveState(ru);
buffer->addUInt16(m_keep_alive).addUInt8(m_moved_to_infinity ? 1 : 0); buffer->addUInt16(m_keep_alive).addUInt8(m_moved_to_infinity ? 1 : 0);
if (m_rubber_band) if (m_rubber_band)
buffer->addUInt8(m_rubber_band->getRubberBandTo()); buffer->addUInt8(m_rubber_band->get8BitState());
else else
buffer->addUInt8(255); buffer->addUInt8(255);
return buffer; return buffer;
@ -268,7 +268,20 @@ void Plunger::restoreState(BareNetworkString *buffer, int count)
Flyable::restoreState(buffer, count); Flyable::restoreState(buffer, count);
m_keep_alive = buffer->getUInt16(); m_keep_alive = buffer->getUInt16();
m_moved_to_infinity = buffer->getUInt8() == 1; m_moved_to_infinity = buffer->getUInt8() == 1;
int8_t rbt = buffer->getUInt8(); uint8_t bit_state = buffer->getUInt8();
if (rbt != -1 && m_rubber_band) if (bit_state == 255 && m_rubber_band)
m_rubber_band->setRubberBandTo((RubberBand::RubberBandTo)rbt); {
delete m_rubber_band;
m_rubber_band = NULL;
if (!m_reverse_mode)
m_reverse_mode = true;
}
else if (bit_state != 255 && !m_rubber_band)
{
m_rubber_band = new RubberBand(this, m_owner);
if (m_reverse_mode)
m_reverse_mode = false;
}
if (bit_state != 255)
m_rubber_band->set8BitState(bit_state);
} // restoreState } // restoreState

View File

@ -563,7 +563,7 @@ void Powerup::hitBonusBox(const ItemState &item_state)
random_number); random_number);
// FIXME Disable switch and bubblegum for now in network // FIXME Disable switch and bubblegum for now in network
if (NetworkConfig::get()->isNetworking() && if (NetworkConfig::get()->isNetworking() &&
(new_powerup == PowerupManager::POWERUP_BUBBLEGUM || (new_powerup == PowerupManager::POWERUP_BUBBLEGUM ||
new_powerup == PowerupManager::POWERUP_SWITCH)) new_powerup == PowerupManager::POWERUP_SWITCH))
new_powerup = PowerupManager::POWERUP_BOWLING; new_powerup = PowerupManager::POWERUP_BOWLING;

View File

@ -314,3 +314,4 @@ std::shared_ptr<Rewinder>
return nullptr; return nullptr;
} }
} // addProjectileFromNetworkState } // addProjectileFromNetworkState

View File

@ -101,3 +101,4 @@ extern ProjectileManager *projectile_manager;
#endif #endif
/* EOF */ /* EOF */

View File

@ -45,6 +45,7 @@
RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart) RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart)
: m_plunger(plunger), m_owner(kart) : m_plunger(plunger), m_owner(kart)
{ {
m_hit_kart = NULL;
m_attached_state = RB_TO_PLUNGER; m_attached_state = RB_TO_PLUNGER;
updatePosition(); updatePosition();
@ -276,6 +277,7 @@ void RubberBand::hit(AbstractKart *kart_hit, const Vec3 *track_xyz)
// ================= // =================
m_hit_position = *track_xyz; m_hit_position = *track_xyz;
m_attached_state = RB_TO_TRACK; m_attached_state = RB_TO_TRACK;
m_hit_kart = NULL;
} // hit } // hit
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -289,3 +291,24 @@ void RubberBand::remove()
} }
#endif #endif
} // remove } // remove
// ----------------------------------------------------------------------------
uint8_t RubberBand::get8BitState() const
{
uint8_t state = (uint8_t)(m_attached_state & 3);
state |= m_attached_state == RB_TO_KART && m_hit_kart ?
(m_hit_kart->getWorldKartId() << 3) : 0;
return state;
} // get8BitState
// ----------------------------------------------------------------------------
void RubberBand::set8BitState(uint8_t bit_state)
{
m_hit_kart = NULL;
m_attached_state = (RubberBandTo)(bit_state & 3);
if (m_attached_state == RB_TO_KART)
{
unsigned kart = bit_state >> 3;
m_hit_kart = World::getWorld()->getKart(kart);
}
} // set8BitState

View File

@ -73,8 +73,8 @@ public:
void updateGraphics(float dt); void updateGraphics(float dt);
void update(int ticks); void update(int ticks);
void hit(AbstractKart *kart_hit, const Vec3 *track_xyz=NULL); void hit(AbstractKart *kart_hit, const Vec3 *track_xyz=NULL);
RubberBandTo getRubberBandTo() const { return m_attached_state; } uint8_t get8BitState() const;
void setRubberBandTo(RubberBandTo rbt) { m_attached_state = rbt; } void set8BitState(uint8_t bit_state);
void remove(); void remove();
}; // RubberBand }; // RubberBand
#endif #endif

View File

@ -161,20 +161,20 @@ void Swatter::updateGrahpics(float dt)
/** Updates an armed swatter: it checks for any karts that are close enough /** Updates an armed swatter: it checks for any karts that are close enough
* and not invulnerable, it swats the kart. * and not invulnerable, it swats the kart.
* \param dt Time step size. * \param dt Time step size.
* \return True if the attachment should be discarded. * \return World ticks to discard the swatter.
*/ */
bool Swatter::updateAndTestFinished(int ticks) int Swatter::updateAndTestFinished(int ticks)
{ {
const int ticks_start = World::getWorld()->getTicksSinceStart(); const int ticks_start = World::getWorld()->getTicksSinceStart();
if (m_removed_bomb_ticks != std::numeric_limits<int>::max()) if (m_removed_bomb_ticks != std::numeric_limits<int>::max())
{ {
if (ticks_start >= m_removed_bomb_ticks) if (ticks_start >= m_removed_bomb_ticks)
return true; return m_removed_bomb_ticks;
return false; return -1;
} // if removing bomb } // if removing bomb
if (RewindManager::get()->isRewinding()) if (RewindManager::get()->isRewinding())
return false; return -1;
if (!m_discard_now) if (!m_discard_now)
{ {
@ -186,7 +186,7 @@ bool Swatter::updateAndTestFinished(int ticks)
// to make sure all clients know the existence of swatter each other // to make sure all clients know the existence of swatter each other
if (ticks_start - m_swatter_start_ticks < 60 || if (ticks_start - m_swatter_start_ticks < 60 ||
m_swatter_end_ticks - ticks_start < 60) m_swatter_end_ticks - ticks_start < 60)
return false; return -1;
chooseTarget(); chooseTarget();
pointToTarget(); pointToTarget();
@ -258,15 +258,15 @@ bool Swatter::updateAndTestFinished(int ticks)
if (m_discard_now) if (m_discard_now)
{ {
return ticks_start > m_end_swat_ticks; return m_end_swat_ticks;
} }
else if (ticks_start > m_end_swat_ticks) else if (ticks_start > m_end_swat_ticks)
{ {
m_animation_phase = SWATTER_AIMING; m_animation_phase = SWATTER_AIMING;
m_end_swat_ticks = std::numeric_limits<int>::max(); m_end_swat_ticks = std::numeric_limits<int>::max();
return false; return -1;
} }
return false; return -1;
} // updateAndTestFinished } // updateAndTestFinished
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -87,7 +87,7 @@ public:
scene::ISceneNode* bomb_scene_node, int ticks); scene::ISceneNode* bomb_scene_node, int ticks);
virtual ~Swatter(); virtual ~Swatter();
void updateGrahpics(float dt) OVERRIDE; void updateGrahpics(float dt) OVERRIDE;
bool updateAndTestFinished(int ticks) OVERRIDE; int updateAndTestFinished(int ticks) OVERRIDE;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns if the swatter is currently aiming, i.e. can be used to /** Returns if the swatter is currently aiming, i.e. can be used to

View File

@ -261,6 +261,8 @@ public:
/** Returns true if the kart has a plunger attached to its face. */ /** Returns true if the kart has a plunger attached to its face. */
virtual int getBlockedByPlungerTicks() const = 0; virtual int getBlockedByPlungerTicks() const = 0;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual float getGraphicalViewBlockedByPlunger() const = 0;
// ------------------------------------------------------------------------
/** Sets that the view is blocked by a plunger. The duration depends on /** Sets that the view is blocked by a plunger. The duration depends on
* the difficulty, see KartPorperties getPlungerInFaceTime. */ * the difficulty, see KartPorperties getPlungerInFaceTime. */
virtual void blockViewWithPlunger() = 0; virtual void blockViewWithPlunger() = 0;

View File

@ -143,9 +143,19 @@ void AbstractKartAnimation::addNetworkAnimationChecker(bool reset_powerup)
{ {
// Prevent access to deleted kart animation object // Prevent access to deleted kart animation object
std::weak_ptr<int> cct = m_check_created_ticks; std::weak_ptr<int> cct = m_check_created_ticks;
Vec3 original_position;
AbstractKart* k = m_kart;
if (k)
original_position = k->getXYZ();
RewindManager::get()->addRewindInfoEventFunction(new RewindManager::get()->addRewindInfoEventFunction(new
RewindInfoEventFunction(m_created_ticks, RewindInfoEventFunction(m_created_ticks,
[](){}, /*undo_function*/[cct, k, original_position]()
{
auto cct_sp = cct.lock();
if (!cct_sp || !k)
return;
k->setXYZ(original_position);
},
/*replay_function*/[p]() /*replay_function*/[p]()
{ {
if (p) if (p)

View File

@ -30,6 +30,14 @@
class AbstractKart; class AbstractKart;
enum KartAnimationType : uint8_t
{
KAT_RESCUE = 0,
KAT_EXPLOSION_DIRECT_HIT = 1,
KAT_EXPLOSION = 2,
KAT_CANNON = 3
};
/** The base class for all kart animation, like rescue, explosion, or cannon. /** The base class for all kart animation, like rescue, explosion, or cannon.
* Kart animations are done by removing the physics body from the physics * Kart animations are done by removing the physics body from the physics
* world, and instead modifying the rotation and position of the kart * world, and instead modifying the rotation and position of the kart
@ -94,10 +102,12 @@ public:
m_end_transform = t; m_end_transform = t;
m_end_ticks = ticks; m_end_ticks = ticks;
} }
// ---------------------------------------------------------------------------- // ------------------------------------------------------------------------
void checkNetworkAnimationCreationSucceed(const btTransform& fallback_trans); void checkNetworkAnimationCreationSucceed(const btTransform& fb_trans);
// ---------------------------------------------------------------------------- // ------------------------------------------------------------------------
int getEndTicks() const { return m_end_ticks; } int getEndTicks() const { return m_end_ticks; }
// ------------------------------------------------------------------------
virtual KartAnimationType getAnimationType() const = 0;
}; // AbstractKartAnimation }; // AbstractKartAnimation
#endif #endif

View File

@ -81,6 +81,8 @@ public:
virtual ~CannonAnimation(); virtual ~CannonAnimation();
virtual void update(int ticks); virtual void update(int ticks);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual bool usePredefinedEndTransform() const { return false; } virtual bool usePredefinedEndTransform() const { return false; }
// ------------------------------------------------------------------------
virtual KartAnimationType getAnimationType() const { return KAT_CANNON; }
}; // CannonAnimation }; // CannonAnimation
#endif #endif

View File

@ -101,6 +101,8 @@ void NetworkAIController::convertAIToPlayerActions()
0 : 32768); 0 : 32768);
all_actions.emplace_back(PA_RESCUE, all_actions.emplace_back(PA_RESCUE,
m_ai_controls->getRescue() ? 32768 : 0); m_ai_controls->getRescue() ? 32768 : 0);
all_actions.emplace_back(PA_LOOK_BACK,
m_ai_controls->getLookBack() ? 32768 : 0);
for (const auto& a : all_actions) for (const auto& a : all_actions)
{ {

View File

@ -72,7 +72,7 @@ ExplosionAnimation *ExplosionAnimation::create(AbstractKart *kart)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ExplosionAnimation::ExplosionAnimation(AbstractKart *kart, ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
const Vec3 &explosion_position, const Vec3 &explosion_position,
bool direct_hit) bool direct_hit, bool from_state)
: AbstractKartAnimation(kart, "ExplosionAnimation") : AbstractKartAnimation(kart, "ExplosionAnimation")
{ {
m_direct_hit = direct_hit; m_direct_hit = direct_hit;
@ -148,7 +148,8 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
m_kart->getAttachment()->clear(); m_kart->getAttachment()->clear();
// Clear powerups when direct hit in CTF // Clear powerups when direct hit in CTF
addNetworkAnimationChecker(m_reset_ticks != -1); if (!from_state)
addNetworkAnimationChecker(m_reset_ticks != -1);
} // ExplosionAnimation } // ExplosionAnimation
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -74,9 +74,9 @@ protected:
Vec3 m_reset_xyz, m_reset_normal; Vec3 m_reset_xyz, m_reset_normal;
ExplosionAnimation(AbstractKart *kart); ExplosionAnimation(AbstractKart *kart);
ExplosionAnimation(AbstractKart *kart, const Vec3 &pos,
bool direct_hit);
public: public:
ExplosionAnimation(AbstractKart *kart, const Vec3 &pos,
bool direct_hit, bool from_state = false);
static ExplosionAnimation *create(AbstractKart *kart, const Vec3 &pos, static ExplosionAnimation *create(AbstractKart *kart, const Vec3 &pos,
bool direct_hit); bool direct_hit);
static ExplosionAnimation *create(AbstractKart *kart); static ExplosionAnimation *create(AbstractKart *kart);
@ -85,5 +85,8 @@ public:
virtual void update(int ticks); virtual void update(int ticks);
bool hasResetAlready() const bool hasResetAlready() const
{ return m_reset_ticks != -1 && m_timer < m_reset_ticks; } { return m_reset_ticks != -1 && m_timer < m_reset_ticks; }
// ------------------------------------------------------------------------
virtual KartAnimationType getAnimationType() const
{ return m_direct_hit ? KAT_EXPLOSION_DIRECT_HIT : KAT_EXPLOSION; }
}; // ExplosionAnimation }; // ExplosionAnimation
#endif #endif

View File

@ -185,7 +185,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id,
m_terrain_sound = NULL; m_terrain_sound = NULL;
m_last_sound_material = NULL; m_last_sound_material = NULL;
m_previous_terrain_sound = NULL; m_previous_terrain_sound = NULL;
m_graphical_view_blocked_by_plunger = 0.0f;
} // Kart } // Kart
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -370,6 +370,7 @@ void Kart::reset()
m_current_lean = 0.0f; m_current_lean = 0.0f;
m_falling_time = 0.0f; m_falling_time = 0.0f;
m_view_blocked_by_plunger = 0; m_view_blocked_by_plunger = 0;
m_graphical_view_blocked_by_plunger = 0.0f;
m_has_caught_nolok_bubblegum = false; m_has_caught_nolok_bubblegum = false;
m_is_jumping = false; m_is_jumping = false;
m_flying = false; m_flying = false;
@ -590,8 +591,15 @@ void Kart::blockViewWithPlunger()
{ {
// Avoid that a plunger extends the plunger time // Avoid that a plunger extends the plunger time
if(m_view_blocked_by_plunger<=0 && !isShielded()) if(m_view_blocked_by_plunger<=0 && !isShielded())
{
m_view_blocked_by_plunger = m_view_blocked_by_plunger =
stk_config->time2Ticks(m_kart_properties->getPlungerInFaceTime()); stk_config->time2Ticks(m_kart_properties->getPlungerInFaceTime());
if (m_graphical_view_blocked_by_plunger == 0.0f)
{
m_graphical_view_blocked_by_plunger =
m_kart_properties->getPlungerInFaceTime();
}
}
if(isShielded()) if(isShielded())
{ {
decreaseShieldTime(); decreaseShieldTime();
@ -875,22 +883,27 @@ float Kart::getSpeedForTurnRadius(float radius) const
InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius(); InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius();
// Convert the turn radius into turn angle // Convert the turn radius into turn angle
for(int i = 0; i < (int)turn_angle_at_speed.size(); i++) for(int i = 0; i < (int)turn_angle_at_speed.size(); i++)
turn_angle_at_speed.setY(i, sin(m_kart_properties->getWheelBase() / turn_angle_at_speed.setY(i, sin( 1.0 / turn_angle_at_speed.getY(i)));
turn_angle_at_speed.getY(i)));
float angle = sin(m_kart_properties->getWheelBase() / radius); float angle = sin(1.0 / radius);
return turn_angle_at_speed.getReverse(angle); return turn_angle_at_speed.getReverse(angle);
} // getSpeedForTurnRadius } // getSpeedForTurnRadius
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the maximum steering angle (depending on speed). */ /** Returns the maximum steering angle (depending on speed).
This is proportional to kart length because physics reverse this effect,
the results of this function should not be used to determine the
real raw steer angle. */
float Kart::getMaxSteerAngle(float speed) const float Kart::getMaxSteerAngle(float speed) const
{ {
InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius(); InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius();
// Convert the turn radius into turn angle // Convert the turn radius into turn angle
// We multiply by wheel base to keep turn radius identical
// across karts of different lengths sharing the same
// turn radius properties
for(int i = 0; i < (int)turn_angle_at_speed.size(); i++) for(int i = 0; i < (int)turn_angle_at_speed.size(); i++)
turn_angle_at_speed.setY(i, sin(m_kart_properties->getWheelBase() / turn_angle_at_speed.setY(i, sin( 1.0 / turn_angle_at_speed.getY(i))
turn_angle_at_speed.getY(i))); * m_kart_properties->getWheelBase());
return turn_angle_at_speed.get(speed); return turn_angle_at_speed.get(speed);
} // getMaxSteerAngle } // getMaxSteerAngle
@ -946,10 +959,16 @@ void Kart::finishedRace(float time, bool from_server)
World::getWorld()->getTicksSinceStart(), World::getWorld()->getTicksSinceStart(),
/*undo_function*/[old_controller, this]() /*undo_function*/[old_controller, this]()
{ {
if (m_network_finish_check_ticks == -1)
return;
m_controller = old_controller; m_controller = old_controller;
}, },
/*replay_function*/[ec, old_controller, this]() /*replay_function*/[ec, old_controller, this]()
{ {
if (m_network_finish_check_ticks == -1)
return;
m_saved_controller = old_controller; m_saved_controller = old_controller;
ec->reset(); ec->reset();
m_controller = ec; m_controller = ec;
@ -1059,9 +1078,8 @@ void Kart::setRaceResult()
} }
else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL) else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL)
{ {
// the top kart wins
FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld()); FreeForAll* ffa = dynamic_cast<FreeForAll*>(World::getWorld());
m_race_result = ffa->getKartAtPosition(1) == this; m_race_result = ffa->getKartFFAResult(getWorldKartId());
} }
else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG) else if (race_manager->getMajorMode() == RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG)
{ {
@ -1314,13 +1332,13 @@ void Kart::eliminate()
*/ */
void Kart::update(int ticks) void Kart::update(int ticks)
{ {
if (m_network_finish_check_ticks != 0 && if (m_network_finish_check_ticks > 0 &&
World::getWorld()->getTicksSinceStart() > World::getWorld()->getTicksSinceStart() >
m_network_finish_check_ticks && m_network_finish_check_ticks &&
!m_finished_race && m_saved_controller != NULL) !m_finished_race && m_saved_controller != NULL)
{ {
Log::warn("Kart", "Missing finish race from server."); Log::warn("Kart", "Missing finish race from server.");
m_network_finish_check_ticks = 0; m_network_finish_check_ticks = -1;
delete m_controller; delete m_controller;
m_controller = m_saved_controller; m_controller = m_saved_controller;
m_saved_controller = NULL; m_saved_controller = NULL;
@ -1423,7 +1441,10 @@ void Kart::update(int ticks)
if(m_view_blocked_by_plunger > 0) m_view_blocked_by_plunger -= ticks; if(m_view_blocked_by_plunger > 0) m_view_blocked_by_plunger -= ticks;
//unblock the view if kart just became shielded //unblock the view if kart just became shielded
if(isShielded()) if(isShielded())
{
m_view_blocked_by_plunger = 0; m_view_blocked_by_plunger = 0;
m_graphical_view_blocked_by_plunger = 0.0f;
}
// Decrease remaining invulnerability time // Decrease remaining invulnerability time
if(m_invulnerable_ticks>0) if(m_invulnerable_ticks>0)
{ {
@ -1532,7 +1553,7 @@ void Kart::update(int ticks)
"v(16-18) %f %f %f steerf(20) %f maxangle(22) %f speed(24) %f " "v(16-18) %f %f %f steerf(20) %f maxangle(22) %f speed(24) %f "
"steering(26-27) %f %f clock(29) %lf skidstate(31) %d factor(33) %f " "steering(26-27) %f %f clock(29) %lf skidstate(31) %d factor(33) %f "
"maxspeed(35) %f engf(37) %f braketick(39) %d brakes(41) %d heading(43) %f " "maxspeed(35) %f engf(37) %f braketick(39) %d brakes(41) %d heading(43) %f "
"noderot(45) %f suslen %f", "bubticks(45) %d bubtor(47) %f",
getIdent().c_str(), getIdent().c_str(),
World::getWorld()->getTime(), World::getWorld()->getTicksSinceStart(), World::getWorld()->getTime(), World::getWorld()->getTicksSinceStart(),
getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(), getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(),
@ -1553,8 +1574,8 @@ void Kart::update(int ticks)
m_brake_ticks, //39 m_brake_ticks, //39
m_controls.getButtonsCompressed(), //41 m_controls.getButtonsCompressed(), //41
getHeading(), //43 getHeading(), //43
m_node->getAbsoluteTransformation().getRotationDegrees().Y, //45 m_bubblegum_ticks, // 45
m_vehicle->getWheelInfo(0).m_raycastInfo.m_suspensionLength m_bubblegum_torque // 47
); );
#endif #endif
// After the physics step was done, the position of the wheels (as stored // After the physics step was done, the position of the wheels (as stored
@ -1663,6 +1684,7 @@ void Kart::update(int ticks)
if (emergency) if (emergency)
{ {
m_view_blocked_by_plunger = 0; m_view_blocked_by_plunger = 0;
m_graphical_view_blocked_by_plunger = 0.0f;
if (m_flying) if (m_flying)
{ {
stopFlying(); stopFlying();
@ -3039,6 +3061,10 @@ void Kart::updateGraphics(float dt)
unsetSquash(); unsetSquash();
} }
} // if squashed } // if squashed
if (m_graphical_view_blocked_by_plunger > 0.0f)
m_graphical_view_blocked_by_plunger -= dt;
if (m_graphical_view_blocked_by_plunger < 0.0f)
m_graphical_view_blocked_by_plunger = 0.0f;
#endif #endif
for (int i = 0; i < EMITTER_COUNT; i++) for (int i = 0; i < EMITTER_COUNT; i++)

View File

@ -223,6 +223,9 @@ protected:
/** When a kart has its view blocked by the plunger, this variable will be /** When a kart has its view blocked by the plunger, this variable will be
* > 0 the number it contains is the time left before removing plunger. */ * > 0 the number it contains is the time left before removing plunger. */
int16_t m_view_blocked_by_plunger; int16_t m_view_blocked_by_plunger;
float m_graphical_view_blocked_by_plunger;
/** The current speed (i.e. length of velocity vector) of this kart. */ /** The current speed (i.e. length of velocity vector) of this kart. */
float m_speed; float m_speed;
@ -394,6 +397,9 @@ public:
virtual int getBlockedByPlungerTicks() const OVERRIDE virtual int getBlockedByPlungerTicks() const OVERRIDE
{ return m_view_blocked_by_plunger; } { return m_view_blocked_by_plunger; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual float getGraphicalViewBlockedByPlunger() const OVERRIDE
{ return m_graphical_view_blocked_by_plunger; }
// ------------------------------------------------------------------------
/** Sets that the view is blocked by a plunger. The duration depends on /** Sets that the view is blocked by a plunger. The duration depends on
* the difficulty, see KartPorperties getPlungerInFaceTime. */ * the difficulty, see KartPorperties getPlungerInFaceTime. */
virtual void blockViewWithPlunger() OVERRIDE; virtual void blockViewWithPlunger() OVERRIDE;

View File

@ -303,17 +303,12 @@ void KartProperties::load(const std::string &filename, const std::string &node)
m_gravity_center_shift.setZ(0); m_gravity_center_shift.setZ(0);
} }
// In older STK versions the physical wheels where moved 'wheel_radius' // The longer the kart,the bigger its turn radius if using an identical
// into the physical body (i.e. 'hypothetical' wheel shape would not // wheel base, exactly proportionally to its length.
// poke out of the physical shape). In order to make the karts a bit more // The wheel base is used to compensate this
// stable, the physical wheel position (i.e. location of raycast) were // We divide by 1.425 to have a default turn radius which conforms
// moved to be on the corner of the shape. In order to retain the same // closely (+-0,1%) with the specifications in kart_characteristics.xml
// steering behaviour, the wheel base (which in turn determines the m_wheel_base = fabsf(m_kart_model->getLength()/1.425f);
// turn angle at certain speeds) is shortened by 2*wheel_radius
// Wheel radius was always 0.25, and is now not used anymore, but in order
// to keep existing steering behaviour, the same formula is still
// used.
m_wheel_base = fabsf(m_kart_model->getLength() - 2*0.25f);
m_shadow_material = material_manager->getMaterialSPM(m_shadow_file, "", m_shadow_material = material_manager->getMaterialSPM(m_shadow_file, "",
"alphablend"); "alphablend");

View File

@ -21,7 +21,8 @@
#include "items/attachment.hpp" #include "items/attachment.hpp"
#include "items/powerup.hpp" #include "items/powerup.hpp"
#include "karts/abstract_kart.hpp" #include "karts/abstract_kart.hpp"
#include "karts/abstract_kart_animation.hpp" #include "karts/explosion_animation.hpp"
#include "karts/rescue_animation.hpp"
#include "karts/controller/player_controller.hpp" #include "karts/controller/player_controller.hpp"
#include "karts/kart_properties.hpp" #include "karts/kart_properties.hpp"
#include "karts/max_speed.hpp" #include "karts/max_speed.hpp"
@ -52,6 +53,7 @@ KartRewinder::KartRewinder(const std::string& ident,
*/ */
void KartRewinder::reset() void KartRewinder::reset()
{ {
m_last_animation_end_ticks = -1;
m_transfrom_from_network = m_transfrom_from_network =
btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f)); btTransform(btQuaternion(0.0f, 0.0f, 0.0f, 1.0f));
Kart::reset(); Kart::reset();
@ -138,7 +140,9 @@ BareNetworkString* KartRewinder::saveState(std::vector<std::string>* ru)
buffer->add(trans.getOrigin()); buffer->add(trans.getOrigin());
btQuaternion quat = trans.getRotation(); btQuaternion quat = trans.getRotation();
buffer->add(quat); buffer->add(quat);
buffer->addUInt32(ka->getEndTicks()); unsigned et = ka->getEndTicks() & 134217727;
et |= ka->getAnimationType() << 27;
buffer->addUInt32(et);
} }
else else
{ {
@ -207,10 +211,38 @@ void KartRewinder::restoreState(BareNetworkString *buffer, int count)
if (has_animation) if (has_animation)
{ {
int end_ticks = buffer->getUInt32(); unsigned et = buffer->getUInt32();
AbstractKartAnimation* ka = getKartAnimation(); int end_ticks = et & 134217727;
if (ka) KartAnimationType kat = (KartAnimationType)(et >> 27);
ka->setEndTransformTicks(m_transfrom_from_network, end_ticks); if (!getKartAnimation() && end_ticks != m_last_animation_end_ticks)
{
Log::info("KartRewinder", "Creating animation %d from state", kat);
switch (kat)
{
case KAT_RESCUE:
new RescueAnimation(this, false/*is_auto_rescue*/,
true/*from_state*/);
break;
case KAT_EXPLOSION_DIRECT_HIT:
new ExplosionAnimation(this, getSmoothedXYZ(),
true/*direct_hit*/, true/*from_state*/);
break;
case KAT_EXPLOSION:
new ExplosionAnimation(this, getSmoothedXYZ(),
false/*direct_hit*/, true/*from_state*/);
break;
default:
Log::warn("KartRewinder", "Unknown animation %d from state",
kat);
break;
}
}
if (getKartAnimation())
{
getKartAnimation()->setEndTransformTicks(m_transfrom_from_network,
end_ticks);
}
m_last_animation_end_ticks = end_ticks;
} }
Vec3 lv = buffer->getVec3(); Vec3 lv = buffer->getVec3();

View File

@ -33,6 +33,7 @@ private:
btTransform m_transfrom_from_network; btTransform m_transfrom_from_network;
float m_prev_steering, m_steering_smoothing_dt, m_steering_smoothing_time; float m_prev_steering, m_steering_smoothing_dt, m_steering_smoothing_time;
int m_last_animation_end_ticks;
public: public:
KartRewinder(const std::string& ident, unsigned int world_kart_id, KartRewinder(const std::string& ident, unsigned int world_kart_id,
int position, const btTransform& init_transform, int position, const btTransform& init_transform,

View File

@ -40,7 +40,8 @@
* and initialised the timer. * and initialised the timer.
* \param kart Pointer to the kart which is animated. * \param kart Pointer to the kart which is animated.
*/ */
RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue) RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue,
bool from_state)
: AbstractKartAnimation(kart, "RescueAnimation") : AbstractKartAnimation(kart, "RescueAnimation")
{ {
btTransform prev_trans = kart->getTrans(); btTransform prev_trans = kart->getTrans();
@ -89,8 +90,11 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
} }
// Clear powerups when rescue in CTF // Clear powerups when rescue in CTF
addNetworkAnimationChecker(race_manager->getMajorMode() == if (!from_state)
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG); {
addNetworkAnimationChecker(race_manager->getMajorMode() ==
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG);
}
} // RescueAnimation } // RescueAnimation
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -49,9 +49,13 @@ protected:
Referee *m_referee; Referee *m_referee;
public: public:
RescueAnimation(AbstractKart *kart, bool is_auto_rescue=false); RescueAnimation(AbstractKart *kart,
bool is_auto_rescue = false,
bool from_state = false);
float maximumHeight(); float maximumHeight();
virtual ~RescueAnimation(); virtual ~RescueAnimation();
virtual void update(int ticks); virtual void update(int ticks);
// ------------------------------------------------------------------------
virtual KartAnimationType getAnimationType() const { return KAT_RESCUE; }
}; // RescueAnimation }; // RescueAnimation
#endif #endif

View File

@ -421,6 +421,7 @@ void Skidding::update(int ticks, bool is_on_ground,
m_predicted_curve->setHeading(m_kart->getHeading()); m_predicted_curve->setHeading(m_kart->getHeading());
float angle = m_kart->getMaxSteerAngle(SPEED) float angle = m_kart->getMaxSteerAngle(SPEED)
* fabsf(getSteeringFraction()); * fabsf(getSteeringFraction());
//FIXME : what is this for ?
float r = kp->getWheelBase() float r = kp->getWheelBase()
/ asin(angle)*1.0f; / asin(angle)*1.0f;

View File

@ -1936,8 +1936,11 @@ int main(int argc, char *argv[] )
} }
else if (CommandLine::has("--lan-server", &s)) else if (CommandLine::has("--lan-server", &s))
{ {
ProfileWorld::disableGraphics(); if (no_graphics)
UserConfigParams::m_enable_sound = false; {
ProfileWorld::disableGraphics();
UserConfigParams::m_enable_sound = false;
}
NetworkConfig::get()->setIsServer(true); NetworkConfig::get()->setIsServer(true);
ServerConfig::m_server_name = s; ServerConfig::m_server_name = s;
ServerConfig::m_wan_server = false; ServerConfig::m_wan_server = false;
@ -2354,7 +2357,8 @@ static void cleanSuperTuxKart()
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
if (!ProfileWorld::isNoGraphics()) if (!ProfileWorld::isNoGraphics())
{ {
if(!NewsManager::get()->waitForReadyToDeleted(2.0f)) if (UserConfigParams::m_internet_status == Online::RequestManager::
IPERM_ALLOWED && !NewsManager::get()->waitForReadyToDeleted(2.0f))
{ {
Log::info("Thread", "News manager not stopping, exiting anyway."); Log::info("Thread", "News manager not stopping, exiting anyway.");
} }

View File

@ -55,10 +55,10 @@ void override_default_params()
{ {
case ACONFIGURATION_SCREENSIZE_SMALL: case ACONFIGURATION_SCREENSIZE_SMALL:
case ACONFIGURATION_SCREENSIZE_NORMAL: case ACONFIGURATION_SCREENSIZE_NORMAL:
UserConfigParams::m_multitouch_scale = 1.3f; UserConfigParams::m_multitouch_scale = 1.4f;
break; break;
case ACONFIGURATION_SCREENSIZE_LARGE: case ACONFIGURATION_SCREENSIZE_LARGE:
UserConfigParams::m_multitouch_scale = 1.2f; UserConfigParams::m_multitouch_scale = 1.3f;
break; break;
case ACONFIGURATION_SCREENSIZE_XLARGE: case ACONFIGURATION_SCREENSIZE_XLARGE:
UserConfigParams::m_multitouch_scale = 1.1f; UserConfigParams::m_multitouch_scale = 1.1f;

View File

@ -308,16 +308,22 @@ void MainLoop::run()
TranslateMessage(&msg); TranslateMessage(&msg);
DispatchMessage(&msg); DispatchMessage(&msg);
if (msg.message == WM_QUIT) if (msg.message == WM_QUIT)
m_abort = true; {
m_request_abort = true;
}
} }
// If parent is killed, abort the child main loop too // If parent is killed, abort the child main loop too
if (WaitForSingleObject(parent, 0) != WAIT_TIMEOUT) if (WaitForSingleObject(parent, 0) != WAIT_TIMEOUT)
m_abort = true; {
m_request_abort = true;
}
} }
#else #else
// POSIX equivalent // POSIX equivalent
if (m_parent_pid != 0 && getppid() != (int)m_parent_pid) if (m_parent_pid != 0 && getppid() != (int)m_parent_pid)
m_abort = true; {
m_request_abort = true;
}
#endif #endif
PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7); PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7);
@ -328,23 +334,28 @@ void MainLoop::run()
// Shutdown next frame if shutdown request is sent while loading the // Shutdown next frame if shutdown request is sent while loading the
// world // world
if (STKHost::existHost() && if ((STKHost::existHost() && STKHost::get()->requestedShutdown()) ||
(STKHost::get()->requestedShutdown() || m_request_abort)) m_request_abort)
{ {
bool exist_host = STKHost::existHost();
core::stringw msg = _("Server connection timed out."); core::stringw msg = _("Server connection timed out.");
if (!ProfileWorld::isNoGraphics()) if (!m_request_abort)
{ {
SFXManager::get()->quickSound("anvil"); if (!ProfileWorld::isNoGraphics())
if (!STKHost::get()->getErrorMessage().empty())
{ {
msg = STKHost::get()->getErrorMessage(); SFXManager::get()->quickSound("anvil");
if (!STKHost::get()->getErrorMessage().empty())
{
msg = STKHost::get()->getErrorMessage();
}
} }
} }
STKHost::get()->shutdown(); if (exist_host == true)
// In case the user opened a race pause dialog {
GUIEngine::ModalDialog::dismiss(); STKHost::get()->shutdown();
}
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
if (CVS->isGLSL()) if (CVS->isGLSL())
@ -357,24 +368,32 @@ void MainLoop::run()
irr_driver->getActualScreenSize().Height); irr_driver->getActualScreenSize().Height);
} }
#endif #endif
// In case the user opened a race pause dialog
GUIEngine::ModalDialog::dismiss();
if (World::getWorld()) if (World::getWorld())
{ {
race_manager->clearNetworkGrandPrixResult(); race_manager->clearNetworkGrandPrixResult();
race_manager->exitRace(); race_manager->exitRace();
} }
if (!ProfileWorld::isNoGraphics())
if (exist_host == true)
{ {
StateManager::get()->resetAndSetStack( if (!ProfileWorld::isNoGraphics())
NetworkConfig::get()->getResetScreens().data()); {
MessageQueue::add(MessageQueue::MT_ERROR, msg); StateManager::get()->resetAndSetStack(
NetworkConfig::get()->getResetScreens().data());
MessageQueue::add(MessageQueue::MT_ERROR, msg);
}
NetworkConfig::get()->unsetNetworking();
}
if (m_request_abort)
{
m_abort = true;
} }
NetworkConfig::get()->unsetNetworking();
}
if (m_request_abort)
{
m_abort = true;
} }
if (!m_abort) if (!m_abort)
@ -404,84 +423,96 @@ void MainLoop::run()
PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F); PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F);
Online::RequestManager::get()->update(frame_duration); Online::RequestManager::get()->update(frame_duration);
PROFILER_POP_CPU_MARKER(); PROFILER_POP_CPU_MARKER();
}
m_ticks_adjustment.lock(); m_ticks_adjustment.lock();
if (m_ticks_adjustment.getData() != 0) if (m_ticks_adjustment.getData() != 0)
{
if (m_ticks_adjustment.getData() > 0)
{ {
num_steps += m_ticks_adjustment.getData(); if (m_ticks_adjustment.getData() > 0)
m_ticks_adjustment.getData() = 0;
}
else if (m_ticks_adjustment.getData() < 0)
{
int new_steps = num_steps + m_ticks_adjustment.getData();
if (new_steps < 0)
{ {
num_steps = 0; num_steps += m_ticks_adjustment.getData();
m_ticks_adjustment.getData() = new_steps;
}
else
{
num_steps = new_steps;
m_ticks_adjustment.getData() = 0; m_ticks_adjustment.getData() = 0;
} }
} else if (m_ticks_adjustment.getData() < 0)
}
m_ticks_adjustment.unlock();
for(int i=0; i<num_steps; i++)
{
if (World::getWorld() && history->replayHistory())
history->updateReplay(World::getWorld()->getTicksSinceStart());
PROFILER_PUSH_CPU_MARKER("Protocol manager update",
0x7F, 0x00, 0x7F);
if (auto pm = ProtocolManager::lock())
{
pm->update(1);
}
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255);
if (World::getWorld()) updateRace(1);
PROFILER_POP_CPU_MARKER();
// We need to check again because update_race may have requested
// the main loop to abort; and it's not a good idea to continue
// since the GUI engine is no more to be called then.
if (m_abort) break;
if (m_frame_before_loading_world)
{
m_frame_before_loading_world = false;
break;
}
if (World::getWorld())
{
if (World::getWorld()->getPhase() == WorldStatus::SETUP_PHASE)
{ {
// Skip the large num steps contributed by loading time int new_steps = num_steps + m_ticks_adjustment.getData();
World::getWorld()->updateTime(1); if (new_steps < 0)
{
num_steps = 0;
m_ticks_adjustment.getData() = new_steps;
}
else
{
num_steps = new_steps;
m_ticks_adjustment.getData() = 0;
}
}
}
m_ticks_adjustment.unlock();
for (int i = 0; i < num_steps; i++)
{
if (World::getWorld() && history->replayHistory())
{
history->updateReplay(
World::getWorld()->getTicksSinceStart());
}
PROFILER_PUSH_CPU_MARKER("Protocol manager update",
0x7F, 0x00, 0x7F);
if (auto pm = ProtocolManager::lock())
{
pm->update(1);
}
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("Update race", 0, 255, 255);
if (World::getWorld())
{
updateRace(1);
}
PROFILER_POP_CPU_MARKER();
// We need to check again because update_race may have requested
// the main loop to abort; and it's not a good idea to continue
// since the GUI engine is no more to be called then.
if (m_abort || m_request_abort)
break;
if (m_frame_before_loading_world)
{
m_frame_before_loading_world = false;
break; break;
} }
World::getWorld()->updateTime(1);
} if (World::getWorld())
} // for i < num_steps {
if (World::getWorld()->getPhase()==WorldStatus::SETUP_PHASE)
{
// Skip the large num steps contributed by loading time
World::getWorld()->updateTime(1);
break;
}
World::getWorld()->updateTime(1);
}
} // for i < num_steps
// Handle controller the last to avoid slow PC sending actions too late // Handle controller the last to avoid slow PC sending actions too
if (!m_abort) // late
{
if (!ProfileWorld::isNoGraphics()) if (!ProfileWorld::isNoGraphics())
{ {
// User aborted (e.g. closed window) // User aborted (e.g. closed window)
bool abort = !irr_driver->getDevice()->run(); bool abort = !irr_driver->getDevice()->run();
if (abort) if (abort)
m_abort = true; {
m_request_abort = true;
}
} }
if (auto gp = GameProtocol::lock()) if (auto gp = GameProtocol::lock())
{
gp->sendActions(); gp->sendActions();
}
} }
PROFILER_POP_CPU_MARKER(); // MainLoop pop PROFILER_POP_CPU_MARKER(); // MainLoop pop
PROFILER_SYNC_FRAME(); PROFILER_SYNC_FRAME();

View File

@ -159,7 +159,8 @@ const std::string& EasterEggHunt::getIdent() const
/** Called when a kart has collected an egg. /** Called when a kart has collected an egg.
* \param kart The kart that collected an egg. * \param kart The kart that collected an egg.
*/ */
void EasterEggHunt::collectedItem(const AbstractKart *kart, const Item *item) void EasterEggHunt::collectedItem(const AbstractKart *kart,
const ItemState *item )
{ {
if(item->getType() != ItemState::ITEM_EASTER_EGG) return; if(item->getType() != ItemState::ITEM_EASTER_EGG) return;

View File

@ -66,7 +66,7 @@ public:
virtual void getKartsDisplayInfo( virtual void getKartsDisplayInfo(
std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE; std::vector<RaceGUIBase::KartIconDisplayInfo> *info) OVERRIDE;
virtual void collectedItem(const AbstractKart *kart, virtual void collectedItem(const AbstractKart *kart,
const Item *item ) OVERRIDE; const ItemState *item ) OVERRIDE;
void collectedEasterEggGhost(int world_id); void collectedEasterEggGhost(int world_id);
const int numberOfEggsFound() { return m_eggs_found; } const int numberOfEggsFound() { return m_eggs_found; }

View File

@ -196,3 +196,14 @@ video::SColor FreeForAll::getColor(unsigned int kart_id) const
{ {
return GUIEngine::getSkin()->getColor("font::normal"); return GUIEngine::getSkin()->getColor("font::normal");
} // getColor } // getColor
// ----------------------------------------------------------------------------
bool FreeForAll::getKartFFAResult(int kart_id) const
{
// the kart(s) which has the top score wins
AbstractKart* k = getKartAtPosition(1);
if (!k)
return false;
int top_score = getKartScore(k->getWorldKartId());
return getKartScore(kart_id) == top_score;
} // getKartFFAResult

View File

@ -66,6 +66,8 @@ public:
void setKartScoreFromServer(NetworkString& ns); void setKartScoreFromServer(NetworkString& ns);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
int getKartScore(int kart_id) const { return m_scores.at(kart_id); } int getKartScore(int kart_id) const { return m_scores.at(kart_id); }
// ------------------------------------------------------------------------
bool getKartFFAResult(int kart_id) const;
}; // FreeForAll }; // FreeForAll

View File

@ -213,11 +213,11 @@ const std::string& SoccerWorld::getIdent() const
void SoccerWorld::update(int ticks) void SoccerWorld::update(int ticks)
{ {
updateBallPosition(ticks); updateBallPosition(ticks);
updateSectorForKarts(); if (Track::getCurrentTrack()->hasNavMesh())
if (Track::getCurrentTrack()->hasNavMesh() &&
!NetworkConfig::get()->isNetworking())
{ {
updateAIData(); updateSectorForKarts();
if (!NetworkConfig::get()->isNetworking())
updateAIData();
} }
WorldWithRank::update(ticks); WorldWithRank::update(ticks);
@ -652,6 +652,9 @@ int SoccerWorld::getAttacker(KartTeam team) const
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
unsigned int SoccerWorld::getRescuePositionIndex(AbstractKart *kart) unsigned int SoccerWorld::getRescuePositionIndex(AbstractKart *kart)
{ {
if (!Track::getCurrentTrack()->hasNavMesh())
return m_kart_position_map.at(kart->getWorldKartId());
int last_valid_node = int last_valid_node =
getTrackSector(kart->getWorldKartId())->getLastValidGraphNode(); getTrackSector(kart->getWorldKartId())->getLastValidGraphNode();
if (last_valid_node >= 0) if (last_valid_node >= 0)
@ -663,6 +666,9 @@ unsigned int SoccerWorld::getRescuePositionIndex(AbstractKart *kart)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
btTransform SoccerWorld::getRescueTransform(unsigned int rescue_pos) const btTransform SoccerWorld::getRescueTransform(unsigned int rescue_pos) const
{ {
if (!Track::getCurrentTrack()->hasNavMesh())
return WorldWithRank::getRescueTransform(rescue_pos);
const Vec3 &xyz = Graph::get()->getQuad(rescue_pos)->getCenter(); const Vec3 &xyz = Graph::get()->getQuad(rescue_pos)->getCenter();
const Vec3 &normal = Graph::get()->getQuad(rescue_pos)->getNormal(); const Vec3 &normal = Graph::get()->getQuad(rescue_pos)->getNormal();
btTransform pos; btTransform pos;

View File

@ -587,6 +587,9 @@ void World::onGo()
*/ */
void World::terminateRace() void World::terminateRace()
{ {
// In case the user opened paused dialog in network
GUIEngine::ModalDialog::dismiss();
m_schedule_pause = false; m_schedule_pause = false;
m_schedule_unpause = false; m_schedule_unpause = false;

View File

@ -42,7 +42,7 @@
class AbstractKart; class AbstractKart;
class btRigidBody; class btRigidBody;
class Controller; class Controller;
class Item; class ItemState;
class PhysicalObject; class PhysicalObject;
namespace Scripting namespace Scripting
@ -263,7 +263,8 @@ public:
int *amount ); int *amount );
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Receives notification if an item is collected. Used for easter eggs. */ /** Receives notification if an item is collected. Used for easter eggs. */
virtual void collectedItem(const AbstractKart *kart, const Item *item) {} virtual void collectedItem(const AbstractKart *kart,
const ItemState *item ) {}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
virtual void endRaceEarly() { return; } virtual void endRaceEarly() { return; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -154,8 +154,27 @@ void RewindManager::saveState()
m_overall_state_size = 0; m_overall_state_size = 0;
std::vector<std::string> rewinder_using; std::vector<std::string> rewinder_using;
// We must save the item state first (so that it is restored first),
// otherwise state updates for a kart could be overwritten by
// e.g. simulating the item collection later (which resets bubblegum
// counter).
BareNetworkString* buffer = NULL;
if(auto r = m_all_rewinder["N"].lock())
buffer = r->saveState(&rewinder_using);
if (buffer)
{
m_overall_state_size += buffer->size();
gp->addState(buffer);
}
delete buffer; // buffer can be freed
for (auto& p : m_all_rewinder) for (auto& p : m_all_rewinder)
{ {
// The Network ItemManager was saved first before this loop,
// so skip it here.
if(p.first=="N") continue;
// TODO: check if it's worth passing in a sufficiently large buffer from // TODO: check if it's worth passing in a sufficiently large buffer from
// GameProtocol - this would save the copy operation. // GameProtocol - this would save the copy operation.
BareNetworkString* buffer = NULL; BareNetworkString* buffer = NULL;

View File

@ -136,7 +136,7 @@ void loadServerConfigXML(const XMLNode* root)
return; return;
} }
int config_file_version = -1; /*int config_file_version = -1;
if (root->get("version", &config_file_version) < 1 || if (root->get("version", &config_file_version) < 1 ||
config_file_version < stk_config->m_min_server_version || config_file_version < stk_config->m_min_server_version ||
config_file_version > stk_config->m_max_server_version) config_file_version > stk_config->m_max_server_version)
@ -146,7 +146,7 @@ void loadServerConfigXML(const XMLNode* root)
delete root; delete root;
writeServerConfigToDisk(); writeServerConfigToDisk();
return; return;
} }*/
for (unsigned i = 0; i < g_server_params.size(); i++) for (unsigned i = 0; i < g_server_params.size(); i++)
g_server_params[i]->findYourDataInAChildOf(root); g_server_params[i]->findYourDataInAChildOf(root);

View File

@ -269,7 +269,7 @@ namespace ServerConfig
// ======================================================================== // ========================================================================
/** Server version, will be advanced if there are protocol changes. */ /** Server version, will be advanced if there are protocol changes. */
static const uint32_t m_server_version = 1; static const uint32_t m_server_version = 2;
// ======================================================================== // ========================================================================
void loadServerConfig(const std::string& path = ""); void loadServerConfig(const std::string& path = "");
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -634,8 +634,7 @@ void STKHost::setErrorMessage(const irr::core::stringw &message)
{ {
if (!message.empty()) if (!message.empty())
{ {
irr::core::stringc s(message.c_str()); Log::error("STKHost", "%s", StringUtils::wideToUtf8(message).c_str());
Log::error("STKHost", "%s", s.c_str());
} }
m_error_message = message; m_error_message = message;
} // setErrorMessage } // setErrorMessage

View File

@ -326,7 +326,11 @@ namespace Online
setProgress(-1.0f); setProgress(-1.0f);
Request::afterOperation(); Request::afterOperation();
curl_easy_cleanup(m_curl_session); if (m_curl_session)
{
curl_easy_cleanup(m_curl_session);
m_curl_session = NULL;
}
} // afterOperation } // afterOperation
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -63,7 +63,7 @@ namespace Online
std::string m_filename; std::string m_filename;
/** Pointer to the curl data structure for this request. */ /** Pointer to the curl data structure for this request. */
CURL *m_curl_session; CURL *m_curl_session = NULL;
/** curl return code. */ /** curl return code. */
CURLcode m_curl_code; CURLcode m_curl_code;
@ -93,7 +93,14 @@ namespace Online
int priority = 1); int priority = 1);
HTTPRequest(const char * const filename, bool manage_memory = false, HTTPRequest(const char * const filename, bool manage_memory = false,
int priority = 1); int priority = 1);
virtual ~HTTPRequest() {} virtual ~HTTPRequest()
{
if (m_curl_session)
{
curl_easy_cleanup(m_curl_session);
m_curl_session = NULL;
}
}
virtual bool isAllowedToAdd() const OVERRIDE; virtual bool isAllowedToAdd() const OVERRIDE;
void setApiURL(const std::string& url, const std::string &action); void setApiURL(const std::string& url, const std::string &action);
void setAddonsURL(const std::string& path); void setAddonsURL(const std::string& path);

View File

@ -213,7 +213,13 @@ namespace Online
me->m_current_request->execute(); me->m_current_request->execute();
// This test is necessary in case that execute() was aborted // This test is necessary in case that execute() was aborted
// (otherwise the assert in addResult will be triggered). // (otherwise the assert in addResult will be triggered).
if (!me->getAbort()) me->addResult(me->m_current_request); if (!me->getAbort())
me->addResult(me->m_current_request);
else if (me->m_current_request->manageMemory())
{
delete me->m_current_request;
me->m_current_request = NULL;
}
me->m_request_queue.lock(); me->m_request_queue.lock();
} // while handle all requests } // while handle all requests

View File

@ -139,10 +139,10 @@ GUIEngine::EventPropagation MultitouchSettingsDialog::processEvent(
{ {
case ACONFIGURATION_SCREENSIZE_SMALL: case ACONFIGURATION_SCREENSIZE_SMALL:
case ACONFIGURATION_SCREENSIZE_NORMAL: case ACONFIGURATION_SCREENSIZE_NORMAL:
UserConfigParams::m_multitouch_scale = 1.3f; UserConfigParams::m_multitouch_scale = 1.4f;
break; break;
case ACONFIGURATION_SCREENSIZE_LARGE: case ACONFIGURATION_SCREENSIZE_LARGE:
UserConfigParams::m_multitouch_scale = 1.2f; UserConfigParams::m_multitouch_scale = 1.3f;
break; break;
case ACONFIGURATION_SCREENSIZE_XLARGE: case ACONFIGURATION_SCREENSIZE_XLARGE:
UserConfigParams::m_multitouch_scale = 1.1f; UserConfigParams::m_multitouch_scale = 1.1f;

View File

@ -52,9 +52,15 @@ GhostReplaySelection::~GhostReplaySelection()
void GhostReplaySelection::tearDown() void GhostReplaySelection::tearDown()
{ {
m_replay_list_widget->setIcons(NULL); m_replay_list_widget->setIcons(NULL);
}
// ----------------------------------------------------------------------------
void GhostReplaySelection::unloaded()
{
delete m_icon_bank; delete m_icon_bank;
m_icon_bank = NULL; m_icon_bank = NULL;
} } // unloaded
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Triggers a refresh of the replay file list. /** Triggers a refresh of the replay file list.
@ -117,6 +123,24 @@ void GhostReplaySelection::loadedFromFile()
m_mode_tabs = getWidget<GUIEngine::RibbonWidget>("race_mode"); m_mode_tabs = getWidget<GUIEngine::RibbonWidget>("race_mode");
m_active_mode = RaceManager::MINOR_MODE_TIME_TRIAL; m_active_mode = RaceManager::MINOR_MODE_TIME_TRIAL;
m_active_mode_is_linear = true; m_active_mode_is_linear = true;
m_icon_bank = new irr::gui::STKModifiedSpriteBank( GUIEngine::getGUIEnv());
for(unsigned int i=0; i<kart_properties_manager->getNumberOfKarts(); i++)
{
const KartProperties* prop = kart_properties_manager->getKartById(i);
m_icon_bank->addTextureAsSprite(prop->getIconMaterial()->getTexture());
}
video::ITexture* kart_not_found = irr_driver->getTexture(
file_manager->getAsset(FileManager::GUI_ICON, "main_help.png"));
m_icon_unknown_kart = m_icon_bank->addTextureAsSprite(kart_not_found);
video::ITexture* lock = irr_driver->getTexture( file_manager->getAsset(
FileManager::GUI_ICON, "gui_lock.png"));
m_icon_lock = m_icon_bank->addTextureAsSprite(lock);
} // loadedFromFile } // loadedFromFile
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -148,24 +172,6 @@ void GhostReplaySelection::init()
Screen::init(); Screen::init();
m_cur_difficulty = race_manager->getDifficulty(); m_cur_difficulty = race_manager->getDifficulty();
m_icon_bank = new irr::gui::STKModifiedSpriteBank( GUIEngine::getGUIEnv());
for(unsigned int i=0; i<kart_properties_manager->getNumberOfKarts(); i++)
{
const KartProperties* prop = kart_properties_manager->getKartById(i);
m_icon_bank->addTextureAsSprite(prop->getIconMaterial()->getTexture());
}
video::ITexture* kart_not_found = irr_driver->getTexture( file_manager->getAsset(FileManager::GUI_ICON,
"main_help.png" ));
m_icon_unknown_kart = m_icon_bank->addTextureAsSprite(kart_not_found);
video::ITexture* lock = irr_driver->getTexture( file_manager->getAsset(FileManager::GUI_ICON,
"gui_lock.png" ));
m_icon_lock = m_icon_bank->addTextureAsSprite(lock);
int icon_height = getHeight()/24; int icon_height = getHeight()/24;
// 128 is the height of the image file // 128 is the height of the image file
//FIXME : this isn't guaranteed //FIXME : this isn't guaranteed

View File

@ -106,6 +106,8 @@ public:
virtual void init() OVERRIDE; virtual void init() OVERRIDE;
virtual void tearDown() OVERRIDE; virtual void tearDown() OVERRIDE;
virtual void unloaded() OVERRIDE;
virtual bool onEscapePressed() OVERRIDE; virtual bool onEscapePressed() OVERRIDE;

View File

@ -258,9 +258,9 @@ void CreateServerScreen::createServer()
return; return;
} }
const bool private_server = !password.empty();
ServerConfig::m_private_server_password = password; ServerConfig::m_private_server_password = password;
if (!password.empty()) password = std::string(" --server-password=") + password;
password = std::string(" --server-password=") + password;
TransportAddress server_address(0x7f000001, TransportAddress server_address(0x7f000001,
stk_config->m_server_discovery_port); stk_config->m_server_discovery_port);
@ -268,7 +268,7 @@ void CreateServerScreen::createServer()
auto server = std::make_shared<Server>(0/*server_id*/, name, auto server = std::make_shared<Server>(0/*server_id*/, name,
max_players, /*current_player*/0, (RaceManager::Difficulty) max_players, /*current_player*/0, (RaceManager::Difficulty)
difficulty_widget->getSelection(PLAYER_ID_GAME_MASTER), difficulty_widget->getSelection(PLAYER_ID_GAME_MASTER),
0, server_address, !password.empty(), false); 0, server_address, private_server, false);
#undef USE_GRAPHICS_SERVER #undef USE_GRAPHICS_SERVER
#ifdef USE_GRAPHICS_SERVER #ifdef USE_GRAPHICS_SERVER

View File

@ -274,7 +274,6 @@ void OptionsScreenVideo::init()
} }
} }
#ifndef ANDROID
if (!found_config_res) if (!found_config_res)
{ {
r.width = UserConfigParams::m_width; r.width = UserConfigParams::m_width;
@ -291,6 +290,7 @@ void OptionsScreenVideo::init()
} }
} // next found resolution } // next found resolution
#ifndef ANDROID
// Add default resolutions that were not found by irrlicht // Add default resolutions that were not found by irrlicht
if (!found_1024_768) if (!found_1024_768)
{ {

View File

@ -270,12 +270,13 @@ void RaceGUI::renderGlobal(float dt)
if(world->getPhase() == World::GOAL_PHASE) if(world->getPhase() == World::GOAL_PHASE)
drawGlobalGoal(); drawGlobalGoal();
if (!m_enabled) return;
// MiniMap is drawn when the players wait for the start countdown to end // MiniMap is drawn when the players wait for the start countdown to end
drawGlobalMiniMap(); drawGlobalMiniMap();
// Timer etc. are not displayed unless the game is actually started. // Timer etc. are not displayed unless the game is actually started.
if(!world->isRacePhase()) return; if(!world->isRacePhase()) return;
if (!m_enabled) return;
//drawGlobalTimer checks if it should display in the current phase/mode //drawGlobalTimer checks if it should display in the current phase/mode
drawGlobalTimer(); drawGlobalTimer();
@ -539,7 +540,8 @@ void RaceGUI::drawGlobalMiniMap()
{ {
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
//TODO : exception for some game modes ? Another option "Hidden in race, shown in battle ?" //TODO : exception for some game modes ? Another option "Hidden in race, shown in battle ?"
if(UserConfigParams::m_minimap_display == 2 /*map hidden*/) if (UserConfigParams::m_minimap_display == 2 /*map hidden*/ ||
UserConfigParams::m_multitouch_scale > 1.3f)
return; return;
// draw a map when arena has a navigation mesh. // draw a map when arena has a navigation mesh.

View File

@ -670,8 +670,8 @@ void RaceGUIBase::drawGlobalReadySetGo()
} // drawGlobalReadySetGo } // drawGlobalReadySetGo
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Draw players icons and their times (if defined in the current mode). /** Draw players icons and, depending on the current mode, their time
* Also takes care of icon looking different due to plumber, squashing, ... * or their score (battle lives, egg collected, etc.).
*/ */
void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin) void RaceGUIBase::drawGlobalPlayerIcons(int bottom_margin)
{ {
@ -1138,3 +1138,5 @@ void RaceGUIBase::removeReferee()
m_referee->removeFromSceneGraph(); m_referee->removeFromSceneGraph();
} }
} // removeReferee } // removeReferee

View File

@ -56,13 +56,13 @@ RaceGUIMultitouch::RaceGUIMultitouch(RaceGUIBase* race_gui)
m_device = input_manager->getDeviceManager()->getMultitouchDevice(); m_device = input_manager->getDeviceManager()->getMultitouchDevice();
if (UserConfigParams::m_multitouch_scale > 1.5f) if (UserConfigParams::m_multitouch_scale > 1.6f)
{ {
UserConfigParams::m_multitouch_scale = 1.5f; UserConfigParams::m_multitouch_scale = 1.6f;
} }
else if (UserConfigParams::m_multitouch_scale < 0.5f) else if (UserConfigParams::m_multitouch_scale < 0.8f)
{ {
UserConfigParams::m_multitouch_scale = 0.5f; UserConfigParams::m_multitouch_scale = 0.8f;
} }
init(); init();
@ -340,6 +340,7 @@ void RaceGUIMultitouch::draw(const AbstractKart* kart,
} }
else if (button->type == MultitouchButtonType::BUTTON_FIRE && else if (button->type == MultitouchButtonType::BUTTON_FIRE &&
kart->getPowerup()->getNum() > 1 && kart->getPowerup()->getNum() > 1 &&
!kart->hasFinishedRace() &&
m_gui_action == false) m_gui_action == false)
{ {
gui::ScalableFont* font = GUIEngine::getHighresDigitFont(); gui::ScalableFont* font = GUIEngine::getHighresDigitFont();

View File

@ -2492,6 +2492,10 @@ Vec3 Track::flagCommand(const XMLNode *node)
} }
#endif #endif
m_track_object_manager->castRay
(loc, loc + (-10000 * quad_normal), &hit_point, &m, &normal,
/*interpolate*/false);
const std::string &name = node->getName(); const std::string &name = node->getName();
if (name == "red-flag") if (name == "red-flag")
{ {

View File

@ -167,9 +167,11 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent,
else if (type == "sfx-emitter") else if (type == "sfx-emitter")
{ {
// FIXME: at this time sound emitters are just disabled in multiplayer // FIXME: at this time sound emitters are just disabled in multiplayer
// otherwise the sounds would be constantly heard // otherwise the sounds would be constantly heard, for networking
if (race_manager->getNumLocalPlayers() < 2) // the index of item needs to be same so we create and disable it
m_presentation = new TrackObjectPresentationSound(xml_node, parent); // in TrackObjectPresentationSound constructor
m_presentation = new TrackObjectPresentationSound(xml_node, parent,
race_manager->getNumLocalPlayers() > 1);
} }
else if (type == "action-trigger") else if (type == "action-trigger")
{ {

View File

@ -652,7 +652,8 @@ void TrackObjectPresentationMesh::reset()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
TrackObjectPresentationSound::TrackObjectPresentationSound( TrackObjectPresentationSound::TrackObjectPresentationSound(
const XMLNode& xml_node, const XMLNode& xml_node,
scene::ISceneNode* parent) scene::ISceneNode* parent,
bool disable_for_multiplayer)
: TrackObjectPresentation(xml_node) : TrackObjectPresentation(xml_node)
{ {
// TODO: respect 'parent' if any // TODO: respect 'parent' if any
@ -680,6 +681,13 @@ TrackObjectPresentationSound::TrackObjectPresentationSound(
float max_dist = 390.0f; float max_dist = 390.0f;
xml_node.get("max_dist", &max_dist ); xml_node.get("max_dist", &max_dist );
if (trigger_when_near)
{
ItemManager::get()->placeTrigger(m_init_xyz, trigger_distance, this);
}
if (disable_for_multiplayer)
return;
// first try track dir, then global dir // first try track dir, then global dir
std::string soundfile = Track::getCurrentTrack()->getTrackFile(sound); std::string soundfile = Track::getCurrentTrack()->getTrackFile(sound);
//std::string soundfile = file_manager->getAsset(FileManager::MODEL,sound); //std::string soundfile = file_manager->getAsset(FileManager::MODEL,sound);
@ -708,10 +716,6 @@ TrackObjectPresentationSound::TrackObjectPresentationSound(
else else
Log::error("TrackObject", "Sound emitter object could not be created."); Log::error("TrackObject", "Sound emitter object could not be created.");
if (trigger_when_near)
{
ItemManager::get()->placeTrigger(m_init_xyz, trigger_distance, this);
}
} // TrackObjectPresentationSound } // TrackObjectPresentationSound
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -286,7 +286,8 @@ private:
public: public:
TrackObjectPresentationSound(const XMLNode& xml_node, TrackObjectPresentationSound(const XMLNode& xml_node,
scene::ISceneNode* parent); scene::ISceneNode* parent,
bool disable_for_multiplayer);
virtual ~TrackObjectPresentationSound(); virtual ~TrackObjectPresentationSound();
virtual void onTriggerItemApproached() OVERRIDE; virtual void onTriggerItemApproached() OVERRIDE;
virtual void update(float dt) OVERRIDE; virtual void update(float dt) OVERRIDE;

View File

@ -283,9 +283,27 @@ bool SeparateProcess::createChildProcess(const std::string& exe,
} }
std::string data_path = AssetsAndroid::getDataPath(); std::string data_path = AssetsAndroid::getDataPath();
std::string main_path = data_path + "/lib/libmain.so"; std::string main_path;
if (data_path.empty() || access(main_path.c_str(), R_OK) != 0) if (!data_path.empty())
{
main_path = data_path + "/lib/libmain.so";
}
if (main_path.empty() || access(main_path.c_str(), R_OK) != 0)
{
std::string lib_path = AssetsAndroid::getLibPath();
if (!lib_path.empty())
{
main_path = lib_path + "/libmain.so";
}
Log::info("SeparateProcess", "Trying to use fallback lib path: %s",
main_path.c_str());
}
if (main_path.empty() || access(main_path.c_str(), R_OK) != 0)
{ {
Log::error("SeparateProcess", "Error: Cannot read libmain.so"); Log::error("SeparateProcess", "Error: Cannot read libmain.so");
return false; return false;

194
tools/android_builder.sh Executable file
View File

@ -0,0 +1,194 @@
#!/bin/sh
#
# (C) 2018 Dawid Gan, under the GPLv3
#
# A script that builds Android APKs for all architectures
#
# The script assumes that you know what you are doing. It allows to generate all
# packages for Google Play Store with single command. If you just want to build
# STK for your own use, then use android/make.sh script instead.
export BUILD_TYPE=Beta
export PROJECT_VERSION=git20181001
export PROJECT_CODE=48
export STOREPASS="xxx"
export KEYSTORE="/path/to/stk.keystore"
export ALIAS="alias"
check_error()
{
if [ $? -gt 0 ]; then
echo "Error ocurred."
exit
fi
}
clean()
{
echo "Clean everything"
rm -rf ./android-armv7
rm -rf ./android-aarch64
rm -rf ./android-x86
rm -rf ./android-x86_64
rm -rf ./android/assets
rm -rf ./android-output
cd android
./make.sh clean
cd -
}
init_directories()
{
echo "Init directories"
if [ ! -d "./android-armv7" ]; then
echo "Creating android-armv7 directory"
mkdir android-armv7
cd android-armv7
ln -s ../android/Android.mk
ln -s ../android/AndroidManifest.xml
ln -s ../android/banner.png
ln -s ../android/build.gradle
ln -s ../android/icon.png
ln -s ../android/icon-dbg.png
ln -s ../android/make.sh
ln -s ../android/android-ndk
ln -s ../android/android-sdk
ln -s ../android/assets
cd -
fi
if [ ! -d "./android-aarch64" ]; then
echo "Creating android-aarch64 directory"
cp -a ./android-armv7 ./android-aarch64
fi
if [ ! -d "./android-x86" ]; then
echo "Creating android-x86 directory"
cp -a ./android-armv7 ./android-x86
fi
if [ ! -d "./android-x86_64" ]; then
echo "Creating android-x86_64 directory"
cp -a ./android-armv7 ./android-x86_64
fi
if [ ! -d "./android-output" ]; then
echo "Creating android-output directory"
mkdir ./android-output
fi
}
generate_assets()
{
echo "Generate assets"
if [ -d "./android/assets" ]; then
echo "Assets already found in ./android/assets"
return
fi
cd ./android
./generate_assets.sh
if [ ! -f "./assets/directories.txt" ]; then
echo "Error: Couldn't generate assets"
return
fi
if [ -f "./assets/data/supertuxkart.git" ]; then
mv "./assets/data/supertuxkart.git" \
"./assets/data/supertuxkart.$PROJECT_VERSION"
fi
cd -
}
build_package()
{
export ARCH1=$1
export ARCH2=$2
echo "Build package for $ARCH1"
if [ -f "./android-output/SuperTuxKart-$PROJECT_VERSION-$ARCH1.apk" ]; then
echo "Package for architecture $ARCH1 is already built"
#return
fi
export COMPILE_ARCH=$ARCH1
cd ./android-$ARCH1
./make.sh -j5
cd -
if [ ! -f ./android-$ARCH1/build/outputs/apk/android-$ARCH1-release-unsigned.apk ]; then
echo "Error: Couldn't build apk for architecture $ARCH1"
return
fi
cp ./android-$ARCH1/build/outputs/apk/android-$ARCH1-release-unsigned.apk \
./android-output/SuperTuxKart-$PROJECT_VERSION-$ARCH1-unaligned.apk
cp ./android-$ARCH1/obj/local/$ARCH2/libmain.so \
./android-output/SuperTuxKart-$PROJECT_VERSION-$ARCH1-libmain.so
cd ./android-output
jarsigner -sigalg SHA1withRSA -digestalg SHA1 \
-keystore "$KEYSTORE" \
-storepass "$STOREPASS" \
SuperTuxKart-$PROJECT_VERSION-$ARCH1-unaligned.apk \
"$ALIAS"
check_error
zipalign -f 4 SuperTuxKart-$PROJECT_VERSION-$ARCH1-unaligned.apk \
SuperTuxKart-$PROJECT_VERSION-$ARCH1.apk
check_error
rm SuperTuxKart-$PROJECT_VERSION-$ARCH1-unaligned.apk
cd -
}
# Handle clean command
if [ ! -z "$1" ] && [ "$1" = "clean" ]; then
clean
exit
fi
#Build packages
init_directories
generate_assets
if [ -z "$1" ] || [ "$1" = "armv7" ]; then
build_package armv7 armeabi-v7a
fi
PROJECT_CODE=$(($PROJECT_CODE + 1))
if [ -z "$1" ] || [ "$1" = "aarch64" ]; then
build_package aarch64 arm64-v8a
fi
PROJECT_CODE=$(($PROJECT_CODE + 1))
if [ -z "$1" ] || [ "$1" = "x86" ]; then
build_package x86 x86
fi
PROJECT_CODE=$(($PROJECT_CODE + 1))
if [ -z "$1" ] || [ "$1" = "x86_64" ]; then
build_package x86_64 x86_64
fi