This commit is contained in:
deve 2016-10-05 11:30:22 +02:00
commit ce28d46d14
76 changed files with 2733 additions and 654 deletions

View File

@ -333,11 +333,10 @@ bool MusicOggStream::streamIntoBuffer(ALuint buffer)
int size = 0;
int portion;
int result;
while(size < m_buffer_size)
{
result = ov_read(&m_oggStream, pcm + size, m_buffer_size - size,
int result = ov_read(&m_oggStream, pcm + size, m_buffer_size - size,
isBigEndian, 2, 1, &portion);
if(result > 0)

View File

@ -516,7 +516,6 @@ const irr::core::stringw
if (track == NULL) return irr::core::stringw( L"????" );
return _("New track '%s' now available", track->getName());
break;
}
case UNLOCK_MODE:
{

View File

@ -81,7 +81,7 @@ void CameraNormal::smoothMoveCamera(float dt)
const KartProperties *kp = m_kart->getKartProperties();
float max_increase_with_zipper = kp->getZipperMaxSpeedIncrease();
float max_speed_without_zipper = kp->getEngineMaxSpeed();
float current_speed = m_kart->getSpeed();
float current_speed = m_kart->getSmoothedSpeed();
const Skidding *ks = m_kart->getSkidding();
float skid_factor = ks->getVisualSkidRotation();
@ -106,10 +106,10 @@ void CameraNormal::smoothMoveCamera(float dt)
core::vector3df wanted_position = m_kart_camera_position_with_offset.toIrrVector();
float f = 5.0f;
if ((m_kart->getSpeed() > 5 ) || (m_kart->getSpeed() < 0 ))
if ((current_speed > 5 ) || (current_speed < 0 ))
{
f = m_kart->getSpeed()>0 ? m_kart->getSpeed()/3 + 1.0f
: -1.5f * m_kart->getSpeed() + 2.0f;
f = current_speed >0 ? current_speed/3 + 1.0f
: -1.5f * current_speed + 2.0f;
}
current_position += (wanted_position - current_position) * (dt *f);

View File

@ -219,7 +219,7 @@ void IrrDriver::computeMatrixesAndCameras(scene::ICameraSceneNode *const camnode
float w = width * UserConfigParams::m_scale_rtts_factor;
float h = height * UserConfigParams::m_scale_rtts_factor;
m_current_screen_size = core::vector2df(w, h);
m_shadow_matrices->computeMatrixesAndCameras(camnode, w, h);
m_shadow_matrices->computeMatrixesAndCameras(camnode, int(w), int(h));
} // computeMatrixesAndCameras
// ----------------------------------------------------------------------------
@ -1600,7 +1600,7 @@ std::string IrrDriver::getSmallerTexture(const std::string& filename)
{
core::dimension2d<u32> dim = img->getDimension();
core::dimension2d<u32> new_dim; // Dimension of the cached texture
const int scale_factor = 2;
const unsigned scale_factor = 2;
// Resize the texture only if it can be done properly
if (dim.Width < scale_factor || dim.Height < scale_factor)
new_dim = dim;

View File

@ -1319,9 +1319,9 @@ void multidraw2ndPass(const std::vector<uint64_t> &Handles, Args... args)
T::InstancedSecondPassShader::getInstance()->use();
glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(T::VertexType,
T::Instance));
uint64_t nulltex[10] = {};
if (SolidPassCmd::getInstance()->Size[T::MaterialType])
{
uint64_t nulltex[10] = {};
HandleExpander<typename T::InstancedSecondPassShader>::template
Expand(nulltex, T::SecondPassTextures, Handles[0], Handles[1],
Handles[2]);
@ -1410,9 +1410,9 @@ void IrrDriver::renderSolidSecondPass()
GrassMat::InstancedSecondPassShader::getInstance()->use();
glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(GrassMat::VertexType,
GrassMat::Instance));
uint64_t nulltex[10] = {};
if (SolidPassCmd::getInstance()->Size[GrassMat::MaterialType])
{
uint64_t nulltex[10] = {};
HandleExpander<GrassMat::InstancedSecondPassShader>
::Expand(nulltex, GrassMat::SecondPassTextures, DiffuseHandle,
SpecularHandle, SSAOHandle, DepthHandle);

View File

@ -314,20 +314,6 @@ void ShadowMatrices::computeMatrixesAndCameras(scene::ICameraSceneNode *const ca
const float oldfar = camnode->getFarValue();
const float oldnear = camnode->getNearValue();
float FarValues[] =
{
ShadowMatrices::m_shadow_split[1],
ShadowMatrices::m_shadow_split[2],
ShadowMatrices::m_shadow_split[3],
ShadowMatrices::m_shadow_split[4],
};
float NearValues[] =
{
ShadowMatrices::m_shadow_split[0],
ShadowMatrices::m_shadow_split[1],
ShadowMatrices::m_shadow_split[2],
ShadowMatrices::m_shadow_split[3]
};
float tmp[16 * 9 + 2];
memcpy(tmp, irr_driver->getViewMatrix().pointer(), 16 * sizeof(float));
@ -359,6 +345,21 @@ void ShadowMatrices::computeMatrixesAndCameras(scene::ICameraSceneNode *const ca
core::aabbox3df trackbox(vmin.toIrrVector(), vmax.toIrrVector() -
core::vector3df(0, 30, 0));
float FarValues[] =
{
ShadowMatrices::m_shadow_split[1],
ShadowMatrices::m_shadow_split[2],
ShadowMatrices::m_shadow_split[3],
ShadowMatrices::m_shadow_split[4],
};
float NearValues[] =
{
ShadowMatrices::m_shadow_split[0],
ShadowMatrices::m_shadow_split[1],
ShadowMatrices::m_shadow_split[2],
ShadowMatrices::m_shadow_split[3]
};
// Shadow Matrixes and cameras
for (unsigned i = 0; i < 4; i++)
{

View File

@ -723,7 +723,7 @@ PROFILER_POP_CPU_MARKER();
ListInstancedMatDetails::getInstance()->clear();
ListInstancedMatUnlit::getInstance()->clear();
size_t SolidPoly = 0, ShadowPoly = 0, MiscPoly = 0;
size_t SolidPoly = 0, ShadowPoly = 0;
PROFILER_PUSH_CPU_MARKER("- Draw Command upload", 0xFF, 0x0, 0xFF);
@ -868,6 +868,8 @@ PROFILER_POP_CPU_MARKER();
RSMCmdBuffer = (DrawElementsIndirectCommand*)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, 10000 * sizeof(DrawElementsIndirectCommand), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
}
size_t MiscPoly = 0;
// Default Material
RSMPassCmd::getInstance()->Offset[Material::SHADERTYPE_SOLID] = current_cmd;
FillInstances(MeshForRSM[Material::SHADERTYPE_SOLID], ListInstancedMatDefault::getInstance()->RSM, RSMInstanceBuffer, RSMCmdBuffer, offset, current_cmd, MiscPoly);

View File

@ -387,7 +387,7 @@ const bool NAVIGATION_DEBUG = false;
*/
void EventHandler::navigate(const int playerID, Input::InputType type, const bool pressedDown, const bool reverse)
{
IGUIElement *el = NULL, *closest = NULL;
IGUIElement *el = NULL;
if (type == Input::IT_STICKBUTTON && !pressedDown)
return;
@ -450,7 +450,7 @@ void EventHandler::navigate(const int playerID, Input::InputType type, const boo
// Down: if the current widget is e.g. 5, search for widget 6, 7, 8, 9, ..., 15 (up to 10 IDs may be missing)
for (int n = 1; n < 10 && !found; n++)
{
closest = GUIEngine::getGUIEnv()->getRootGUIElement()->getElementFromId(el->getTabOrder() + (reverse ? -n : n), true);
IGUIElement *closest = GUIEngine::getGUIEnv()->getRootGUIElement()->getElementFromId(el->getTabOrder() + (reverse ? -n : n), true);
if (closest != NULL && Widget::isFocusableId(closest->getID()))
{

View File

@ -927,7 +927,6 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
//if (widget->m_deactivated) return;
bool mark_selected = widget->isSelected(PLAYER_ID_GAME_MASTER);
bool always_show_selection = false;
IGUIElement* focusedElem = NULL;
if (GUIEngine::getFocusForPlayer(PLAYER_ID_GAME_MASTER) != NULL)
@ -1007,6 +1006,7 @@ void Skin::drawRibbonChild(const core::recti &rect, Widget* widget,
/* in combo ribbons, always show selection */
RibbonWidget* parentRibbonWidget = NULL;
bool always_show_selection = false;
if (widget->m_event_handler != NULL &&
widget->m_event_handler->m_type == WTYPE_RIBBON)

View File

@ -1321,11 +1321,10 @@ void CGUIEditBox::breakText()
s32 size = Text.size();
s32 length = 0;
s32 elWidth = RelativeRect.getWidth() - 6;
wchar_t c;
for (s32 i=0; i<size; ++i)
{
c = Text[i];
wchar_t c = Text[i];
bool lineBreak = false;
if (c == L'\r') // Mac or Windows breaks

View File

@ -210,7 +210,6 @@ void DynamicRibbonWidget::add()
// const int count = m_items.size();
m_row_amount = -1;
float max_score_so_far = -1;
if (m_h - m_label_height < 0)
{
@ -219,6 +218,7 @@ void DynamicRibbonWidget::add()
}
else
{
float max_score_so_far = -1;
for (int row_count = 1; row_count < 10; row_count++)
{
int visible_items;

View File

@ -34,6 +34,7 @@
#include "modes/demo_world.hpp"
#include "modes/profile_world.hpp"
#include "modes/world.hpp"
#include "network/rewind_manager.hpp"
#include "physics/physics.hpp"
#include "race/history.hpp"
#include "replay/replay_recorder.hpp"
@ -100,6 +101,8 @@ InputManager::~InputManager()
void InputManager::handleStaticAction(int key, int value)
{
static bool control_is_pressed = false;
static bool shift_is_pressed = false;
World *world = World::getWorld();
// When no players... a cutscene
@ -149,6 +152,10 @@ void InputManager::handleStaticAction(int key, int value)
case KEY_LWIN:
control_is_pressed = value!=0;
break;
case KEY_LSHIFT:
case KEY_RSHIFT:
case KEY_SHIFT:
shift_is_pressed = value!=0; break;
// Flying up and down
case KEY_KEY_I:
@ -267,9 +274,22 @@ void InputManager::handleStaticAction(int key, int value)
if (value ==0 )
irr_driver->requestScreenshot();
break;
case KEY_F11:
if(value && shift_is_pressed && world && RewindManager::isEnabled())
{
printf("Enter rewind to time:");
char s[256];
fgets(s, 256, stdin);
float t;
StringUtils::fromString(s,t);
RewindManager::get()->rewindTo(t);
Log::info("Rewind", "Rewinding from %f to %f",
world->getTime(), t);
}
break;
/*
case KEY_F1:
if (UserConfigParams::m_artist_debug_mode && world)
else if (UserConfigParams::m_artist_debug_mode && world)
{
AbstractKart* kart = world->getLocalPlayerKart(0);

View File

@ -143,7 +143,6 @@ void WiimoteManager::launchDetection(int timeout)
//To prevent segmentation fault, have to delete NULLs
number_deletables = 0;
number_deletables = 0;
deletable_wiimotes = (wiimote_t**) malloc(sizeof(struct wiimote_t*) * (number_previous_wiimotes-number_merged_wiimotes));
memset(deletable_wiimotes,0,sizeof(struct wiimote_t*) * (number_previous_wiimotes-number_merged_wiimotes));
for (int i = 0; i < number_previous_wiimotes; i++)

View File

@ -36,6 +36,7 @@
#include "karts/kart_properties.hpp"
#include "modes/three_strikes_battle.hpp"
#include "modes/world.hpp"
#include "network/rewind_manager.hpp"
#include "physics/triangle_mesh.hpp"
#include "tracks/track.hpp"
#include "physics/triangle_mesh.hpp"
@ -45,6 +46,7 @@
/** Initialises the attachment each kart has.
*/
Attachment::Attachment(AbstractKart* kart)
: EventRewinder()
{
m_type = ATTACH_NOTHING;
m_time_left = 0.0;
@ -106,7 +108,7 @@ void Attachment::set(AttachmentType type, float time,
{
bool was_bomb = (m_type == ATTACH_BOMB);
scene::ISceneNode* bomb_scene_node = NULL;
if (was_bomb && type == ATTACH_SWATTER) //What about ATTACH_NOLOKS_SWATTER ??
if (was_bomb && type == ATTACH_SWATTER)
{
// let's keep the bomb node, and create a new one for
// the new attachment
@ -182,6 +184,15 @@ void Attachment::set(AttachmentType type, float time,
m_node->setVisible(true);
irr_driver->applyObjectPassShader(m_node);
// Save event about the new attachment
RewindManager *rwm = RewindManager::get();
if(rwm->isEnabled() && !rwm->isRewinding())
{
BareNetworkString *buffer = new BareNetworkString(2);
saveState(buffer);
rwm->addEvent(this, buffer);
}
} // set
// -----------------------------------------------------------------------------
@ -216,6 +227,76 @@ void Attachment::clear()
m_kart->updateWeight();
} // clear
// -----------------------------------------------------------------------------
/** Saves the attachment state. Called as part of the kart saving its state.
* \param buffer The kart rewinder's state buffer.
*/
void Attachment::saveState(BareNetworkString *buffer) const
{
// We use bit 7 to indicate if a previous owner is defined for a bomb
assert(ATTACH_MAX<=127);
uint8_t type = m_type | (( (m_type==ATTACH_BOMB) && (m_previous_owner!=NULL) )
? 0x80 : 0 );
buffer->addUInt8(type);
if(m_type!=ATTACH_NOTHING)
{
buffer->addFloat(m_time_left);
if(m_type==ATTACH_BOMB && m_previous_owner)
buffer->addUInt8(m_previous_owner->getWorldKartId());
// m_initial_speed is not saved, on restore state it will
// be set to the kart speed, which has already been restored
}
} // saveState
// -----------------------------------------------------------------------------
/** Called from the kart rewinder when resetting to a certain state.
* \param buffer The kart rewinder's buffer with the attachment state next.
*/
void Attachment::rewindTo(BareNetworkString *buffer)
{
uint8_t type = buffer->getUInt8();
AttachmentType new_type = AttachmentType(type & 0x7f); // mask out bit 7
// If there is no attachment, clear the attachment if necessary and exit
if(new_type==ATTACH_NOTHING)
{
if(m_type!=new_type) clear();
return;
}
float time_left = buffer->getFloat();
// Attaching an object can be expensive (loading new models, ...)
// so avoid doing this if there is no change in attachment type
if(new_type == m_type)
{
setTimeLeft(time_left);
return;
}
// Now it is a new attachment:
if (type == (ATTACH_BOMB | 0x80)) // we have previous owner information
{
uint8_t kart_id = buffer->getUInt8();
m_previous_owner = World::getWorld()->getKart(kart_id);
}
else
{
m_previous_owner = NULL;
}
set(new_type, time_left, m_previous_owner);
} // rewindTo
// -----------------------------------------------------------------------------
/** Called when going forwards in time during a rewind.
* \param buffer Buffer with the rewind information.
*/
void Attachment::rewind(BareNetworkString *buffer)
{
// Event has same info as a state, so re-use the restore function
rewindTo(buffer);
} // rewind
// -----------------------------------------------------------------------------
/** Randomly selects the new attachment. For a server process, the
* attachment can be passed into this function.
@ -440,9 +521,13 @@ void Attachment::update(float dt)
case ATTACH_MAX:
break;
case ATTACH_SWATTER:
case ATTACH_NOLOKS_SWATTER:
// Everything is done in the plugin.
break;
case ATTACH_NOLOKS_SWATTER:
// Should never be called, this symbols is only used as an index for
// the model, Nolok's attachment type is ATTACH_SWATTER
assert(false);
break;
case ATTACH_BOMB:
if (m_bomb_sound) m_bomb_sound->setPosition(m_kart->getXYZ());

View File

@ -21,6 +21,7 @@
#include "config/stk_config.hpp"
#include "items/attachment_plugin.hpp"
#include "network/event_rewinder.hpp"
#include "utils/no_copy.hpp"
#include "utils/random_generator.hpp"
@ -28,6 +29,7 @@
using namespace irr;
class AbstractKart;
class BareNetworkString;
class Item;
class SFXBase;
@ -44,7 +46,8 @@ class SFXBase;
* a scene node).
* \ingroup items
*/
class Attachment: public NoCopy, public scene::IAnimationEndCallBack
class Attachment: public NoCopy, public scene::IAnimationEndCallBack,
public EventRewinder
{
public:
// Some loop in attachment.cpp depend on ATTACH_FIRST and ATTACH_MAX.
@ -57,6 +60,8 @@ public:
ATTACH_BOMB,
ATTACH_ANVIL,
ATTACH_SWATTER,
// Note that the next symbol is only used as an index into the mesh
// array; it will NEVER be actually assigned as an attachment type
ATTACH_NOLOKS_SWATTER,
ATTACH_TINYTUX,
ATTACH_BUBBLEGUM_SHIELD,
@ -111,6 +116,9 @@ public:
void handleCollisionWithKart(AbstractKart *other);
void set (AttachmentType type, float time,
AbstractKart *previous_kart=NULL);
virtual void rewind(BareNetworkString *buffer);
void rewindTo(BareNetworkString *buffer);
void saveState(BareNetworkString *buffer) const;
// ------------------------------------------------------------------------
/** Sets the type of the attachment, but keeps the old time left value. */
@ -137,6 +145,10 @@ public:
// ------------------------------------------------------------------------
/** Implement IAnimatedMeshSceneNode */
virtual void OnAnimationEnd(scene::IAnimatedMeshSceneNode* node);
// ------------------------------------------------------------------------
/** Nothing to undo when going back during a rewind, the full state info
* will take care of creating the right attachment. */
virtual void undo(BareNetworkString *buffer) { }
}; // Attachment
#endif

View File

@ -41,7 +41,7 @@ Bowling::Bowling(AbstractKart *kart)
float y_offset = 0.5f*kart->getKartLength() + m_extend.getZ()*0.5f;
// if the kart is looking backwards, release from the back
if( kart->getControls().m_look_back )
if( kart->getControls().getLookBack())
{
y_offset = -y_offset;
m_speed = -m_speed*2;

View File

@ -67,7 +67,7 @@ Cake::Cake (AbstractKart *kart) : Flyable(kart, PowerupManager::POWERUP_CAKE)
float pitch = kart->getTerrainPitch(heading);
// Find closest kart in front of the current one
const bool backwards = kart->getControls().m_look_back;
const bool backwards = kart->getControls().getLookBack();
const AbstractKart *closest_kart=NULL;
Vec3 direction;
float kart_dist_squared;

View File

@ -25,11 +25,6 @@
* Defines the various collectibles and weapons of STK.
*/
namespace irr
{
namespace scene { class IMesh; class ISceneNode; }
}
using namespace irr;
#include "utils/leak_check.hpp"
#include "utils/no_copy.hpp"
@ -38,8 +33,14 @@ using namespace irr;
#include <line2d.h>
class AbstractKart;
class LODNode;
class Item;
class LODNode;
namespace irr
{
namespace scene { class IMesh; class ISceneNode; }
}
using namespace irr;
// -----------------------------------------------------------------------------

View File

@ -48,7 +48,7 @@ Plunger::Plunger(AbstractKart *kart)
float plunger_speed = 2 * m_speed;
// if the kart is looking backwards, release from the back
m_reverse_mode = kart->getControls().m_look_back;
m_reverse_mode = kart->getControls().getLookBack();
// find closest kart in front of the current one
const AbstractKart *closest_kart=0;

View File

@ -43,8 +43,8 @@
*/
Powerup::Powerup(AbstractKart* kart)
{
m_owner = kart;
m_sound_use = NULL;
m_kart = kart;
m_sound_use = NULL;
reset();
} // Powerup
@ -69,6 +69,45 @@ void Powerup::reset()
set( (PowerupManager::PowerupType)type, number );
} // reset
//-----------------------------------------------------------------------------
/** Save the powerup state. Called from the kart rewinder when saving the kart
* state or when a new powerup even is saved.
* \param buffer The buffer into which to save the state.
*/
void Powerup::saveState(BareNetworkString *buffer) const
{
buffer->addUInt8(uint8_t(m_type));
if(m_type!=PowerupManager::POWERUP_NOTHING)
{
buffer->addUInt8(m_number); // number is <=255
}
} // saveState
//-----------------------------------------------------------------------------
/** Restore a powerup state. Called from the kart rewinder when restoring a
* state.
* \param buffer Buffer with the state of this powerup object.
*/
void Powerup::rewindTo(BareNetworkString *buffer)
{
PowerupManager::PowerupType new_type =
PowerupManager::PowerupType(buffer->getUInt8());
int n=0;
if(new_type==PowerupManager::POWERUP_NOTHING)
{
set(new_type, 0);
return;
}
n = buffer->getUInt8();
if(m_type == new_type)
m_number = n;
else
{
m_number = 0;
set(new_type, n);
}
} // rewindTo
//-----------------------------------------------------------------------------
/** Sets the collected items. The number of items is increased if the same
* item is currently collected, otherwise replaces the existing item. It also
@ -81,9 +120,15 @@ void Powerup::set(PowerupManager::PowerupType type, int n)
if (m_type==type)
{
m_number+=n;
// Limit to 255 (save space in network state saving)
if(m_number>255) m_number = 255;
return;
}
m_type=type;
// Limit to 255 (save space in network state saving)
if(n>255) n = 255;
m_number=n;
if(m_sound_use != NULL)
@ -147,14 +192,14 @@ Material *Powerup::getIcon() const
*/
void Powerup::adjustSound()
{
m_sound_use->setPosition(m_owner->getXYZ());
m_sound_use->setPosition(m_kart->getXYZ());
// in multiplayer mode, sounds are NOT positional (because we have multiple listeners)
// so the sounds of all AIs are constantly heard. So reduce volume of sounds.
if (race_manager->getNumLocalPlayers() > 1)
{
// player karts played at full volume; AI karts much dimmer
if (m_owner->getController()->isLocalPlayerController())
if (m_kart->getController()->isLocalPlayerController())
{
m_sound_use->setVolume( 1.0f );
}
@ -171,11 +216,11 @@ void Powerup::adjustSound()
*/
void Powerup::use()
{
const KartProperties *kp = m_owner->getKartProperties();
const KartProperties *kp = m_kart->getKartProperties();
// The player gets an achievement point for using a powerup
if (m_type != PowerupManager::POWERUP_NOTHING &&
m_owner->getController()->canGetAchievements() )
m_kart->getController()->canGetAchievements() )
{
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_POWERUP_LOVER, "poweruplover");
}
@ -184,7 +229,7 @@ void Powerup::use()
if (m_type != PowerupManager::POWERUP_NOTHING &&
m_type != PowerupManager::POWERUP_SWATTER &&
m_type != PowerupManager::POWERUP_ZIPPER)
m_owner->playCustomSFX(SFXManager::CUSTOM_SHOOT);
m_kart->playCustomSFX(SFXManager::CUSTOM_SHOOT);
// FIXME - for some collectibles, set() is never called
if(m_sound_use == NULL)
@ -199,12 +244,12 @@ void Powerup::use()
switch (m_type)
{
case PowerupManager::POWERUP_ZIPPER:
m_owner->handleZipper(NULL, true);
m_kart->handleZipper(NULL, true);
break ;
case PowerupManager::POWERUP_SWITCH:
{
ItemManager::get()->switchItems();
m_sound_use->setPosition(m_owner->getXYZ());
m_sound_use->setPosition(m_kart->getXYZ());
m_sound_use->play();
break;
}
@ -213,28 +258,29 @@ void Powerup::use()
case PowerupManager::POWERUP_BOWLING:
case PowerupManager::POWERUP_PLUNGER:
if(stk_config->m_shield_restrict_weapos)
m_owner->setShieldTime(0.0f); // make weapon usage destroy the shield
m_kart->setShieldTime(0.0f); // make weapon usage destroy the shield
Powerup::adjustSound();
m_sound_use->play();
projectile_manager->newProjectile(m_owner, m_type);
projectile_manager->newProjectile(m_kart, m_type);
break ;
case PowerupManager::POWERUP_SWATTER:
m_owner->getAttachment()
m_kart->getAttachment()
->set(Attachment::ATTACH_SWATTER, kp->getSwatterDuration());
break;
case PowerupManager::POWERUP_BUBBLEGUM:
// use the bubble gum the traditional way, if the kart is looking back
if (m_owner->getControls().m_look_back)
if (m_kart->getControls().getLookBack())
{
Vec3 hit_point;
Vec3 normal;
const Material* material_hit;
world->getTrack()->getTriangleMesh().castRay(m_owner->getXYZ(),
m_owner->getTrans().getBasis() * Vec3(0, -10000, 0), &hit_point,
&material_hit, &normal);
Vec3 pos = m_kart->getXYZ();
Vec3 to = pos+ m_kart->getTrans().getBasis() * Vec3(0, -10000, 0);
world->getTrack()->getTriangleMesh().castRay(pos, to, &hit_point,
&material_hit, &normal);
// This can happen if the kart is 'over nothing' when dropping
// the bubble gum
if(!material_hit)
@ -244,36 +290,36 @@ void Powerup::use()
Powerup::adjustSound();
m_sound_use->play();
Vec3 pos = hit_point + m_owner->getTrans().getBasis() * Vec3(0, -0.05f, 0);
ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos, normal, m_owner);
pos = hit_point + m_kart->getTrans().getBasis() * Vec3(0, -0.05f, 0);
ItemManager::get()->newItem(Item::ITEM_BUBBLEGUM, pos, normal, m_kart);
}
else // if the kart is looking forward, use the bubblegum as a shield
{
if(!m_owner->isShielded()) //if the previous shield had been used up.
if(!m_kart->isShielded()) //if the previous shield had been used up.
{
if (m_owner->getIdent() == "nolok")
if (m_kart->getIdent() == "nolok")
{
m_owner->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD,
m_kart->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD,
kp->getBubblegumShieldDuration());
}
else
{
m_owner->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD,
m_kart->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD,
kp->getBubblegumShieldDuration());
}
}
else // using a bubble gum while still having a shield
{
if (m_owner->getIdent() == "nolok")
if (m_kart->getIdent() == "nolok")
{
m_owner->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD,
kp->getBubblegumShieldDuration() + m_owner->getShieldTime());
m_kart->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD,
kp->getBubblegumShieldDuration() + m_kart->getShieldTime());
}
else
{
m_owner->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD,
kp->getBubblegumShieldDuration() + m_owner->getShieldTime());
m_kart->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD,
kp->getBubblegumShieldDuration() + m_kart->getShieldTime());
}
}
@ -293,7 +339,7 @@ void Powerup::use()
{
AbstractKart *kart=world->getKart(i);
if(kart->isEliminated() || kart->isInvulnerable()) continue;
if(kart == m_owner) continue;
if(kart == m_kart) continue;
if(kart->getPosition() == 1)
{
kart->getAttachment()->set(Attachment::ATTACH_ANVIL,
@ -308,7 +354,7 @@ void Powerup::use()
if(kart->getController()->isLocalPlayerController())
m_sound_use->setPosition(kart->getXYZ());
else
m_sound_use->setPosition(m_owner->getXYZ());
m_sound_use->setPosition(m_kart->getXYZ());
m_sound_use->play();
break;
@ -326,13 +372,13 @@ void Powerup::use()
for(unsigned int i = 0 ; i < world->getNumKarts(); ++i)
{
AbstractKart *kart=world->getKart(i);
if(kart->isEliminated() || kart== m_owner || kart->isInvulnerable()) continue;
if(kart->isEliminated() || kart== m_kart || kart->isInvulnerable()) continue;
if(kart->isShielded())
{
kart->decreaseShieldTime();
continue;
}
if(m_owner->getPosition() > kart->getPosition())
if(m_kart->getPosition() > kart->getPosition())
{
kart->getAttachment()->set(Attachment::ATTACH_PARACHUTE,
kp->getParachuteDurationOther());
@ -346,8 +392,8 @@ void Powerup::use()
// or the kart "throwing" the anvil? Ideally it should be both.
// Meanwhile, don't play it near AI karts since they obviously
// don't hear anything
if(m_owner->getController()->isLocalPlayerController())
m_sound_use->setPosition(m_owner->getXYZ());
if(m_kart->getController()->isLocalPlayerController())
m_sound_use->setPosition(m_kart->getXYZ());
else if(player_kart)
m_sound_use->setPosition(player_kart->getXYZ());
m_sound_use->play();
@ -356,8 +402,8 @@ void Powerup::use()
case PowerupManager::POWERUP_NOTHING:
{
if(!m_owner->getKartAnimation())
m_owner->beep();
if(!m_kart->getKartAnimation())
m_kart->beep();
}
break;
default : break;
@ -387,7 +433,7 @@ void Powerup::hitBonusBox(const Item &item, int add_info)
{
// Position can be -1 in case of a battle mode (which doesn't have
// positions), but this case is properly handled in getRandomPowerup.
int position = m_owner->getPosition();
int position = m_kart->getPosition();
unsigned int n=1;
PowerupManager::PowerupType new_powerup;

View File

@ -26,6 +26,7 @@
#include "utils/random_generator.hpp"
class AbstractKart;
class BareNetworkString;
class Item;
class SFXBase;
@ -48,7 +49,7 @@ private:
int m_number;
/** The owner (kart) of this powerup. */
AbstractKart* m_owner;
AbstractKart* m_kart;
public:
Powerup (AbstractKart* kart_);
@ -56,9 +57,12 @@ public:
void set (PowerupManager::PowerupType _type, int n=1);
void reset ();
Material* getIcon () const;
void adjustSound ();
void adjustSound ();
void use ();
void hitBonusBox (const Item &item, int newC=-1);
void saveState(BareNetworkString *buffer) const;
void rewindTo(BareNetworkString *buffer);
/** Returns the number of powerups. */
int getNum () const {return m_number;}

View File

@ -19,20 +19,19 @@
#ifndef HEADER_POWERUPMANAGER_HPP
#define HEADER_POWERUPMANAGER_HPP
namespace irr
{
namespace scene { class IMesh; }
}
#include "utils/no_copy.hpp"
#include "btBulletDynamicsCommon.h"
#include <string>
#include <vector>
#include "btBulletDynamicsCommon.h"
#include "utils/no_copy.hpp"
class Material;
class XMLNode;
namespace irr
{
namespace scene { class IMesh; }
}
/**
* \ingroup items

View File

@ -110,16 +110,13 @@ public:
// Functions related to controlling the kart
// ------------------------------------------------------------------------
/** Returns the current steering value for this kart. */
float getSteerPercent() const { return m_controls.m_steer; }
float getSteerPercent() const { return m_controls.getSteer(); }
// ------------------------------------------------------------------------
/** Returns all controls of this kart. */
KartControl& getControls() { return m_controls; }
// ------------------------------------------------------------------------
/** Returns all controls of this kart - const version. */
const KartControl& getControls() const { return m_controls; }
// ------------------------------------------------------------------------
/** Sets the kart controls. Used e.g. by replaying history. */
void setControls(const KartControl &c) { m_controls = c; }
// ========================================================================
// Access to the kart properties.
@ -278,6 +275,10 @@ public:
* like Ghost. */
virtual float getSpeed() const = 0;
// ------------------------------------------------------------------------
/** Returns the exponentially smoothened speed of the kart in
* which is removes shaking from camera. */
virtual float getSmoothedSpeed() const = 0;
// ------------------------------------------------------------------------
/** Returns the current maximum speed for this kart, this includes all
* bonus and maluses that are currently applied. */
virtual float getCurrentMaxSpeed() const = 0;

View File

@ -184,11 +184,11 @@ void AIBaseController::setSteering(float angle, float dt)
{
float steer_fraction = angle / m_kart->getMaxSteerAngle();
if(!canSkid(steer_fraction))
m_controls->m_skid = KartControl::SC_NONE;
m_controls->setSkidControl(KartControl::SC_NONE);
else
m_controls->m_skid = steer_fraction > 0 ? KartControl::SC_RIGHT
: KartControl::SC_LEFT;
float old_steer = m_controls->m_steer;
m_controls->setSkidControl(steer_fraction > 0 ? KartControl::SC_RIGHT
: KartControl::SC_LEFT );
float old_steer = m_controls->getSteer();
if (steer_fraction > 1.0f) steer_fraction = 1.0f;
else if(steer_fraction < -1.0f) steer_fraction = -1.0f;
@ -203,13 +203,13 @@ void AIBaseController::setSteering(float angle, float dt)
float max_steer_change = dt/m_ai_properties->m_time_full_steer;
if(old_steer < steer_fraction)
{
m_controls->m_steer = (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change;
m_controls->setSteer(( old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change);
}
else
{
m_controls->m_steer = (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change;
m_controls->setSteer( (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change );
}
} // setSteering

View File

@ -78,8 +78,8 @@ void ArenaAI::reset()
void ArenaAI::update(float dt)
{
// This is used to enable firing an item backwards.
m_controls->m_look_back = false;
m_controls->m_nitro = false;
m_controls->setLookBack(false);
m_controls->setNitro(false);
// Don't do anything if there is currently a kart animations shown.
if (m_kart->getKartAnimation())
@ -129,8 +129,8 @@ void ArenaAI::update(float dt)
if (m_kart->getSpeed() > 15.0f && !m_is_uturn && m_turn_radius > 30.0f &&
!ignorePathFinding())
{
// Only use nitro when turn radius is large
m_controls->m_nitro = true;
// Only use nitro when turn angle is big (180 - angle)
m_controls->setNitro(true);
}
if (m_is_uturn)
@ -323,27 +323,27 @@ void ArenaAI::checkIfStuck(const float dt)
*/
void ArenaAI::configSpeed()
{
m_controls->m_accel = 0.0f;
m_controls->m_brake = false;
m_controls->setAccel(0.0f);
m_controls->setBrake(false);
// A kart will not brake when the speed is already slower than this
// value. This prevents a kart from going too slow (or even backwards)
// in tight curves.
const float MIN_SPEED = 5.0f;
const float handicap =
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 0.7f : 1.0f);
const float handicap = (m_cur_difficulty == RaceManager::DIFFICULTY_EASY
? 0.7f : 1.0f );
const float max_turn_speed = m_kart->getSpeedForTurnRadius(m_turn_radius);
if ((m_kart->getSpeed() > max_turn_speed || forceBraking()) &&
m_kart->getSpeed() > MIN_SPEED * handicap)
{
// Brake if necessary
m_controls->m_brake = true;
m_controls->setBrake(true);
}
else
{
// Otherwise accelerate
m_controls->m_accel = stk_config->m_ai_acceleration * handicap;
m_controls->setAccel(stk_config->m_ai_acceleration * handicap);
}
} // configSpeed
@ -355,9 +355,9 @@ void ArenaAI::doUTurn(const float dt)
if (fabsf(m_kart->getSpeed()) >
(m_kart->getKartProperties()->getEngineMaxSpeed() / 5)
&& m_kart->getSpeed() < 0) // Try to emulate reverse like human players
m_controls->m_accel = -0.06f;
m_controls->setAccel(-0.06f);
else
m_controls->m_accel = -5.0f;
m_controls->setAccel(-5.0f);
if (m_time_since_uturn >=
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 2.0f : 1.5f))
@ -389,9 +389,9 @@ bool ArenaAI::gettingUnstuck(const float dt)
if (fabsf(m_kart->getSpeed()) >
(m_kart->getKartProperties()->getEngineMaxSpeed() / 5)
&& m_kart->getSpeed() < 0)
m_controls->m_accel = -0.06f;
m_controls->setAccel(-0.06f);
else
m_controls->m_accel = -4.0f;
m_controls->setAccel(-4.0f);
m_time_since_reversing += dt;
@ -408,7 +408,7 @@ bool ArenaAI::gettingUnstuck(const float dt)
//-----------------------------------------------------------------------------
void ArenaAI::useItems(const float dt)
{
m_controls->m_fire = false;
m_controls->setFire(false);
if (m_kart->getKartAnimation() ||
m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING)
return;
@ -443,24 +443,21 @@ void ArenaAI::useItems(const float dt)
{
Attachment::AttachmentType type = m_kart->getAttachment()->getType();
// Don't use shield when we have a swatter.
if (type == Attachment::ATTACH_SWATTER ||
type == Attachment::ATTACH_NOLOKS_SWATTER)
if (type == Attachment::ATTACH_SWATTER)
break;
// Check if a flyable (cake, ...) is close or a kart nearby
// has a swatter attachment. If so, use bubblegum
// as shield
if ((!m_kart->isShielded() &&
projectile_manager->projectileIsClose(m_kart,
m_ai_properties->m_shield_incoming_radius)) ||
(dist_to_kart < 15.0f &&
((m_closest_kart->getAttachment()->
getType() == Attachment::ATTACH_SWATTER) ||
(m_closest_kart->getAttachment()->
getType() == Attachment::ATTACH_NOLOKS_SWATTER))))
if ( (!m_kart->isShielded() &&
projectile_manager->projectileIsClose(m_kart,
m_ai_properties->m_shield_incoming_radius) ) ||
(dist_to_kart < 15.0f &&
(m_closest_kart->getAttachment()->
getType() == Attachment::ATTACH_SWATTER) ) )
{
m_controls->m_fire = true;
m_controls->m_look_back = false;
m_controls->setFire(true);
m_controls->setLookBack(false);
break;
}
@ -471,8 +468,8 @@ void ArenaAI::useItems(const float dt)
// or can't find a close kart for too long time
if (dist_to_kart < 15.0f || m_time_since_last_shot > 15.0f)
{
m_controls->m_fire = true;
m_controls->m_look_back = true;
m_controls->setFire(true);
m_controls->setLookBack(true);
break;
}
@ -490,8 +487,8 @@ void ArenaAI::useItems(const float dt)
if (dist_to_kart < 25.0f &&
!m_closest_kart->isInvulnerable())
{
m_controls->m_fire = true;
m_controls->m_look_back = fire_behind;
m_controls->setFire(true);
m_controls->setLookBack(fire_behind);
break;
}
@ -511,8 +508,8 @@ void ArenaAI::useItems(const float dt)
(difficulty || perfect_aim) &&
!m_closest_kart->isInvulnerable())
{
m_controls->m_fire = true;
m_controls->m_look_back = fire_behind;
m_controls->setFire(true);
m_controls->setLookBack(fire_behind);
break;
}
@ -531,8 +528,8 @@ void ArenaAI::useItems(const float dt)
dist_to_kart * dist_to_kart < d2 &&
m_closest_kart->getSpeed() < m_kart->getSpeed())
{
m_controls->m_fire = true;
m_controls->m_look_back = false;
m_controls->setFire(true);
m_controls->setLookBack(false);
break;
}
break;
@ -546,7 +543,7 @@ void ArenaAI::useItems(const float dt)
break; // POWERUP_PLUNGER
case PowerupManager::POWERUP_SWITCH: // Don't handle switch
m_controls->m_fire = true; // (use it no matter what) for now
m_controls->setFire(true); // (use it no matter what) for now
break; // POWERUP_SWITCH
case PowerupManager::POWERUP_PARACHUTE:
@ -564,7 +561,7 @@ void ArenaAI::useItems(const float dt)
m_kart->getPowerup()->getType());
assert(false);
}
if (m_controls->m_fire)
if (m_controls->getFire())
m_time_since_last_shot = 0.0f;
} // useItems

View File

@ -175,10 +175,10 @@ void EndController::action(PlayerAction action, int value)
void EndController::update(float dt)
{
// This is used to enable firing an item backwards.
m_controls->m_look_back = false;
m_controls->m_nitro = false;
m_controls->m_brake = false;
m_controls->m_accel = 1.0f;
m_controls->setLookBack(false);
m_controls->setNitro(false);
m_controls->setBrake(false);
m_controls->setAccel(1.0f);
AIBaseLapController::update(dt);
@ -187,10 +187,10 @@ void EndController::update(float dt)
race_manager->getMinorMode()==RaceManager::MINOR_MODE_SOCCER ||
race_manager->getMinorMode()==RaceManager::MINOR_MODE_EASTER_EGG)
{
m_controls->m_accel = 0.0f;
m_controls->setAccel(0.0f);
// Brake while we are still driving forwards (if we keep
// on braking, the kart will reverse otherwise)
m_controls->m_brake = m_kart->getSpeed()>0;
m_controls->setBrake(m_kart->getSpeed()>0);
return;
}
/*Get information that is needed by more than 1 of the handling funcs*/
@ -260,26 +260,24 @@ void EndController::handleRescue(const float DELTA)
void EndController::findNonCrashingPoint(Vec3 *result)
{
unsigned int sector = m_next_node_index[m_track_node];
int target_sector;
Vec3 direction;
Vec3 step_track_coord;
float distance;
int steps;
//We exit from the function when we have found a solution
while( 1 )
{
//target_sector is the sector at the longest distance that we can drive
//to without crashing with the track.
target_sector = m_next_node_index[sector];
int target_sector = m_next_node_index[sector];
//direction is a vector from our kart to the sectors we are testing
direction = DriveGraph::get()->getNode(target_sector)->getCenter()
- m_kart->getXYZ();
float len=direction.length();
steps = int( len / m_kart_length );
int steps = int( len / m_kart_length );
if( steps < 3 ) steps = 3;
//Protection against having vel_normal with nan values

View File

@ -54,7 +54,7 @@ void GhostController::update(float dt)
if(camera->getKart()!=m_kart) continue;
if (camera->getType() != Camera::CM_TYPE_END)
{
if (m_controls->m_look_back)
if (m_controls->getLookBack())
{
camera->setMode(Camera::CM_REVERSE);
}
@ -85,5 +85,5 @@ void GhostController::action(PlayerAction action, int value)
{
// Watching replay use only
if (action == PA_LOOK_BACK)
m_controls->m_look_back = (value!=0);
m_controls->setLookBack(value!=0);
} // action

View File

@ -0,0 +1,185 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2008-2016 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/controller/kart_control.hpp"
#include "network/rewind_manager.hpp"
// ------------------------------------------------------------------------
/** Called when going back in time during a rewind. Nothing to do here
* in this case.
*/
void KartControl::undo(BareNetworkString *buffer)
{
} // undo
// ------------------------------------------------------------------------
/** Replay an event for a KartControl object from the buffer.
* \param buffer BareNetworkString with saved event into.
*/
void KartControl::rewind(BareNetworkString *buffer)
{
if(buffer->getTotalSize()>1)
{
// Full state including accel and steering was saved
setFromBuffer(buffer);
}
else // only a button event was stored
{
setButtonsCompressed(buffer->getUInt8());
}
} // rewind
// ------------------------------------------------------------------------
/** Sets this KartControl form the given value (basically a copy). This
* function uses the explicit setSteer() etc function, which means that
* rewind information will be collected.
*/
void KartControl::set(const KartControl &c)
{
setAccel(c.getAccel());
setBrake(c.getBrake());
setFire(c.getFire());
setLookBack(c.getLookBack());
setNitro(c.getNitro());
setRescue(c.getRescue());
setSkidControl(c.getSkidControl());
setSteer(c.getSteer());
} // set
// ------------------------------------------------------------------------
/** Sets the current steering value. */
void KartControl::setSteer(float f)
{
float old_steer = m_steer;
m_steer = f;
if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() &&
old_steer != m_steer )
{
// Save full status
BareNetworkString *buffer = new BareNetworkString(getLength());
copyToBuffer(buffer);
RewindManager::get()->addEvent(this, buffer);
}
} // setSteer
// ----------------------------------------------------------------------------
/** Sets the acceleration. */
void KartControl::setAccel(float f)
{
float old_accel = m_accel;
m_accel = f;
if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() &&
old_accel != m_accel )
{
BareNetworkString *buffer = new BareNetworkString(getLength());
copyToBuffer(buffer);
RewindManager::get()->addEvent(this, buffer);
}
} // setAccel
// ----------------------------------------------------------------------------
/** Sets if the kart is braking. */
void KartControl::setBrake(bool b)
{
bool old_brake = m_brake;
m_brake = b;
if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() &&
old_brake != m_brake )
{
// Only store the buttons in this case
BareNetworkString *buffer = new BareNetworkString(1);
buffer->addUInt8(getButtonsCompressed());
RewindManager::get()->addEvent(this, buffer);
}
} // setBrake
// ----------------------------------------------------------------------------
/** Sets if the kart activates nitro. */
void KartControl::setNitro(bool b)
{
bool old_nitro = m_nitro;
m_nitro = b;
if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() &&
old_nitro != m_nitro )
{
BareNetworkString *buffer = new BareNetworkString(1);
buffer->addUInt8(getButtonsCompressed());
RewindManager::get()->addEvent(this, buffer);
}
} // setNitro
// ----------------------------------------------------------------------------
/** Sets the skid control for this kart. */
void KartControl::setSkidControl(SkidControl sc)
{
SkidControl old_skid = m_skid;
m_skid = sc;
if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() &&
old_skid != m_skid )
{
BareNetworkString *buffer = new BareNetworkString(1);
buffer->addUInt8(getButtonsCompressed());
RewindManager::get()->addEvent(this, buffer);
}
} // seSkidControl
// ----------------------------------------------------------------------------
/** Returns if this kart wants to get rescued. */
void KartControl::setRescue(bool b)
{
bool old_rescue = m_rescue;
m_rescue = b;
if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() &&
old_rescue != m_rescue)
{
BareNetworkString *buffer = new BareNetworkString(1);
buffer->addUInt8(getButtonsCompressed());
RewindManager::get()->addEvent(this, buffer);
}
} // setRescue
// ----------------------------------------------------------------------------
/** Sets if the kart wants to fire. */
void KartControl::setFire(bool b)
{
bool old_fire = m_fire;
m_fire = b;
if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() &&
old_fire != m_fire )
{
BareNetworkString *buffer = new BareNetworkString(1);
buffer->addUInt8(getButtonsCompressed());
RewindManager::get()->addEvent(this, buffer);
}
} // setFire
// ----------------------------------------------------------------------------
/** Sets if the kart wants to look (and therefore also fires) backwards. */
void KartControl::setLookBack(bool b)
{
bool old_look = m_look_back;
m_look_back = b;
if (RewindManager::isEnabled() && !RewindManager::get()->isRewinding() &&
old_look != m_look_back)
{
BareNetworkString *buffer = new BareNetworkString(1);
buffer->addUInt8(getButtonsCompressed());
RewindManager::get()->addEvent(this, buffer);
}
} // setLookBack

View File

@ -19,13 +19,21 @@
#ifndef HEADER_KART_CONTROL_HPP
#define HEADER_KART_CONTROL_HPP
#include "network/network_string.hpp"
#include "network/rewind_info.hpp"
/**
* \ingroup controller
*/
class KartControl
class KartControl : public EventRewinder
{
public:
/** The skidding control state: SC_NONE: not pressed;
* SC_NO_DIRECTION: pressed, but no steering;
* SC_LEFT/RIGHT: pressed in the specified direction. */
enum SkidControl {SC_NONE, SC_NO_DIRECTION, SC_LEFT, SC_RIGHT};
private:
/** The current steering value in [-1, 1]. */
float m_steer;
/** Acceleration, in [0, 1]. */
@ -34,18 +42,28 @@ public:
bool m_brake;
/** True if the kart activates nitro. */
bool m_nitro;
/** The skidding control state: SC_NONE: not pressed;
SC_NO_DIRECTION: pressed, but no steering;
SC_LEFT/RIGHT: pressed in the specified direction. */
enum SkidControl {SC_NONE, SC_NO_DIRECTION, SC_LEFT, SC_RIGHT}
m_skid;
/** Skidding control state. */
SkidControl m_skid;
/** True if rescue is selected. */
bool m_rescue;
/** True if fire is selected. */
bool m_fire;
/** True if the kart looks (and shoots) backwards. */
bool m_look_back;
public:
virtual void undo(BareNetworkString *buffer);
virtual void rewind(BareNetworkString *buffer);
void setSteer(float f);
void setAccel(float f);
void setBrake(bool b);
void setNitro(bool b);
void setSkidControl(SkidControl sc);
void setRescue(bool b);
void setFire(bool b);
void setLookBack(bool b);
void set(const KartControl &c);
// ------------------------------------------------------------------------
KartControl()
{
reset();
@ -64,14 +82,43 @@ public:
m_look_back = false;
} // reset
// ------------------------------------------------------------------------
void uncompress(char *c)
/** Tests if two KartControls are equal.
*/
bool operator==(const KartControl &other)
{
m_steer = ((float*)c)[0];
m_accel = ((float*)c)[1];
setButtonsCompressed(c[8]);
} // uncompress
return m_steer == other.m_steer &&
m_accel == other.m_accel &&
m_brake == other.m_brake &&
m_nitro == other.m_nitro &&
m_skid == other.m_skid &&
m_rescue == other.m_rescue &&
m_fire == other.m_fire &&
m_look_back == other.m_look_back;
} // operator==
// ------------------------------------------------------------------------
/** Compresses all buttons into a single integer value. */
/** Return the serialised size in bytes. */
static int getLength() { return 9; }
// ------------------------------------------------------------------------
/** Copies the important data from this objects into a memory buffer. */
void copyToBuffer(BareNetworkString *buffer) const
{
buffer->add(m_steer);
buffer->add(m_accel);
buffer->addChar(getButtonsCompressed());
} // copyToBuffer
// ------------------------------------------------------------------------
/** Restores this object from a previously saved memory buffer. */
void setFromBuffer(BareNetworkString *buffer)
{
m_steer = buffer->getFloat();
m_accel = buffer->getFloat();
setButtonsCompressed(buffer->getUInt8());
} // setFromMemory
// ------------------------------------------------------------------------
/** Compresses all buttons into a single byte. */
char getButtonsCompressed() const
{
return (m_brake ? 1 : 0)
@ -82,7 +129,7 @@ public:
+ (m_skid<<5); // m_skid is in {0,1,2,3}
} // getButtonsCompressed
// ------------------------------------------------------------------------
/** Sets the buttons from a compressed representation.
/** Sets the buttons from a compressed (1 byte) representation.
* /param c Character containing the compressed representation.
*/
void setButtonsCompressed(char c)
@ -94,6 +141,33 @@ public:
m_look_back = (c & 16) != 0;
m_skid = (SkidControl)((c & 96) >> 5);
} // setButtonsCompressed
// ------------------------------------------------------------------------
/** Returns the current steering value. */
float getSteer() const { return m_steer; }
// ------------------------------------------------------------------------
/** Returns current acceleration. */
float getAccel() const { return m_accel; }
// ------------------------------------------------------------------------
/** Returns if the kart is braking. */
bool getBrake() const { return m_brake; }
// ------------------------------------------------------------------------
/** Returns if the kart activates nitro. */
bool getNitro() const { return m_nitro; }
// ------------------------------------------------------------------------
/** Returns the skidding control state: SC_NONE: not pressed;
* SC_NO_DIRECTION: pressed, but no steering;
* SC_LEFT/RIGHT: pressed in the specified direction. */
SkidControl getSkidControl() const { return m_skid; }
// ------------------------------------------------------------------------
/** Returns true if the kart triggered rescue. */
bool getRescue() const { return m_rescue; }
// ------------------------------------------------------------------------
/** Returns if fire is selected. */
bool getFire() const { return m_fire; }
// ------------------------------------------------------------------------
/** Returns if the kart wants to look back (which also implies that it
* will fire backwards. */
bool getLookBack() const { return m_look_back; }
};
#endif

View File

@ -147,7 +147,8 @@ void LocalPlayerController::steer(float dt, int steer_val)
if(UserConfigParams::m_gamepad_debug)
{
Log::debug("LocalPlayerController", " set to: %f\n", m_controls->m_steer);
Log::debug("LocalPlayerController", " set to: %f\n",
m_controls->getSteer());
}
} // steer
@ -170,7 +171,7 @@ void LocalPlayerController::update(float dt)
Camera *camera = Camera::getCamera(m_camera_index);
if (camera->getType() != Camera::CM_TYPE_END)
{
if (m_controls->m_look_back || (UserConfigParams::m_reverse_look_threshold > 0 &&
if (m_controls->getLookBack() || (UserConfigParams::m_reverse_look_threshold > 0 &&
m_kart->getSpeed() < -UserConfigParams::m_reverse_look_threshold))
{
camera->setMode(Camera::CM_REVERSE);

View File

@ -102,8 +102,8 @@ void PlayerController::action(PlayerAction action, int value)
if (value)
{
m_steer_val = value;
if(m_controls->m_skid==KartControl::SC_NO_DIRECTION)
m_controls->m_skid = KartControl::SC_LEFT;
if(m_controls->getSkidControl()==KartControl::SC_NO_DIRECTION)
m_controls->setSkidControl(KartControl::SC_LEFT);
}
else
m_steer_val = m_steer_val_r;
@ -114,8 +114,8 @@ void PlayerController::action(PlayerAction action, int value)
if (value)
{
m_steer_val = -value;
if(m_controls->m_skid==KartControl::SC_NO_DIRECTION)
m_controls->m_skid = KartControl::SC_RIGHT;
if(m_controls->getSkidControl()==KartControl::SC_NO_DIRECTION)
m_controls->setSkidControl(KartControl::SC_RIGHT);
}
else
m_steer_val = m_steer_val_l;
@ -125,15 +125,15 @@ void PlayerController::action(PlayerAction action, int value)
m_prev_accel = value;
if (value && !(m_penalty_time > 0.0f))
{
m_controls->m_accel = value/32768.0f;
m_controls->m_brake = false;
m_controls->m_nitro = m_prev_nitro;
m_controls->setAccel(value/32768.0f);
m_controls->setBrake(false);
m_controls->setNitro(m_prev_nitro);
}
else
{
m_controls->m_accel = 0.0f;
m_controls->m_brake = m_prev_brake;
m_controls->m_nitro = false;
m_controls->setAccel(0.0f);
m_controls->setBrake(m_prev_brake);
m_controls->setNitro(false);
}
break;
case PA_BRAKE:
@ -141,44 +141,44 @@ void PlayerController::action(PlayerAction action, int value)
// let's consider below that to be a deadzone
if(value > 32768/2)
{
m_controls->m_brake = true;
m_controls->m_accel = 0.0f;
m_controls->m_nitro = false;
m_controls->setBrake(true);
m_controls->setAccel(0.0f);
m_controls->setNitro(false);
}
else
{
m_controls->m_brake = false;
m_controls->m_accel = m_prev_accel/32768.0f;
m_controls->setBrake(false);
m_controls->setAccel(m_prev_accel/32768.0f);
// Nitro still depends on whether we're accelerating
m_controls->m_nitro = (m_prev_nitro && m_prev_accel);
m_controls->setNitro(m_prev_nitro && m_prev_accel);
}
break;
case PA_NITRO:
// This basically keeps track whether the button still is being pressed
m_prev_nitro = (value != 0);
// Enable nitro only when also accelerating
m_controls->m_nitro = ((value!=0) && m_controls->m_accel);
m_controls->setNitro( ((value!=0) && m_controls->getAccel()) );
break;
case PA_RESCUE:
m_controls->m_rescue = (value!=0);
m_controls->setRescue(value!=0);
break;
case PA_FIRE:
m_controls->m_fire = (value!=0);
m_controls->setFire(value!=0);
break;
case PA_LOOK_BACK:
m_controls->m_look_back = (value!=0);
m_controls->setLookBack(value!=0);
break;
case PA_DRIFT:
if(value==0)
m_controls->m_skid = KartControl::SC_NONE;
m_controls->setSkidControl(KartControl::SC_NONE);
else
{
if(m_steer_val==0)
m_controls->m_skid = KartControl::SC_NO_DIRECTION;
m_controls->setSkidControl(KartControl::SC_NO_DIRECTION);
else
m_controls->m_skid = m_steer_val<0
? KartControl::SC_RIGHT
: KartControl::SC_LEFT;
m_controls->setSkidControl(m_steer_val<0
? KartControl::SC_RIGHT
: KartControl::SC_LEFT );
}
break;
case PA_PAUSE_RACE:
@ -195,52 +195,54 @@ void PlayerController::action(PlayerAction action, int value)
*/
void PlayerController::steer(float dt, int steer_val)
{
// Get the old value, compute the new steering value,
// and set it at the end of this function
float steer = m_controls->getSteer();
if(stk_config->m_disable_steer_while_unskid &&
m_controls->m_skid==KartControl::SC_NONE &&
m_controls->getSkidControl()==KartControl::SC_NONE &&
m_kart->getSkidding()->getVisualSkidRotation()!=0)
{
m_controls->m_steer = 0;
steer = 0;
}
// Amount the steering is changed for digital devices.
// If the steering is 'back to straight', a different steering
// change speed is used.
const float STEER_CHANGE = ( (steer_val<=0 && m_controls->m_steer<0) ||
(steer_val>=0 && m_controls->m_steer>0) )
const float STEER_CHANGE = ( (steer_val<=0 && steer<0) ||
(steer_val>=0 && steer>0) )
? dt/m_kart->getKartProperties()->getTurnTimeResetSteer()
: dt/m_kart->getTimeFullSteer(fabsf(m_controls->m_steer));
: dt/m_kart->getTimeFullSteer(fabsf(steer));
if (steer_val < 0)
{
// If we got analog values do not cumulate.
if (steer_val > -32767)
m_controls->m_steer = -steer_val/32767.0f;
steer = -steer_val/32767.0f;
else
m_controls->m_steer += STEER_CHANGE;
steer += STEER_CHANGE;
}
else if(steer_val > 0)
{
// If we got analog values do not cumulate.
if (steer_val < 32767)
m_controls->m_steer = -steer_val/32767.0f;
steer = -steer_val/32767.0f;
else
m_controls->m_steer -= STEER_CHANGE;
steer -= STEER_CHANGE;
}
else
{ // no key is pressed
if(m_controls->m_steer>0.0f)
if(steer>0.0f)
{
m_controls->m_steer -= STEER_CHANGE;
if(m_controls->m_steer<0.0f) m_controls->m_steer=0.0f;
steer -= STEER_CHANGE;
if(steer<0.0f) steer=0.0f;
}
else
{ // m_controls->m_steer<=0.0f;
m_controls->m_steer += STEER_CHANGE;
if(m_controls->m_steer>0.0f) m_controls->m_steer=0.0f;
} // if m_controls->m_steer<=0.0f
{ // steer<=0.0f;
steer += STEER_CHANGE;
if(steer>0.0f) steer=0.0f;
} // if steer<=0.0f
} // no key is pressed
m_controls->m_steer = std::min(1.0f, std::max(-1.0f, m_controls->m_steer));
m_controls->setSteer(std::min(1.0f, std::max(-1.0f, steer)) );
} // steer
@ -250,7 +252,7 @@ void PlayerController::steer(float dt, int steer_val)
*/
void PlayerController::skidBonusTriggered()
{
m_controls->m_steer = 0;
m_controls->setSteer(0);
} // skidBonusTriggered
//-----------------------------------------------------------------------------
@ -266,15 +268,15 @@ void PlayerController::update(float dt)
if (World::getWorld()->getPhase() == World::GOAL_PHASE)
{
m_controls->m_brake = false;
m_controls->m_accel = 0.0f;
m_controls->setBrake(false);
m_controls->setAccel(0.0f);
return;
}
if (World::getWorld()->isStartPhase())
{
if (m_controls->m_accel || m_controls->m_brake ||
m_controls->m_fire || m_controls->m_nitro)
if (m_controls->getAccel() || m_controls->getBrake()||
m_controls->getFire() || m_controls->getNitro())
{
// Only give penalty time in SET_PHASE.
// Penalty time check makes sure it doesn't get rendered on every
@ -286,8 +288,8 @@ void PlayerController::update(float dt)
m_penalty_time = stk_config->m_penalty_time;
} // if penalty_time = 0
m_controls->m_brake = false;
m_controls->m_accel = 0.0f;
m_controls->setBrake(false);
m_controls->setAccel(0.0f);
} // if key pressed
return;
@ -302,10 +304,10 @@ void PlayerController::update(float dt)
// Only accept rescue if there is no kart animation is already playing
// (e.g. if an explosion happens, wait till the explosion is over before
// starting any other animation).
if ( m_controls->m_rescue && !m_kart->getKartAnimation() )
if ( m_controls->getRescue() && !m_kart->getKartAnimation() )
{
new RescueAnimation(m_kart);
m_controls->m_rescue=false;
m_controls->setRescue(false);
}
} // update

View File

@ -223,8 +223,8 @@ unsigned int SkiddingAI::getNextSector(unsigned int index)
void SkiddingAI::update(float dt)
{
// This is used to enable firing an item backwards.
m_controls->m_look_back = false;
m_controls->m_nitro = false;
m_controls->setLookBack(false);
m_controls->setNitro(false);
// Don't do anything if there is currently a kart animations shown.
if(m_kart->getKartAnimation())
@ -286,8 +286,8 @@ void SkiddingAI::update(float dt)
// or slipstreaming.
#undef AI_DOES_NOT_MOVE_FOR_DEBUGGING
#ifdef AI_DOES_NOT_MOVE_FOR_DEBUGGING
m_controls->m_accel = 0;
m_controls->m_steer = 0;
m_controls->setAccel(0);
m_controls->setSteer(0);
return;
#endif
@ -324,8 +324,8 @@ void SkiddingAI::update(float dt)
m_kart_ahead )
{
// Use nitro if the kart is far ahead, or faster than this kart
m_controls->m_nitro = m_distance_ahead>10.0f ||
m_kart_ahead->getSpeed() > m_kart->getSpeed();
m_controls->setNitro(m_distance_ahead>10.0f ||
m_kart_ahead->getSpeed() > m_kart->getSpeed());
// If we are close enough, try to hit this kart
if(m_distance_ahead<=10)
{
@ -355,12 +355,12 @@ void SkiddingAI::update(float dt)
handleRescue(dt);
handleBraking();
// If a bomb is attached, nitro might already be set.
if(!m_controls->m_nitro)
if(!m_controls->getNitro())
handleNitroAndZipper();
}
// If we are supposed to use nitro, but have a zipper,
// use the zipper instead (unless there are items to avoid cloe by)
if(m_controls->m_nitro &&
if(m_controls->getNitro() &&
m_kart->getPowerup()->getType()==PowerupManager::POWERUP_ZIPPER &&
m_kart->getSpeed()>1.0f &&
m_kart->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0 &&
@ -372,8 +372,8 @@ void SkiddingAI::update(float dt)
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_TIME_TRIAL ||
(m_world->getTime()<3.0f && rand()%50==1) )
{
m_controls->m_nitro = false;
m_controls->m_fire = true;
m_controls->setNitro(false);
m_controls->setFire(true);
}
}
@ -391,7 +391,7 @@ void SkiddingAI::update(float dt)
*/
void SkiddingAI::handleBraking()
{
m_controls->m_brake = false;
m_controls->setBrake(false);
// In follow the leader mode, the kart should brake if they are ahead of
// the leader (and not the leader, i.e. don't have initial position 1)
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER &&
@ -404,7 +404,7 @@ void SkiddingAI::handleBraking()
m_kart->getIdent().c_str());
#endif
m_controls->m_brake = true;
m_controls->setBrake(true);
return;
}
@ -424,7 +424,7 @@ void SkiddingAI::handleBraking()
"%s not aligned with track.",
m_kart->getIdent().c_str());
#endif
m_controls->m_brake = true;
m_controls->setBrake(true);
return;
}
if(m_current_track_direction==DriveNode::DIR_LEFT ||
@ -435,9 +435,9 @@ void SkiddingAI::handleBraking()
if(m_kart->getSpeed() > 1.5f*max_turn_speed &&
m_kart->getSpeed()>MIN_SPEED &&
fabsf(m_controls->m_steer) > 0.95f )
fabsf(m_controls->getSteer()) > 0.95f )
{
m_controls->m_brake = true;
m_controls->setBrake(true);
#ifdef DEBUG
if(m_ai_debug)
Log::debug(getControllerName().c_str(),
@ -1144,7 +1144,7 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction,
*/
void SkiddingAI::handleItems(const float dt)
{
m_controls->m_fire = false;
m_controls->setFire(false);
if(m_kart->getKartAnimation() ||
m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING )
return;
@ -1153,12 +1153,12 @@ void SkiddingAI::handleItems(const float dt)
if (m_superpower == RaceManager::SUPERPOWER_NOLOK_BOSS)
{
m_controls->m_look_back = (m_kart->getPowerup()->getType() ==
PowerupManager::POWERUP_BOWLING );
m_controls->setLookBack(m_kart->getPowerup()->getType() ==
PowerupManager::POWERUP_BOWLING );
if( m_time_since_last_shot > 3.0f )
{
m_controls->m_fire = true;
m_controls->setFire(true);
if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_SWATTER)
m_time_since_last_shot = 3.0f;
else
@ -1169,7 +1169,7 @@ void SkiddingAI::handleItems(const float dt)
}
else
{
m_controls->m_fire = false;
m_controls->setFire(false);
}
return;
}
@ -1180,7 +1180,7 @@ void SkiddingAI::handleItems(const float dt)
{
if( m_time_since_last_shot > 10.0f )
{
m_controls->m_fire = true;
m_controls->setFire(true);
m_time_since_last_shot = 0.0f;
}
return;
@ -1196,8 +1196,7 @@ void SkiddingAI::handleItems(const float dt)
{
Attachment::AttachmentType type = m_kart->getAttachment()->getType();
// Don't use shield when we have a swatter.
if( type == Attachment::ATTACH_SWATTER ||
type == Attachment::ATTACH_NOLOKS_SWATTER )
if( type == Attachment::ATTACH_SWATTER)
break;
// Check if a flyable (cake, ...) is close. If so, use bubblegum
@ -1206,8 +1205,8 @@ void SkiddingAI::handleItems(const float dt)
projectile_manager->projectileIsClose(m_kart,
m_ai_properties->m_shield_incoming_radius) )
{
m_controls->m_fire = true;
m_controls->m_look_back = false;
m_controls->setFire(true);
m_controls->setLookBack(false);
break;
}
@ -1222,8 +1221,8 @@ void SkiddingAI::handleItems(const float dt)
// overtaken kart to overtake us again.
if(m_distance_behind < 15.0f && m_distance_behind > 3.0f )
{
m_controls->m_fire = true;
m_controls->m_look_back = true;
m_controls->setFire(true);
m_controls->setLookBack(true);
break;
}
@ -1235,8 +1234,8 @@ void SkiddingAI::handleItems(const float dt)
lin_world->getKartLaps(m_kart->getWorldKartId())
== race_manager->getNumLaps()-1)
{
m_controls->m_fire = true;
m_controls->m_look_back = true;
m_controls->setFire(true);
m_controls->setLookBack(true);
break;
}
break; // POWERUP_BUBBLEGUM
@ -1282,10 +1281,10 @@ void SkiddingAI::handleItems(const float dt)
: m_distance_ahead;
// Since cakes can be fired all around, just use a sane distance
// with a bit of extra for backwards, as enemy will go towards cake
m_controls->m_fire = (fire_backwards && distance < 25.0f) ||
(!fire_backwards && distance < 20.0f);
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
m_controls->setFire( (fire_backwards && distance < 25.0f) ||
(!fire_backwards && distance < 20.0f) );
if(m_controls->getFire())
m_controls->setLookBack(fire_backwards);
break;
} // POWERUP_CAKE
@ -1332,12 +1331,12 @@ void SkiddingAI::handleItems(const float dt)
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls->m_fire = ( (fire_backwards && distance < 30.0f) ||
m_controls->setFire( ( (fire_backwards && distance < 30.0f) ||
(!fire_backwards && distance <10.0f) ) &&
m_time_since_last_shot > 3.0f &&
(straight_behind || straight_ahead);
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
(straight_behind || straight_ahead) );
if(m_controls->getFire())
m_controls->setLookBack(fire_backwards);
break;
} // POWERUP_BOWLING
@ -1363,10 +1362,10 @@ void SkiddingAI::handleItems(const float dt)
!m_kart_ahead;
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls->m_fire = distance < 30.0f ||
m_time_since_last_shot > 10.0f;
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
m_controls->setFire(distance < 30.0f ||
m_time_since_last_shot > 10.0f );
if(m_controls->getFire())
m_controls->setLookBack(fire_backwards);
break;
} // POWERUP_PLUNGER
@ -1376,13 +1375,13 @@ void SkiddingAI::handleItems(const float dt)
// after a waiting an appropriate time
if(m_kart->getPosition()>1 &&
m_time_since_last_shot > stk_config->m_item_switch_time+2.0f)
m_controls->m_fire = true;
m_controls->setFire(true);
break; // POWERUP_SWITCH
case PowerupManager::POWERUP_PARACHUTE:
// Wait one second more than a previous parachute
if(m_time_since_last_shot > m_kart->getKartProperties()->getParachuteDurationOther() + 1.0f)
m_controls->m_fire = true;
m_controls->setFire(true);
break; // POWERUP_PARACHUTE
case PowerupManager::POWERUP_ANVIL:
@ -1391,13 +1390,13 @@ void SkiddingAI::handleItems(const float dt)
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
m_controls->m_fire = m_world->getTime()<1.0f &&
m_kart->getPosition()>2;
m_controls->setFire(m_world->getTime()<1.0f &&
m_kart->getPosition()>2 );
}
else
{
m_controls->m_fire = m_time_since_last_shot > 3.0f &&
m_kart->getPosition()>1;
m_controls->setFire(m_time_since_last_shot > 3.0f &&
m_kart->getPosition()>1 );
}
break; // POWERUP_ANVIL
@ -1418,7 +1417,7 @@ void SkiddingAI::handleItems(const float dt)
m_kart_ahead->getSpeed() < m_kart->getSpeed() ) ||
( m_kart_behind && !m_kart_behind->isSquashed() &&
(m_kart_behind->getXYZ()-m_kart->getXYZ()).length2()<d2) )
m_controls->m_fire = true;
m_controls->setFire(true);
break;
}
case PowerupManager::POWERUP_RUBBERBALL:
@ -1428,7 +1427,7 @@ void SkiddingAI::handleItems(const float dt)
// Perhaps some more sophisticated algorithm might be useful.
// For now: fire if there is a kart ahead (which means that
// this kart is certainly not the first kart)
m_controls->m_fire = m_kart_ahead != NULL;
m_controls->setFire(m_kart_ahead != NULL);
break;
default:
Log::error(getControllerName().c_str(),
@ -1436,7 +1435,7 @@ void SkiddingAI::handleItems(const float dt)
m_kart->getPowerup()->getType());
assert(false);
}
if(m_controls->m_fire) m_time_since_last_shot = 0.0f;
if(m_controls->getFire()) m_time_since_last_shot = 0.0f;
} // handleItems
//-----------------------------------------------------------------------------
@ -1512,26 +1511,26 @@ void SkiddingAI::handleAcceleration( const float dt)
if( m_start_delay > 0.0f )
{
m_start_delay -= dt;
m_controls->m_accel = 0.0f;
m_controls->setAccel(0.0f);
return;
}
if( m_controls->m_brake )
if( m_controls->getBrake())
{
m_controls->m_accel = 0.0f;
m_controls->setAccel(0.0f);
return;
}
if(m_kart->getBlockedByPlungerTime()>0)
{
if(m_kart->getSpeed() < m_kart->getCurrentMaxSpeed() / 2)
m_controls->m_accel = 0.05f;
m_controls->setAccel(0.05f);
else
m_controls->m_accel = 0.0f;
m_controls->setAccel(0.0f);
return;
}
m_controls->m_accel = stk_config->m_ai_acceleration;
m_controls->setAccel(stk_config->m_ai_acceleration);
} // handleAcceleration
@ -1588,7 +1587,7 @@ void SkiddingAI::handleRescue(const float dt)
*/
void SkiddingAI::handleNitroAndZipper()
{
m_controls->m_nitro = false;
m_controls->setNitro(false);
// If we are already very fast, save nitro.
if(m_kart->getSpeed() > 0.95f*m_kart->getCurrentMaxSpeed())
return;
@ -1596,7 +1595,7 @@ void SkiddingAI::handleNitroAndZipper()
if(m_kart->getBlockedByPlungerTime()>0) return;
// Don't use nitro if we are braking
if(m_controls->m_brake) return;
if(m_controls->getBrake()) return;
// Don't use nitro if the kart is not on ground or has finished the race
if(!m_kart->isOnGround() || m_kart->hasFinishedRace()) return;
@ -1626,7 +1625,7 @@ void SkiddingAI::handleNitroAndZipper()
// If the kart is very slow (e.g. after rescue), use nitro
if(m_kart->getSpeed()<5)
{
m_controls->m_nitro = true;
m_controls->setNitro(true);
return;
}
@ -1637,7 +1636,7 @@ void SkiddingAI::handleNitroAndZipper()
if(m_kart->getPosition()== (int)num_karts &&
num_karts>1 && m_kart->getEnergy()>2.0f)
{
m_controls->m_nitro = true;
m_controls->setNitro(true);
return;
}
@ -1653,7 +1652,7 @@ void SkiddingAI::handleNitroAndZipper()
m_world->getEstimatedFinishTime(m_kart->getWorldKartId());
if( 1.5f*m_kart->getEnergy() >= finish - m_world->getTime() )
{
m_controls->m_nitro = true;
m_controls->setNitro(true);
return;
}
}
@ -1669,7 +1668,7 @@ void SkiddingAI::handleNitroAndZipper()
m_distance_ahead < overtake_distance &&
m_kart_ahead->getSpeed()+5.0f > m_kart->getSpeed() )
{
m_controls->m_nitro = true;
m_controls->setNitro(true);
return;
}
@ -1678,8 +1677,8 @@ void SkiddingAI::handleNitroAndZipper()
m_kart_behind->getSpeed() > m_kart->getSpeed() )
{
// Only prevent overtaking on highest level
m_controls->m_nitro = m_ai_properties->m_nitro_usage
== AIProperties::NITRO_ALL;
m_controls->setNitro(m_ai_properties->m_nitro_usage
== AIProperties::NITRO_ALL);
return;
}
@ -1697,7 +1696,7 @@ void SkiddingAI::handleNitroAndZipper()
- DriveGraph::get()->getDistanceFromStart(m_track_node);
if(diff<0) diff+=World::getWorld()->getTrack()->getTrackLength();
if(diff>m_ai_properties->m_straight_length_for_zipper)
m_controls->m_fire = true;
m_controls->setFire(true);
}
}
@ -1934,7 +1933,6 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps);
#endif
*last_node = m_next_node_index[m_track_node];
int target_sector;
Vec3 direction;
Vec3 step_track_coord;
@ -1946,7 +1944,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
{
// target_sector is the sector at the longest distance that we can
// drive to without crashing with the track.
target_sector = m_next_node_index[*last_node];
int target_sector = m_next_node_index[*last_node];
//direction is a vector from our kart to the sectors we are testing
direction = DriveGraph::get()->getNode(target_sector)->getCenter()
@ -2030,12 +2028,10 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
*last_node = m_next_node_index[m_track_node];
float angle = DriveGraph::get()->getAngleToNext(m_track_node,
m_successor_index[m_track_node]);
int target_sector;
Vec3 direction;
Vec3 step_track_coord;
float angle1;
// The original while(1) loop is replaced with a for loop to avoid
// infinite loops (which we had once or twice). Usually the number
// of iterations in the while loop is less than 7.
@ -2043,8 +2039,8 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
{
// target_sector is the sector at the longest distance that we can
// drive to without crashing with the track.
target_sector = m_next_node_index[*last_node];
angle1 = DriveGraph::get()->getAngleToNext(target_sector,
int target_sector = m_next_node_index[*last_node];
float angle1 = DriveGraph::get()->getAngleToNext(target_sector,
m_successor_index[target_sector]);
// In very sharp turns this algorithm tends to aim at off track points,
// resulting in hitting a corner. So test for this special case and
@ -2231,7 +2227,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
// If the kart has to do a sharp turn, but is already skidding, find
// a good time to release the skid button, since this will turn the
// kart more sharply:
if(m_controls->m_skid)
if(m_controls->getSkidControl())
{
#ifdef DEBUG
if(m_ai_debug)
@ -2256,7 +2252,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
m_current_track_direction==DriveNode::DIR_UNDEFINED )
{
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
if(m_controls->getSkidControl() && m_ai_debug)
{
Log::debug(getControllerName().c_str(),
"%s stops skidding on straight.",
@ -2293,7 +2289,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
// If the remaining estimated time for skidding is too short, stop
// it. This code will mostly trigger the bonus at the end of a skid.
if(m_controls->m_skid && duration < 1.0f)
if(m_controls->getSkidControl() && duration < 1.0f)
{
if(m_ai_debug)
Log::debug(getControllerName().c_str(),
@ -2311,7 +2307,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
m_current_track_direction==DriveNode::DIR_RIGHT) )
{
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
if(m_controls->getSkidControl() && m_ai_debug)
Log::debug(getControllerName().c_str(),
"%s skidding against track direction.",
m_kart->getIdent().c_str());
@ -2323,7 +2319,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
m_kart->getKartProperties()->getSkidTimeTillBonus()[0] < duration)
{
#ifdef DEBUG
if(!m_controls->m_skid && m_ai_debug)
if(!m_controls->getSkidControl() && m_ai_debug)
Log::debug(getControllerName().c_str(),
"%s start skid, duration %f.",
m_kart->getIdent().c_str(), duration);
@ -2333,7 +2329,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
} // if curve long enough for skidding
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
if(m_controls->getSkidControl() && m_ai_debug)
Log::debug(getControllerName().c_str(),
"%s has no reasons to skid anymore.",
m_kart->getIdent().c_str());
@ -2364,7 +2360,7 @@ void SkiddingAI::setSteering(float angle, float dt)
if(!canSkid(steer_fraction))
{
m_skid_probability_state = SKID_PROBAB_NOT_YET;
m_controls->m_skid = KartControl::SC_NONE;
m_controls->setSkidControl(KartControl::SC_NONE);
}
else
{
@ -2387,8 +2383,8 @@ void SkiddingAI::setSteering(float angle, float dt)
sc= ? "no" : sc==KartControl::SC_LEFT ? "left" : "right");
#endif
}
m_controls->m_skid = m_skid_probability_state == SKID_PROBAB_SKID
? sc : KartControl::SC_NONE;
m_controls->setSkidControl(m_skid_probability_state == SKID_PROBAB_SKID
? sc : KartControl::SC_NONE );
}
// Adjust steer fraction in case to be in [-1,1]
@ -2412,7 +2408,7 @@ void SkiddingAI::setSteering(float angle, float dt)
if((ss==Skidding::SKID_ACCUMULATE_LEFT && steer_fraction>0.2f ) ||
(ss==Skidding::SKID_ACCUMULATE_RIGHT && steer_fraction<-0.2f) )
{
m_controls->m_skid = KartControl::SC_NONE;
m_controls->setSkidControl(KartControl::SC_NONE);
#ifdef DEBUG
if(m_ai_debug)
Log::info(getControllerName().c_str(),
@ -2421,7 +2417,7 @@ void SkiddingAI::setSteering(float angle, float dt)
#endif
}
if(m_controls->m_skid!=KartControl::SC_NONE &&
if(m_controls->getSkidControl()!=KartControl::SC_NONE &&
( ss==Skidding::SKID_ACCUMULATE_LEFT ||
ss==Skidding::SKID_ACCUMULATE_RIGHT ) )
{
@ -2435,7 +2431,7 @@ void SkiddingAI::setSteering(float angle, float dt)
"%s steering too much (%f).",
m_kart->getIdent().c_str(), steer_fraction);
#endif
m_controls->m_skid = KartControl::SC_NONE;
m_controls->setSkidControl(KartControl::SC_NONE);
}
if(steer_fraction<-1.0f)
steer_fraction = -1.0f;
@ -2443,19 +2439,19 @@ void SkiddingAI::setSteering(float angle, float dt)
steer_fraction = 1.0f;
}
float old_steer = m_controls->m_steer;
float old_steer = m_controls->getSteer();
// The AI has its own 'time full steer' value (which is the time
float max_steer_change = dt/m_ai_properties->m_time_full_steer;
if(old_steer < steer_fraction)
{
m_controls->m_steer = (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change;
m_controls->setSteer( (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change );
}
else
{
m_controls->m_steer = (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change;
m_controls->setSteer( (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change );
}

View File

@ -119,8 +119,8 @@ void SoccerAI::update(float dt)
if (m_world->getPhase() == World::GOAL_PHASE)
{
resetAfterStop();
m_controls->m_brake = false;
m_controls->m_accel = 0.0f;
m_controls->setBrake(false);
m_controls->setAccel(0.0f);
AIBaseController::update(dt);
return;
}

View File

@ -229,8 +229,8 @@ unsigned int SkiddingAI::getNextSector(unsigned int index)
void SkiddingAI::update(float dt)
{
// This is used to enable firing an item backwards.
m_controls->m_look_back = false;
m_controls->m_nitro = false;
m_controls->setLookBack(false);
m_controls->setNitro(false);
// Don't do anything if there is currently a kart animations shown.
if(m_kart->getKartAnimation())
@ -292,8 +292,8 @@ void SkiddingAI::update(float dt)
// or slipstreaming.
#undef AI_DOES_NOT_MOVE_FOR_DEBUGGING
#ifdef AI_DOES_NOT_MOVE_FOR_DEBUGGING
m_controls->m_accel = 0;
m_controls->m_steer = 0;
m_controls->setAccel(0);
m_controls->setSteer(0);
return;
#endif
@ -330,8 +330,8 @@ void SkiddingAI::update(float dt)
m_kart_ahead )
{
// Use nitro if the kart is far ahead, or faster than this kart
m_controls->m_nitro = m_distance_ahead>10.0f ||
m_kart_ahead->getSpeed() > m_kart->getSpeed();
m_controls->setNitro(m_distance_ahead>10.0f ||
m_kart_ahead->getSpeed() > m_kart->getSpeed());
// If we are close enough, try to hit this kart
if(m_distance_ahead<=10)
{
@ -361,12 +361,12 @@ void SkiddingAI::update(float dt)
handleRescue(dt);
handleBraking();
// If a bomb is attached, nitro might already be set.
if(!m_controls->m_nitro)
if(!m_controls->getNitro())
handleNitroAndZipper();
}
// If we are supposed to use nitro, but have a zipper,
// use the zipper instead (unless there are items to avoid cloe by)
if(m_controls->m_nitro &&
if(m_controls->getNitro() &&
m_kart->getPowerup()->getType()==PowerupManager::POWERUP_ZIPPER &&
m_kart->getSpeed()>1.0f &&
m_kart->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0 &&
@ -378,8 +378,8 @@ void SkiddingAI::update(float dt)
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_TIME_TRIAL ||
(m_world->getTime()<3.0f && rand()%50==1) )
{
m_controls->m_nitro = false;
m_controls->m_fire = true;
m_controls->setNitro(false);
m_controls->setFire(true);
}
}
@ -397,7 +397,7 @@ void SkiddingAI::update(float dt)
*/
void SkiddingAI::handleBraking()
{
m_controls->m_brake = false;
m_controls->setBrake(false);
// In follow the leader mode, the kart should brake if they are ahead of
// the leader (and not the leader, i.e. don't have initial position 1)
if(race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER &&
@ -410,7 +410,7 @@ void SkiddingAI::handleBraking()
m_kart->getIdent().c_str());
#endif
m_controls->m_brake = true;
m_controls->setBrake(true);
return;
}
@ -430,7 +430,7 @@ void SkiddingAI::handleBraking()
"%s not aligned with track.",
m_kart->getIdent().c_str());
#endif
m_controls->m_brake = true;
m_controls->setBrake(true);
return;
}
if(m_current_track_direction==DriveNode::DIR_LEFT ||
@ -441,9 +441,9 @@ void SkiddingAI::handleBraking()
if(m_kart->getSpeed() > 1.5f*max_turn_speed &&
m_kart->getSpeed()>MIN_SPEED &&
fabsf(m_controls->m_steer) > 0.95f )
fabsf(m_controls->getSteer()) > 0.95f )
{
m_controls->m_brake = true;
m_controls->setBrake(true);
#ifdef DEBUG
if(m_ai_debug)
Log::debug(getControllerName().c_str(),
@ -1150,7 +1150,7 @@ void SkiddingAI::evaluateItems(const Item *item, Vec3 kart_aim_direction,
*/
void SkiddingAI::handleItems(const float dt)
{
m_controls->m_fire = false;
m_controls->setFire(false);
if(m_kart->getKartAnimation() ||
m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING )
return;
@ -1159,12 +1159,12 @@ void SkiddingAI::handleItems(const float dt)
if (m_superpower == RaceManager::SUPERPOWER_NOLOK_BOSS)
{
m_controls->m_look_back = (m_kart->getPowerup()->getType() ==
PowerupManager::POWERUP_BOWLING );
m_controls->setLookBack(m_kart->getPowerup()->getType() ==
PowerupManager::POWERUP_BOWLING );
if( m_time_since_last_shot > 3.0f )
{
m_controls->m_fire = true;
m_controls->setFire(true);
if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_SWATTER)
m_time_since_last_shot = 3.0f;
else
@ -1175,7 +1175,7 @@ void SkiddingAI::handleItems(const float dt)
}
else
{
m_controls->m_fire = false;
m_controls->setFire(false);
}
return;
}
@ -1186,7 +1186,7 @@ void SkiddingAI::handleItems(const float dt)
{
if( m_time_since_last_shot > 10.0f )
{
m_controls->m_fire = true;
m_controls->setFire(true);
m_time_since_last_shot = 0.0f;
}
return;
@ -1202,8 +1202,7 @@ void SkiddingAI::handleItems(const float dt)
{
Attachment::AttachmentType type = m_kart->getAttachment()->getType();
// Don't use shield when we have a swatter.
if( type == Attachment::ATTACH_SWATTER ||
type == Attachment::ATTACH_NOLOKS_SWATTER )
if( type == Attachment::ATTACH_SWATTER)
break;
// Check if a flyable (cake, ...) is close. If so, use bubblegum
@ -1212,8 +1211,8 @@ void SkiddingAI::handleItems(const float dt)
projectile_manager->projectileIsClose(m_kart,
m_ai_properties->m_shield_incoming_radius) )
{
m_controls->m_fire = true;
m_controls->m_look_back = false;
m_controls->setFire(true);
m_controls->setLookBack(false);
break;
}
@ -1228,8 +1227,8 @@ void SkiddingAI::handleItems(const float dt)
// overtaken kart to overtake us again.
if(m_distance_behind < 15.0f && m_distance_behind > 3.0f )
{
m_controls->m_fire = true;
m_controls->m_look_back = true;
m_controls->setFire(true);
m_controls->setLookBack(true);
break;
}
@ -1241,8 +1240,8 @@ void SkiddingAI::handleItems(const float dt)
lin_world->getKartLaps(m_kart->getWorldKartId())
== race_manager->getNumLaps()-1)
{
m_controls->m_fire = true;
m_controls->m_look_back = true;
m_controls->setFire(true);
m_controls->setLookBack(true);
break;
}
break; // POWERUP_BUBBLEGUM
@ -1288,10 +1287,10 @@ void SkiddingAI::handleItems(const float dt)
: m_distance_ahead;
// Since cakes can be fired all around, just use a sane distance
// with a bit of extra for backwards, as enemy will go towards cake
m_controls->m_fire = (fire_backwards && distance < 25.0f) ||
(!fire_backwards && distance < 20.0f);
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
m_controls->setFire( (fire_backwards && distance < 25.0f) ||
(!fire_backwards && distance < 20.0f) );
if(m_controls->getFire())
m_controls->setLookBack(fire_backwards);
break;
} // POWERUP_CAKE
@ -1338,12 +1337,12 @@ void SkiddingAI::handleItems(const float dt)
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls->m_fire = ( (fire_backwards && distance < 30.0f) ||
m_controls->setFire( ( (fire_backwards && distance < 30.0f) ||
(!fire_backwards && distance <10.0f) ) &&
m_time_since_last_shot > 3.0f &&
(straight_behind || straight_ahead);
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
(straight_behind || straight_ahead) );
if(m_controls->getFire())
m_controls->setLookBack(fire_backwards);
break;
} // POWERUP_BOWLING
@ -1369,10 +1368,10 @@ void SkiddingAI::handleItems(const float dt)
!m_kart_ahead;
float distance = fire_backwards ? m_distance_behind
: m_distance_ahead;
m_controls->m_fire = distance < 30.0f ||
m_time_since_last_shot > 10.0f;
if(m_controls->m_fire)
m_controls->m_look_back = fire_backwards;
m_controls->setFire(distance < 30.0f ||
m_time_since_last_shot > 10.0f );
if(m_controls->getFire())
m_controls->setLookBack(fire_backwards);
break;
} // POWERUP_PLUNGER
@ -1382,13 +1381,13 @@ void SkiddingAI::handleItems(const float dt)
// after a waiting an appropriate time
if(m_kart->getPosition()>1 &&
m_time_since_last_shot > stk_config->m_item_switch_time+2.0f)
m_controls->m_fire = true;
m_controls->setFire(true);
break; // POWERUP_SWITCH
case PowerupManager::POWERUP_PARACHUTE:
// Wait one second more than a previous parachute
if(m_time_since_last_shot > m_kart->getKartProperties()->getParachuteDurationOther() + 1.0f)
m_controls->m_fire = true;
m_controls->setFire(true);
break; // POWERUP_PARACHUTE
case PowerupManager::POWERUP_ANVIL:
@ -1397,13 +1396,13 @@ void SkiddingAI::handleItems(const float dt)
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
m_controls->m_fire = m_world->getTime()<1.0f &&
m_kart->getPosition()>2;
m_controls->setFire(m_world->getTime()<1.0f &&
m_kart->getPosition()>2 );
}
else
{
m_controls->m_fire = m_time_since_last_shot > 3.0f &&
m_kart->getPosition()>1;
m_controls->setFire(m_time_since_last_shot > 3.0f &&
m_kart->getPosition()>1 );
}
break; // POWERUP_ANVIL
@ -1424,7 +1423,7 @@ void SkiddingAI::handleItems(const float dt)
m_kart_ahead->getSpeed() < m_kart->getSpeed() ) ||
( m_kart_behind && !m_kart_behind->isSquashed() &&
(m_kart_behind->getXYZ()-m_kart->getXYZ()).length2()<d2) )
m_controls->m_fire = true;
m_controls->setFire(true);
break;
}
case PowerupManager::POWERUP_RUBBERBALL:
@ -1434,7 +1433,7 @@ void SkiddingAI::handleItems(const float dt)
// Perhaps some more sophisticated algorithm might be useful.
// For now: fire if there is a kart ahead (which means that
// this kart is certainly not the first kart)
m_controls->m_fire = m_kart_ahead != NULL;
m_controls->setFire(m_kart_ahead != NULL);
break;
default:
Log::error(getControllerName().c_str(),
@ -1442,7 +1441,7 @@ void SkiddingAI::handleItems(const float dt)
m_kart->getPowerup()->getType());
assert(false);
}
if(m_controls->m_fire) m_time_since_last_shot = 0.0f;
if(m_controls->getFire()) m_time_since_last_shot = 0.0f;
} // handleItems
//-----------------------------------------------------------------------------
@ -1518,7 +1517,7 @@ void SkiddingAI::handleAcceleration( const float dt)
if( m_start_delay > 0.0f )
{
m_start_delay -= dt;
m_controls->m_accel = 0.0f;
m_controls->setAccel(0.0f);
return;
}
@ -1526,22 +1525,22 @@ void SkiddingAI::handleAcceleration( const float dt)
// m_brake has not been reset from the previous frame, which can
// cause too long slow downs. On the other hand removing it appears
// to decrease performance in some narrower tracks
if( m_controls->m_brake )
if( m_controls->getBrake())
{
m_controls->m_accel = 0.0f;
m_controls->setAccel(0.0f);
return;
}
if(m_kart->getBlockedByPlungerTime()>0)
{
if(m_kart->getSpeed() < m_kart->getCurrentMaxSpeed() / 2)
m_controls->m_accel = 0.05f;
m_controls->setAccel(0.05f);
else
m_controls->m_accel = 0.0f;
m_controls->setAccel(0.0f);
return;
}
m_controls->m_accel = stk_config->m_ai_acceleration;
m_controls->setAccel(stk_config->m_ai_acceleration);
} // handleAcceleration
@ -1598,7 +1597,7 @@ void SkiddingAI::handleRescue(const float dt)
*/
void SkiddingAI::handleNitroAndZipper()
{
m_controls->m_nitro = false;
m_controls->setNitro(false);
// The next line prevents usage of nitro on long straights, where the kart
// is already fast.
@ -1607,14 +1606,14 @@ void SkiddingAI::handleNitroAndZipper()
if(m_kart->getSpeed() > 0.95f*m_kart->getCurrentMaxSpeed())
return;
// About the above: removing this line might enable the AI to better use
// nitro and zippers .
// nitro and zippers.
// This works well for some tracks (math, lighthouse
// sandtrack), but worse in others (zen, xr591). The good result
// is caused by long straights in which the AI will now use zippers,
// but in the other tracks the high speed causes karts to crash in
// tight corners. In some cases it would need a reasonable large 'lookahead'
// to know that at a certain spot a zipper or skidding should not be used
// (e.g. in xr591, left at the fork - new AI will nearly always fell of the
// (e.g. in xr591, left at the fork - new AI will nearly always fall off the
// track after the curve). Here the summarised results of 10 laps runs with
// 4 old against 4 new AIs in time trail mode. The 'gain' is the sum of all
// (star_positions-end_positions). So a gain of -10 means that basically
@ -1645,7 +1644,7 @@ void SkiddingAI::handleNitroAndZipper()
if(m_kart->getBlockedByPlungerTime()>0) return;
// Don't use nitro if we are braking
if(m_controls->m_brake) return;
if(m_controls->getBrake()) return;
// Don't use nitro if the kart is not on ground or has finished the race
if(!m_kart->isOnGround() || m_kart->hasFinishedRace()) return;
@ -1675,7 +1674,7 @@ void SkiddingAI::handleNitroAndZipper()
// If the kart is very slow (e.g. after rescue), use nitro
if(m_kart->getSpeed()<5)
{
m_controls->m_nitro = true;
m_controls->setNitro(true);
return;
}
@ -1686,7 +1685,7 @@ void SkiddingAI::handleNitroAndZipper()
if(m_kart->getPosition()== (int)num_karts &&
num_karts>1 && m_kart->getEnergy()>2.0f)
{
m_controls->m_nitro = true;
m_controls->setNitro(true);
return;
}
@ -1702,7 +1701,7 @@ void SkiddingAI::handleNitroAndZipper()
m_world->getEstimatedFinishTime(m_kart->getWorldKartId());
if( 1.5f*m_kart->getEnergy() >= finish - m_world->getTime() )
{
m_controls->m_nitro = true;
m_controls->setNitro(true);
return;
}
}
@ -1718,7 +1717,7 @@ void SkiddingAI::handleNitroAndZipper()
m_distance_ahead < overtake_distance &&
m_kart_ahead->getSpeed()+5.0f > m_kart->getSpeed() )
{
m_controls->m_nitro = true;
m_controls->setNitro(true);
return;
}
@ -1727,8 +1726,8 @@ void SkiddingAI::handleNitroAndZipper()
m_kart_behind->getSpeed() > m_kart->getSpeed() )
{
// Only prevent overtaking on highest level
m_controls->m_nitro = m_ai_properties->m_nitro_usage
== AIProperties::NITRO_ALL;
m_controls->setNitro(m_ai_properties->m_nitro_usage
== AIProperties::NITRO_ALL );
return;
}
@ -1746,7 +1745,7 @@ void SkiddingAI::handleNitroAndZipper()
- DriveGraph::get()->getDistanceFromStart(m_track_node);
if(diff<0) diff+=World::getWorld()->getTrack()->getTrackLength();
if(diff>m_ai_properties->m_straight_length_for_zipper)
m_controls->m_fire = true;
m_controls->setFire(true);
}
}
@ -1983,7 +1982,6 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
m_curve[CURVE_KART]->addPoint(m_kart->getTrans()(forw)+eps);
#endif
*last_node = m_next_node_index[m_track_node];
int target_sector;
Vec3 direction;
Vec3 step_track_coord;
@ -1995,7 +1993,7 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
{
// target_sector is the sector at the longest distance that we can
// drive to without crashing with the track.
target_sector = m_next_node_index[*last_node];
int target_sector = m_next_node_index[*last_node];
//direction is a vector from our kart to the sectors we are testing
direction = DriveGraph::get()->getNode(target_sector)->getCenter()
@ -2079,12 +2077,9 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
*last_node = m_next_node_index[m_track_node];
float angle = DriveGraph::get()->getAngleToNext(m_track_node,
m_successor_index[m_track_node]);
int target_sector;
Vec3 direction;
Vec3 step_track_coord;
float angle1;
// The original while(1) loop is replaced with a for loop to avoid
// infinite loops (which we had once or twice). Usually the number
// of iterations in the while loop is less than 7.
@ -2092,8 +2087,8 @@ void SkiddingAI::findNonCrashingPointFixed(Vec3 *aim_position, int *last_node)
{
// target_sector is the sector at the longest distance that we can
// drive to without crashing with the track.
target_sector = m_next_node_index[*last_node];
angle1 = DriveGraph::get()->getAngleToNext(target_sector,
int target_sector = m_next_node_index[*last_node];
float angle1 = DriveGraph::get()->getAngleToNext(target_sector,
m_successor_index[target_sector]);
// In very sharp turns this algorithm tends to aim at off track points,
// resulting in hitting a corner. So test for this special case and
@ -2280,7 +2275,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
// If the kart has to do a sharp turn, but is already skidding, find
// a good time to release the skid button, since this will turn the
// kart more sharply:
if(m_controls->m_skid)
if(m_controls->getSkidControl())
{
#ifdef DEBUG
if(m_ai_debug)
@ -2305,7 +2300,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
m_current_track_direction==DriveNode::DIR_UNDEFINED )
{
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
if(m_controls->getSkidControl() && m_ai_debug)
{
Log::debug(getControllerName().c_str(),
"%s stops skidding on straight.",
@ -2342,7 +2337,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
// If the remaining estimated time for skidding is too short, stop
// it. This code will mostly trigger the bonus at the end of a skid.
if(m_controls->m_skid && duration < 1.0f)
if(m_controls->getSkidControl() && duration < 1.0f)
{
if(m_ai_debug)
Log::debug(getControllerName().c_str(),
@ -2360,7 +2355,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
m_current_track_direction==DriveNode::DIR_RIGHT) )
{
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
if(m_controls->getSkidControl() && m_ai_debug)
Log::debug(getControllerName().c_str(),
"%s skidding against track direction.",
m_kart->getIdent().c_str());
@ -2372,7 +2367,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
m_kart->getKartProperties()->getSkidTimeTillBonus()[0] < duration)
{
#ifdef DEBUG
if(!m_controls->m_skid && m_ai_debug)
if(!m_controls->getSkidControl() && m_ai_debug)
Log::debug(getControllerName().c_str(),
"%s start skid, duration %f.",
m_kart->getIdent().c_str(), duration);
@ -2382,7 +2377,7 @@ bool SkiddingAI::canSkid(float steer_fraction)
} // if curve long enough for skidding
#ifdef DEBUG
if(m_controls->m_skid && m_ai_debug)
if(m_controls->getSkidControl() && m_ai_debug)
Log::debug(getControllerName().c_str(),
"%s has no reasons to skid anymore.",
m_kart->getIdent().c_str());
@ -2413,7 +2408,7 @@ void SkiddingAI::setSteering(float angle, float dt)
if(!canSkid(steer_fraction))
{
m_skid_probability_state = SKID_PROBAB_NOT_YET;
m_controls->m_skid = KartControl::SC_NONE;
m_controls->setSkidControl(KartControl::SC_NONE);
}
else
{
@ -2436,8 +2431,8 @@ void SkiddingAI::setSteering(float angle, float dt)
sc= ? "no" : sc==KartControl::SC_LEFT ? "left" : "right");
#endif
}
m_controls->m_skid = m_skid_probability_state == SKID_PROBAB_SKID
? sc : KartControl::SC_NONE;
m_controls->setSkidControl(m_skid_probability_state == SKID_PROBAB_SKID
? sc : KartControl::SC_NONE );
}
// Adjust steer fraction in case to be in [-1,1]
@ -2461,7 +2456,7 @@ void SkiddingAI::setSteering(float angle, float dt)
if((ss==Skidding::SKID_ACCUMULATE_LEFT && steer_fraction>0.2f ) ||
(ss==Skidding::SKID_ACCUMULATE_RIGHT && steer_fraction<-0.2f) )
{
m_controls->m_skid = KartControl::SC_NONE;
m_controls->setSkidControl(KartControl::SC_NONE);
#ifdef DEBUG
if(m_ai_debug)
Log::info(getControllerName().c_str(),
@ -2470,7 +2465,7 @@ void SkiddingAI::setSteering(float angle, float dt)
#endif
}
if(m_controls->m_skid!=KartControl::SC_NONE &&
if(m_controls->getSkidControl()!=KartControl::SC_NONE &&
( ss==Skidding::SKID_ACCUMULATE_LEFT ||
ss==Skidding::SKID_ACCUMULATE_RIGHT ) )
{
@ -2484,7 +2479,7 @@ void SkiddingAI::setSteering(float angle, float dt)
"%s steering too much (%f).",
m_kart->getIdent().c_str(), steer_fraction);
#endif
m_controls->m_skid = KartControl::SC_NONE;
m_controls->setSkidControl(KartControl::SC_NONE);
}
if(steer_fraction<-1.0f)
steer_fraction = -1.0f;
@ -2492,19 +2487,19 @@ void SkiddingAI::setSteering(float angle, float dt)
steer_fraction = 1.0f;
}
float old_steer = m_controls->m_steer;
float old_steer = m_controls->getSteer();
// The AI has its own 'time full steer' value (which is the time
float max_steer_change = dt/m_ai_properties->m_time_full_steer;
if(old_steer < steer_fraction)
{
m_controls->m_steer = (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change;
m_controls->setSteer( (old_steer+max_steer_change > steer_fraction)
? steer_fraction : old_steer+max_steer_change );
}
else
{
m_controls->m_steer = (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change;
m_controls->setSteer( (old_steer-max_steer_change < steer_fraction)
? steer_fraction : old_steer-max_steer_change );
}

View File

@ -18,7 +18,7 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/kart.hpp"
#include "graphics/central_settings.hpp"
#include "audio/sfx_manager.hpp"
#include "audio/sfx_base.hpp"
#include "challenges/challenge_status.hpp"
@ -27,6 +27,7 @@
#include "config/user_config.hpp"
#include "font/bold_face.hpp"
#include "graphics/camera.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/explosion.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/material_manager.hpp"
@ -40,25 +41,33 @@
#include "graphics/stk_text_billboard.hpp"
#include "graphics/stars.hpp"
#include "guiengine/scalable_font.hpp"
#include "karts/explosion_animation.hpp"
#include "karts/kart_gfx.hpp"
#include "karts/rescue_animation.hpp"
#include "modes/overworld.hpp"
#include "modes/soccer_world.hpp"
#include "modes/world.hpp"
#include "io/file_manager.hpp"
#include "items/attachment.hpp"
#include "items/item_manager.hpp"
#include "items/powerup.hpp"
#include "items/projectile_manager.hpp"
#include "karts/controller/end_controller.hpp"
#include "karts/abstract_characteristic.hpp"
#include "karts/abstract_kart_animation.hpp"
#include "karts/cached_characteristic.hpp"
#include "karts/controller/end_controller.hpp"
#include "karts/explosion_animation.hpp"
#include "karts/kart_gfx.hpp"
#include "karts/kart_model.hpp"
#include "karts/kart_properties_manager.hpp"
#include "karts/kart_rewinder.hpp"
#include "karts/max_speed.hpp"
#include "karts/rescue_animation.hpp"
#include "karts/skidding.hpp"
#include "modes/overworld.hpp"
#include "modes/soccer_world.hpp"
#include "modes/world.hpp"
#include "modes/linear_world.hpp"
#include "modes/overworld.hpp"
#include "modes/soccer_world.hpp"
#include "modes/world.hpp"
#include "network/network_config.hpp"
#include "network/race_event_manager.hpp"
#include "network/rewind_manager.hpp"
#include "physics/btKart.hpp"
#include "physics/btKartRaycast.hpp"
#include "physics/physics.hpp"
@ -81,6 +90,13 @@
#include <iostream>
#include <cmath>
#include <math.h>
#include <iostream>
#include <algorithm> // for min and max
#include <ICameraSceneNode.h>
#include <ISceneManager.h>
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
// Disable warning for using 'this' in base member initializer list
@ -143,6 +159,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id,
// Set position and heading:
m_reset_transform = init_transform;
m_speed = 0.0f;
m_smoothed_speed = 0.0f;
m_kart_model->setKart(this);
@ -342,6 +359,7 @@ void Kart::reset()
m_brake_time = 0.0f;
m_time_last_crash = 0.0f;
m_speed = 0.0f;
m_smoothed_speed = 0.0f;
m_current_lean = 0.0f;
m_view_blocked_by_plunger = 0.0f;
m_bubblegum_time = 0.0f;
@ -1164,18 +1182,8 @@ void Kart::eliminate()
*/
void Kart::update(float dt)
{
#ifdef DEBUG_TO_COMPARE_KART_PHYSICS
// This information is useful when comparing kart physics, e.g. to
// see top speed, acceleration (i.e. time to top speed) etc.
Log::verbose("physics", "%s t %f xyz %f %f %f %f v %f %f %f %f maxv %f",
getIdent().c_str(),
World::getWorld()->getTime(),
getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(),
getXYZ().length(),
getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(),
getVelocity().length(),
m_max_speed->getCurrentMaxSpeed());
#endif
// Reset any instand speed increase in the bullet kart
m_vehicle->resetInstantSpeed();
// update star effect (call will do nothing if stars are not activated)
m_stars_effect->update(dt);
@ -1213,10 +1221,44 @@ void Kart::update(float dt)
Vec3 front(0, 0, getKartLength()*0.5f);
m_xyz_front = getTrans()(front);
// Update the locally maintained speed of the kart (m_speed), which
// is used furthermore for engine power, camera distance etc
updateSpeed();
if(!history->replayHistory())
if(!history->replayHistory() && !RewindManager::get()->isRewinding())
m_controller->update(dt);
#undef DEBUG_CAMERA_SHAKE
#ifdef DEBUG_CAMERA_SHAKE
Log::verbose("camera", "%s t %f %f xyz %f %f %f v %f %f %f d3 %f d2 %f",
getIdent().c_str(),
World::getWorld()->getTime(), dt,
getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(),
getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(),
(Camera::getCamera(0)->getXYZ()-getXYZ()).length(),
(Camera::getCamera(0)->getXYZ()-getXYZ()).length_2d()
);
#endif
#undef DEBUG_TO_COMPARE_KART_PHYSICS
#ifdef DEBUG_TO_COMPARE_KART_PHYSICS
// This information is useful when comparing kart physics, e.g. to
// see top speed, acceleration (i.e. time to top speed) etc.
Log::verbose("physics", "%s t %f %f xyz %f %f %f v %f %f %f sk %f %d %f %f %f st %f %f",
getIdent().c_str(),
World::getWorld()->getTime(), dt,
getXYZ().getX(), getXYZ().getY(), getXYZ().getZ(),
getVelocity().getX(), getVelocity().getY(), getVelocity().getZ(),
m_skidding->getSkidFactor(),
m_skidding->getSkidState(),
m_skidding->getSteeringFraction(),
getMaxSteerAngle(),
m_speed,
m_vehicle->getWheelInfo(0).m_steering,
m_vehicle->getWheelInfo(1).m_steering
);
#endif
// if its view is blocked by plunger, decrease remaining time
if(m_view_blocked_by_plunger > 0) m_view_blocked_by_plunger -= dt;
//unblock the view if kart just became shielded
@ -1271,9 +1313,9 @@ void Kart::update(float dt)
updatePhysics(dt);
PROFILER_POP_CPU_MARKER();
if(!m_controls.m_fire) m_fire_clicked = 0;
if(!m_controls.getFire()) m_fire_clicked = 0;
if(m_controls.m_fire && !m_fire_clicked && !m_kart_animation)
if(m_controls.getFire() && !m_fire_clicked && !m_kart_animation)
{
// use() needs to be called even if there currently is no collecteable
// since use() can test if something needs to be switched on/off.
@ -1510,6 +1552,47 @@ void Kart::update(float dt)
} // update
//-----------------------------------------------------------------------------
/** Updates the local speed based on the current physical velocity. The value
* is smoothed exponentially to avoid camera stuttering (camera distance
* is dependent on speed)
*/
void Kart::updateSpeed()
{
// Compute the speed of the kart. Smooth it with previous speed to make
// the camera smoother (because of capping the speed in m_max_speed
// the speed value jitters when approaching maximum speed. This results
// in the distance between kart and camera to jitter as well (typically
// only in the order of centimetres though). Smoothing the speed value
// gets rid of this jitter, and also r
m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length();
// calculate direction of m_speed
const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform();
btVector3 forwardW(
chassisTrans.getBasis()[0][2],
chassisTrans.getBasis()[1][2],
chassisTrans.getBasis()[2][2]);
if (forwardW.dot(getVehicle()->getRigidBody()->getLinearVelocity()) < btScalar(0.))
{
m_speed = -m_speed;
}
float f = 0.3f;
m_smoothed_speed = f*m_speed + (1.0f - f)*m_smoothed_speed;
// At low velocity, forces on kart push it back and forth so we ignore this
// - quick'n'dirty workaround for bug 1776883
if (fabsf(m_speed) < 0.2f ||
dynamic_cast<RescueAnimation*> ( getKartAnimation() ) ||
dynamic_cast<ExplosionAnimation*>( getKartAnimation() ) )
{
m_speed = 0;
m_smoothed_speed = 0;
}
} // updateSpeed
//-----------------------------------------------------------------------------
/** Show fire to go with a zipper.
*/
@ -1799,7 +1882,7 @@ void Kart::handleZipper(const Material *material, bool play_sound)
engine_force = m_kart_properties->getZipperForce();
}
// Ignore a zipper that's activated while braking
if(m_controls.m_brake || m_speed<0) return;
if(m_controls.getBrake() || m_speed<0) return;
m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER,
max_speed_increase, speed_gain,
@ -1815,7 +1898,7 @@ void Kart::handleZipper(const Material *material, bool play_sound)
*/
void Kart::updateNitro(float dt)
{
if (m_controls.m_nitro && m_min_nitro_time <= 0.0f)
if (m_controls.getNitro() && m_min_nitro_time <= 0.0f)
{
m_min_nitro_time = m_kart_properties->getNitroMinConsumptionTime();
}
@ -1825,11 +1908,11 @@ void Kart::updateNitro(float dt)
// when pressing the key, don't allow the min time to go under zero.
// If it went under zero, it would be reset
if (m_controls.m_nitro && m_min_nitro_time <= 0.0f)
if (m_controls.getNitro() && m_min_nitro_time <= 0.0f)
m_min_nitro_time = 0.1f;
}
bool increase_speed = (m_controls.m_nitro && isOnGround());
bool increase_speed = (m_controls.getNitro() && isOnGround());
if (!increase_speed && m_min_nitro_time <= 0.0f)
{
if(m_nitro_sound->getStatus() == SFXBase::SFX_PLAYING)
@ -2140,16 +2223,19 @@ void Kart::updatePhysics(float dt)
// Check if accel is pressed for the first time. The actual timing
// is done in getStartupBoost - it returns 0 if the start was actually
// too slow to qualify for a boost.
if(!m_has_started && m_controls.m_accel)
if(!m_has_started && m_controls.getAccel())
{
m_has_started = true;
float f = getStartupBoost();
if(f >= 0.0f) m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_ZIPPER, 100*f);
m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER,
0.9f*f, f,
/*engine_force*/200.0f,
/*duration*/5.0f,
/*fade_out_time*/5.0f);
if(f >= 0.0f)
{
m_kart_gfx->setCreationRateAbsolute(KartGFX::KGFX_ZIPPER, 100*f);
m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER,
0.9f*f, f,
/*engine_force*/200.0f,
/*duration*/5.0f,
/*fade_out_time*/5.0f);
}
}
m_bounce_back_time-=dt;
@ -2160,8 +2246,8 @@ void Kart::updatePhysics(float dt)
if (m_flying)
updateFlying();
m_skidding->update(dt, isOnGround(), m_controls.m_steer,
m_controls.m_skid);
m_skidding->update(dt, isOnGround(), m_controls.getSteer(),
m_controls.getSkidControl());
m_vehicle->setVisualRotation(m_skidding->getVisualSkidRotation());
if(( m_skidding->getSkidState() == Skidding::SKID_ACCUMULATE_LEFT ||
m_skidding->getSkidState() == Skidding::SKID_ACCUMULATE_RIGHT ) &&
@ -2181,19 +2267,6 @@ void Kart::updatePhysics(float dt)
updateSliding();
// Compute the speed of the kart.
m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length();
// calculate direction of m_speed
const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform();
btVector3 forwardW (
chassisTrans.getBasis()[0][2],
chassisTrans.getBasis()[1][2],
chassisTrans.getBasis()[2][2]);
if (forwardW.dot(getVehicle()->getRigidBody()->getLinearVelocity()) < btScalar(0.))
m_speed *= -1.f;
// Cap speed if necessary
const Material *m = getMaterial();
@ -2201,34 +2274,6 @@ void Kart::updatePhysics(float dt)
m_max_speed->setMinSpeed(min_speed);
m_max_speed->update(dt);
// To avoid tunneling (which can happen on long falls), clamp the
// velocity in Y direction. Tunneling can happen if the Y velocity
// is larger than the maximum suspension travel (per frame), since then
// the wheel suspension can not stop/slow down the fall (though I am
// not sure if this is enough in all cases!). So the speed is limited
// to suspensionTravel / dt with dt = 1/60 (since this is the dt
// bullet is using).
// Only apply if near ground instead of purely based on speed avoiding
// the "parachute on top" look.
const Vec3 &v = m_body->getLinearVelocity();
if(/*isNearGround() &&*/ v.getY() < - m_kart_properties->getSuspensionTravel() * 60)
{
Vec3 v_clamped = v;
// clamp the speed to 99% of the maxium falling speed.
v_clamped.setY(-m_kart_properties->getSuspensionTravel() * 60 * 0.99f);
//m_body->setLinearVelocity(v_clamped);
}
//at low velocity, forces on kart push it back and forth so we ignore this
if(fabsf(m_speed) < 0.2f) // quick'n'dirty workaround for bug 1776883
m_speed = 0;
if (dynamic_cast<RescueAnimation*>(getKartAnimation()) ||
dynamic_cast<ExplosionAnimation*>(getKartAnimation()))
{
m_speed = 0;
}
updateEngineSFX();
#ifdef XX
@ -2244,7 +2289,7 @@ void Kart::updatePhysics(float dt)
);
#endif
} // updatePhysics
} // updatephysics
//-----------------------------------------------------------------------------
/** Adjust the engine sound effect depending on the speed of the kart.
@ -2301,7 +2346,7 @@ void Kart::updateEnginePowerAndBrakes(float dt)
m_body->applyTorque(btVector3(0.0, m_bubblegum_torque, 0.0));
}
if(m_controls.m_accel) // accelerating
if(m_controls.getAccel()) // accelerating
{
// For a short time after a collision disable the engine,
// so that the karts can bounce back a bit from the obstacle.
@ -2314,11 +2359,11 @@ void Kart::updateEnginePowerAndBrakes(float dt)
engine_power *= 5.0f;
// Lose some traction when skidding, to balance the advantage
if (m_controls.m_skid &&
if (m_controls.getSkidControl() &&
m_kart_properties->getSkidVisualTime() == 0)
engine_power *= 0.5f;
applyEngineForce(engine_power*m_controls.m_accel);
applyEngineForce(engine_power*m_controls.getAccel());
// Either all or no brake is set, so test only one to avoid
// resetting all brakes most of the time.
@ -2329,7 +2374,7 @@ void Kart::updateEnginePowerAndBrakes(float dt)
}
else
{ // not accelerating
if(m_controls.m_brake)
if(m_controls.getBrake())
{ // check if the player is currently only slowing down
// or moving backwards
if(m_speed > 0.0f)
@ -2365,9 +2410,9 @@ void Kart::updateEnginePowerAndBrakes(float dt)
{
m_brake_time = 0;
// lift the foot from throttle, brakes with 10% engine_power
assert(!std::isnan(m_controls.m_accel));
assert(!std::isnan(m_controls.getAccel()));
assert(!std::isnan(engine_power));
applyEngineForce(-m_controls.m_accel*engine_power*0.1f);
applyEngineForce(-m_controls.getAccel()*engine_power*0.1f);
// If not giving power (forward or reverse gear), and speed is low
// we are "parking" the kart, so in battle mode we can ambush people
@ -2436,7 +2481,7 @@ void Kart::updateFlying()
{
m_body->setLinearVelocity(m_body->getLinearVelocity() * 0.99f);
if (m_controls.m_accel)
if (m_controls.getAccel())
{
btVector3 velocity = m_body->getLinearVelocity();
if (velocity.length() < 25)
@ -2446,7 +2491,7 @@ void Kart::updateFlying()
100.0f*cos(orientation)));
}
}
else if (m_controls.m_brake)
else if (m_controls.getBrake())
{
btVector3 velocity = m_body->getLinearVelocity();
if (velocity.length() > -15)
@ -2457,9 +2502,9 @@ void Kart::updateFlying()
}
}
if (m_controls.m_steer != 0.0f)
if (m_controls.getSteer()!= 0.0f)
{
m_body->applyTorque(btVector3(0.0, m_controls.m_steer * 3500.0f, 0.0));
m_body->applyTorque(btVector3(0.0, m_controls.getSteer()*3500.0f, 0.0));
}
// dampen any roll while flying, makes the kart hard to control
@ -2695,7 +2740,7 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz,
// depending on speed)
// --------------------------------------------------------
float nitro_frac = 0;
if ( (m_controls.m_nitro || m_min_nitro_time > 0.0f) &&
if ( (m_controls.getNitro() || m_min_nitro_time > 0.0f) &&
isOnGround() && m_collected_energy > 0 )
{
// fabs(speed) is important, otherwise the negative number will
@ -2796,7 +2841,7 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz,
}
#ifdef XX
// cheap wheelie effect
if (m_controls.m_nitro)
if (m_controls.getNitro())
{
m_node->updateAbsolutePosition();
m_kart_model->getWheelNodes()[0]->updateAbsolutePosition();

View File

@ -28,19 +28,21 @@
#include "LinearMath/btTransform.h"
#include "items/powerup.hpp"
#include "graphics/render_info.hpp"
#include "items/powerup_manager.hpp" // For PowerupType
#include "karts/abstract_kart.hpp"
#include "karts/kart_properties.hpp"
#include "utils/no_copy.hpp"
class btKart;
class Attachment;
class Controller;
class Item;
class AbstractKartAnimation;
class Attachment;
class btKart;
class btUprightConstraint;
class Controller;
class HitEffect;
class Item;
class KartGFX;
class KartRewinder;
class MaxSpeed;
class ParticleEmitter;
class ParticleKind;
@ -52,8 +54,6 @@ class SlipStream;
class Stars;
class TerrainInfo;
enum KartRenderType: unsigned int;
/** The main kart class. All type of karts are of this object, but with
* different controllers. The controllers are what turn a kart into a
* player kart (i.e. the controller handle input), or an AI kart (the
@ -77,7 +77,7 @@ protected:
/** Is time flying activated */
bool m_is_jumping;
private:
protected:
/** Handles speed increase and capping due to powerup, terrain, ... */
MaxSpeed *m_max_speed;
@ -198,7 +198,11 @@ private:
/** 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. */
float m_view_blocked_by_plunger;
/** The current speed (i.e. length of velocity vector) of this kart. */
float m_speed;
/** For camera handling an exponentially smoothened value is used, which
* reduces stuttering of the camera. */
float m_smoothed_speed;
std::vector<SFXBase*> m_custom_sounds;
SFXBase *m_beep_sound;
@ -225,6 +229,7 @@ private:
void updateSliding();
void updateEnginePowerAndBrakes(float dt);
void updateEngineSFX();
void updateSpeed();
void updateNitro(float dt);
float getActualWheelForce();
void playCrashSFX(const Material* m, AbstractKart *k);
@ -234,7 +239,7 @@ public:
Kart(const std::string& ident, unsigned int world_kart_id,
int position, const btTransform& init_transform,
PerPlayerDifficulty difficulty,
KartRenderType krt);
KartRenderType krt = KRT_DEFAULT);
virtual ~Kart();
virtual void init(RaceManager::KartType type);
virtual void kartIsInRestNow();
@ -364,6 +369,9 @@ public:
/** Returns the speed of the kart in meters/second. */
virtual float getSpeed() const {return m_speed; }
// ------------------------------------------------------------------------
/** Returns the speed of the kart in meters/second. */
virtual float getSmoothedSpeed() const { return m_smoothed_speed; }
// ------------------------------------------------------------------------
/** This is used on the client side only to set the speed of the kart
* from the server information. */
virtual void setSpeed(float s) {m_speed = s; }

View File

@ -324,7 +324,7 @@ void KartGFX::updateTerrain(const ParticleKind *pk)
bool on_ground = m_kart->isOnGround() &&
m_kart->getSkidding()->getGraphicalJumpOffset()==0;
if (skidding > 1.0f && on_ground)
rate = fabsf(m_kart->getControls().m_steer) > 0.8 ? skidding - 1 : 0;
rate = fabsf(m_kart->getControls().getSteer()) > 0.8 ? skidding - 1 : 0;
else if (speed >= 0.5f && on_ground)
rate = speed/m_kart->getKartProperties()->getEngineMaxSpeed();
else

View File

@ -926,11 +926,10 @@ void KartModel::update(float dt, float distance, float steer, float speed,
m_kart->getKartProperties()->getSpeedWeightedObjectProperties().value_name
// Animation strength
float strength = 1.0f;
const float strength_factor = GET_VALUE(obj, m_strength_factor);
if (strength_factor >= 0.0f)
{
strength = speed * strength_factor;
float strength = speed * strength_factor;
btClamp<float>(strength, 0.0f, 1.0f);
}

162
src/karts/kart_rewinder.cpp Normal file
View File

@ -0,0 +1,162 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2013 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/kart_rewinder.hpp"
#include "items/attachment.hpp"
#include "items/powerup.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/max_speed.hpp"
#include "karts/skidding.hpp"
#include "modes/world.hpp"
#include "network/rewind_manager.hpp"
#include "network/network_string.hpp"
#include "physics/btKart.hpp"
#include "utils/vec3.hpp"
#include <string.h>
KartRewinder::KartRewinder(const std::string& ident,unsigned int world_kart_id,
int position, const btTransform& init_transform,
PerPlayerDifficulty difficulty,
KartRenderType krt)
: Rewinder(/*can_be_destroyed*/ false)
, Kart(ident, world_kart_id, position, init_transform, difficulty,
krt)
{
} // KartRewinder
// ----------------------------------------------------------------------------
/** Resets status in case of a resetart.
*/
void KartRewinder::reset()
{
Kart::reset();
Rewinder::reset();
} // reset
// ----------------------------------------------------------------------------
/** Saves all state information for a kart in a memory buffer. The memory
* is allocated here and the address returned. It will then be managed
* by the RewindManager. The size is used to keep track of memory usage
* for rewinding.
* \param[out] buffer Address of the memory buffer.
* \returns Size of allocated memory, or -1 in case of an error.
*/
BareNetworkString* KartRewinder::saveState() const
{
const int MEMSIZE = 13*sizeof(float) + 9+3;
BareNetworkString *buffer = new BareNetworkString(MEMSIZE);
const btRigidBody *body = getBody();
// 1) Physics values: transform and velocities
// -------------------------------------------
const btTransform &t = body->getWorldTransform();
buffer->add(t.getOrigin());
btQuaternion q = t.getRotation();
buffer->add(q);
buffer->add(body->getLinearVelocity());
buffer->add(body->getAngularVelocity());
buffer->addUInt8(m_has_started); // necessary for startup speed boost
buffer->addFloat(m_vehicle->getInstantSpeedIncrease());
// 2) Steering and other player controls
// -------------------------------------
getControls().copyToBuffer(buffer);
// 3) Attachment
// -------------
getAttachment()->saveState(buffer);
// 4) Powerup
// ----------
getPowerup()->saveState(buffer);
// 5) Max speed info
// ------------------
m_max_speed->saveState(buffer);
// 6) Skidding
// -----------
m_skidding->saveState(buffer);
return buffer;
} // saveState
// ----------------------------------------------------------------------------
/** Actually rewind to the specified state. */
void KartRewinder::rewindToState(BareNetworkString *buffer)
{
buffer->reset(); // make sure the buffer is read from the beginning
// 1) Physics values: transform and velocities
// -------------------------------------------
btTransform t;
t.setOrigin(buffer->getVec3());
t.setRotation(buffer->getQuat());
btRigidBody *body = getBody();
body->setLinearVelocity(buffer->getVec3());
body->setAngularVelocity(buffer->getVec3());
// This function also reads the velocity, so it must be called
// after the velocities are set
body->proceedToTransform(t);
// Update kart transform in case that there are access to its value
// before Moveable::update() is called (which updates the transform)
setTrans(t);
m_has_started = buffer->getUInt8()!=0; // necessary for startup speed boost
m_vehicle->instantSpeedIncreaseTo(buffer->getFloat());
// 2) Steering and other controls
// ------------------------------
getControls().setFromBuffer(buffer);
// 3) Attachment
// -------------
getAttachment()->rewindTo(buffer);
// 4) Powerup
// ----------
getPowerup()->rewindTo(buffer);
// 5) Max speed info
// ------------------
m_max_speed->rewindTo(buffer);
m_max_speed->update(0);
// 6) Skidding
// -----------
m_skidding->rewindTo(buffer);
return;
} // rewindToState
// ----------------------------------------------------------------------------
/** Called once a frame. It will add a new kart control event to the rewind
* manager if any control values have changed.
*/
void KartRewinder::update(float dt)
{
Kart::update(dt);
} // update
// ----------------------------------------------------------------------------
void KartRewinder::rewindToEvent(BareNetworkString *buffer)
{
}; // rewindToEvent

View File

@ -0,0 +1,66 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2013 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_KART_REWINDER_HPP
#define HEADER_KART_REWINDER_HPP
#include "graphics/render_info.hpp"
#include "karts/kart.hpp"
#include "network/rewinder.hpp"
#include "utils/cpp2011.hpp"
class AbstractKart;
class BareNetworkString;
class KartRewinder : public Rewinder, public Kart
{
private:
// Flags to indicate the different event types
enum { EVENT_CONTROL = 0x01,
EVENT_ATTACH = 0x02 };
public:
KartRewinder(const std::string& ident,
unsigned int world_kart_id,
int position, const btTransform& init_transform,
PerPlayerDifficulty difficulty,
KartRenderType krt = KRT_DEFAULT);
virtual ~KartRewinder() {};
virtual BareNetworkString* saveState() const;
void reset();
virtual void rewindToState(BareNetworkString *p) OVERRIDE;
virtual void rewindToEvent(BareNetworkString *p) OVERRIDE;
virtual void update(float dt);
// -------------------------------------------------------------------------
virtual void undoState(BareNetworkString *p) OVERRIDE
{
}; // undoState
// -------------------------------------------------------------------------
virtual void undoEvent(BareNetworkString *p) OVERRIDE
{
}; // undoEvent
// -------------------------------------------------------------------------
}; // Rewinder
#endif

View File

@ -62,11 +62,9 @@ void KartWithStats::reset()
void KartWithStats::update(float dt)
{
Kart::update(dt);
if(getSpeed()>m_top_speed) m_top_speed = getSpeed();
if(getControls().m_skid)
m_skidding_time += dt;
if(getControls().m_brake)
m_brake_count ++;
if(getSpeed()>m_top_speed ) m_top_speed = getSpeed();
if(getControls().getSkidControl()) m_skidding_time += dt;
if(getControls().getBrake() ) m_brake_count ++;
LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld());
if(world && !world->isOnRoad(getWorldKartId()))
m_off_track_count ++;

View File

@ -135,8 +135,7 @@ void MaxSpeed::instantSpeedIncrease(unsigned int category,
m_kart->getVehicle()->instantSpeedIncreaseTo(speed);
}
// instantSpeedIncrease
} // instantSpeedIncrease
// ----------------------------------------------------------------------------
/** Handles the update of speed increase objects. The m_duration variable
@ -161,6 +160,34 @@ void MaxSpeed::SpeedIncrease::update(float dt)
m_current_speedup -= dt*m_max_add_speed/m_fade_out_time;
} // SpeedIncrease::update
// ----------------------------------------------------------------------------
void MaxSpeed::SpeedIncrease::saveState(BareNetworkString *buffer) const
{
buffer->addFloat(m_max_add_speed);
buffer->addFloat(m_duration);
buffer->addFloat(m_fade_out_time);
buffer->addFloat(m_current_speedup);
buffer->addFloat(m_engine_force);
} // saveState
// ----------------------------------------------------------------------------
void MaxSpeed::SpeedIncrease::rewindTo(BareNetworkString *buffer,
bool is_active)
{
if(is_active)
{
m_max_add_speed = buffer->getFloat();
m_duration = buffer->getFloat();
m_fade_out_time = buffer->getFloat();
m_current_speedup = buffer->getFloat();
m_engine_force = buffer->getFloat();
}
else // make sure to disable this category
{
reset();
}
} // restoreState
// ----------------------------------------------------------------------------
/** Defines a slowdown, which is in fraction of top speed.
* \param category The category for which the speed is increased.
@ -210,6 +237,38 @@ void MaxSpeed::SpeedDecrease::update(float dt)
m_current_fraction = m_max_speed_fraction;
} // SpeedDecrease::update
// ----------------------------------------------------------------------------
/** Saves the state of an (active) speed decrease category. It is not called
* if the speed decrease is not active.
* \param buffer Buffer which will store the state information.
*/
void MaxSpeed::SpeedDecrease::saveState(BareNetworkString *buffer) const
{
buffer->addFloat(m_max_speed_fraction);
buffer->addFloat(m_fade_in_time);
buffer->addFloat(m_current_fraction);
buffer->addFloat(m_duration);
} // saveState
// ----------------------------------------------------------------------------
/** Restores a previously saved state for an active speed decrease category.
*/
void MaxSpeed::SpeedDecrease::rewindTo(BareNetworkString *buffer,
bool is_active)
{
if(is_active)
{
m_max_speed_fraction = buffer->getFloat();
m_fade_in_time = buffer->getFloat();
m_current_fraction = buffer->getFloat();
m_duration = buffer->getFloat();
}
else // make sure it is not active
{
reset();
}
} // restoreState
// ----------------------------------------------------------------------------
/** Returns how much increased speed time is left over in the given category.
* \param category Which category to report on.
@ -267,3 +326,70 @@ void MaxSpeed::update(float dt)
} // update
// ----------------------------------------------------------------------------
/** Saves the speed data in a network string for rewind.
* \param buffer Pointer to the network string to store the data.
*/
void MaxSpeed::saveState(BareNetworkString *buffer) const
{
// Save the slowdown states
// ------------------------
// Get the bit pattern of all active slowdowns
uint8_t active_slowdown = 0;
for(unsigned int i=MS_DECREASE_MIN, b=1; i<MS_DECREASE_MAX; i++, b <<=1)
{
if (m_speed_decrease[i].isActive())
active_slowdown |= b;
}
buffer->addUInt8(active_slowdown);
for(unsigned int i=MS_DECREASE_MIN, b=1; i<MS_DECREASE_MAX; i++, b <<= 1)
{
if (active_slowdown & b)
m_speed_decrease->saveState(buffer);
}
// Now save the speedup state
// --------------------------
// Get the bit pattern of all active speedups
uint8_t active_speedups = 0;
for(unsigned int i=MS_INCREASE_MIN, b=1; i<MS_INCREASE_MAX; i++, b <<= 1)
{
if(m_speed_increase[i].isActive())
active_speedups |= b;
}
buffer->addUInt8(active_speedups);
for(unsigned int i=MS_INCREASE_MIN, b=1; i<MS_INCREASE_MAX; i++, b <<= 1)
{
if(active_speedups & b)
m_speed_increase[i].saveState(buffer);
}
} // saveState
// ----------------------------------------------------------------------------
/** Restore a saved state.
* \param buffer Saved state.
*/
void MaxSpeed::rewindTo(BareNetworkString *buffer)
{
// Restore the slowdown states
// ---------------------------
// Get the bit pattern of all active slowdowns
uint8_t active_slowdown = buffer->getUInt8();
for(unsigned int i=MS_DECREASE_MIN, b=1; i<MS_DECREASE_MAX; i++, b <<= 1)
{
m_speed_decrease->rewindTo(buffer, (active_slowdown & b) == b);
}
// Restore the speedup state
// --------------------------
// Get the bit pattern of all active speedups
uint8_t active_speedups = buffer->getUInt8();
for(unsigned int i=MS_INCREASE_MIN, b=1; i<MS_INCREASE_MAX; i++, b <<= 1)
{
m_speed_increase[i].rewindTo(buffer, (active_speedups & b) == b);
}
} // rewindoTo

View File

@ -22,6 +22,7 @@
/** \defgroup karts */
class AbstractKart;
class BareNetworkString;
class MaxSpeed
{
@ -81,18 +82,27 @@ private:
/** The constructor initialised the values with a no-increase
* entry, i.e. an entry that does affect top speed at all. */
SpeedIncrease()
{
reset();
} // SpeedIncrease
// --------------------------------------------------------------------
/** Resets this increase category to be not active. */
void reset()
{
m_max_add_speed = 0;
m_duration = -9999999;
m_fade_out_time = 0;
m_current_speedup = 0;
m_engine_force = 0;
} // SpeedIncrease
} // reset
// --------------------------------------------------------------------
void update(float dt);
/** Returns the current speedup for this category. */
void saveState(BareNetworkString *buffer) const;
void rewindTo(BareNetworkString *buffer, bool is_active);
// --------------------------------------------------------------------
/** Returns the current speedup for this category. */
float getSpeedIncrease() const {return m_current_speedup;}
// --------------------------------------------------------------------
/** Returns the remaining time till the fade out time starts.
* Note that this function will return a negative value if
* the fade_out time has started or this speed increase has
@ -103,7 +113,10 @@ private:
float getEngineForce() const
{
return m_duration > 0 ? m_engine_force : 0;
}
} // getEngineForce
// --------------------------------------------------------------------
/** Returns if this speed increase is active atm. */
bool isActive() const { return m_duration > -m_fade_out_time; }
}; // SpeedIncrease
// ------------------------------------------------------------------------
@ -126,17 +139,30 @@ private:
/** The constructor initialises the data with data that won't
* affect top speed at all. */
SpeedDecrease()
{
reset();
} // SpeedDecrease
// --------------------------------------------------------------------
/** Resets the state to be inactive. */
void reset()
{
m_max_speed_fraction = 1.0f;
m_fade_in_time = 0.0f;
m_current_fraction = 1.0f;
m_duration = -1.0f;
} // SpeedDecrease
m_duration = 0.0f;
} //reset
// --------------------------------------------------------------------
void update(float dt);
void saveState(BareNetworkString *buffer) const;
void rewindTo(BareNetworkString *buffer, bool is_active);
// --------------------------------------------------------------------
/** Returns the current slowdown fracftion, taking a 'fade in'
* into account. */
float getSlowdownFraction() const {return m_current_fraction;}
// --------------------------------------------------------------------
/** Returns if this speed decrease is active atm. A duration of
* -1 indicates an ongoing effect. */
bool isActive() const { return m_duration > 0 || m_duration <= -1.0f; }
}; // SpeedDecrease
// ------------------------------------------------------------------------
@ -164,6 +190,8 @@ public:
float getSpeedIncreaseTimeLeft(unsigned int category);
void update(float dt);
void reset();
void saveState(BareNetworkString *buffer) const;
void rewindTo(BareNetworkString *buffer);
// ------------------------------------------------------------------------
/** Sets the minimum speed a kart should have. This is used to guarantee
* that e.g. zippers on ramps will always fast enough for the karts to

View File

@ -29,6 +29,7 @@
#include "karts/max_speed.hpp"
#include "karts/controller/controller.hpp"
#include "modes/world.hpp"
#include "network/network_string.hpp"
#include "physics/btKart.hpp"
#include "tracks/track.hpp"
#include "utils/log.hpp"
@ -77,7 +78,7 @@ void Skidding::reset()
m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDL, 0);
m_kart->getKartGFX()->setCreationRateAbsolute(KartGFX::KGFX_SKIDR, 0);
m_kart->getKartGFX()->updateSkidLight(0);
m_kart->getControls().m_skid = KartControl::SC_NONE;
m_kart->getControls().setSkidControl(KartControl::SC_NONE);
btVector3 rot(0, 0, 0);
// Only access the vehicle if the kart is not a ghost
@ -85,6 +86,36 @@ void Skidding::reset()
m_kart->getVehicle()->setTimedRotation(0, rot);
} // reset
// ----------------------------------------------------------------------------
/** Save the skidding state of a kart. It only saves the important physics
* values, not visual only values like m_visual_rotation, m_gfx_jump_offset,
* m_remaining_jump_time and m_jump_speed. Similarly m_real_steering is output
* of updateRewind() and will be recomputed every frame when update() is called,
* and similart m_skid_bonus_ready
* \param buffer Buffer for the state information.
*/
void Skidding::saveState(BareNetworkString *buffer)
{
buffer->addUInt8(m_skid_state);
if(m_skid_state == SKID_NONE)
return;
buffer->addFloat(m_skid_time);
buffer->addFloat(m_skid_factor);
} // saveState
// ----------------------------------------------------------------------------
/** Restores the skidding state of a kart.
* \param buffer Buffer with state information.
*/
void Skidding::rewindTo(BareNetworkString *buffer)
{
m_skid_state = (SkidState)buffer->getUInt8();
if(m_skid_state == SKID_NONE)
return;
m_skid_time = buffer->getFloat();
m_skid_factor = buffer->getFloat();
} // rewindTo
// ----------------------------------------------------------------------------
/** Computes the actual steering fraction to be used in the physics, and
* stores it in m_real_skidding. This is later used by kart to set the
@ -92,8 +123,10 @@ void Skidding::reset()
* kart skids either left or right, the steering fraction is bound by
* reduce-turn-min and reduce-turn-max.
*/
void Skidding::updateSteering(float steer, float dt)
float Skidding::updateSteering(float steer, float dt)
{
float steer_result;
const KartProperties *kp = m_kart->getKartProperties();
switch(m_skid_state)
@ -101,7 +134,7 @@ void Skidding::updateSteering(float steer, float dt)
case SKID_SHOW_GFX_LEFT:
case SKID_SHOW_GFX_RIGHT:
case SKID_NONE:
m_real_steering = steer;
steer_result = steer;
if (m_skid_time < kp->getSkidVisualTime() && m_skid_time > 0)
{
float f = m_visual_rotation - m_visual_rotation*dt/m_skid_time;
@ -115,7 +148,7 @@ void Skidding::updateSteering(float steer, float dt)
}
break;
case SKID_BREAK:
m_real_steering = steer;
steer_result = steer;
if (m_visual_rotation > 0.1f) m_visual_rotation -= 0.1f;
else if (m_visual_rotation < -0.1f) m_visual_rotation += 0.1f;
else
@ -126,33 +159,33 @@ void Skidding::updateSteering(float steer, float dt)
case SKID_ACCUMULATE_RIGHT:
{
float f = (1.0f+steer)*0.5f; // map [-1,1] --> [0, 1]
m_real_steering = kp->getSkidReduceTurnMin()
steer_result = kp->getSkidReduceTurnMin()
+ m_skid_reduce_turn_delta * f;
if(m_skid_time < kp->getSkidVisualTime())
m_visual_rotation = kp->getSkidVisual()
* m_real_steering * m_skid_time
* steer_result * m_skid_time
/ kp->getSkidVisualTime();
else
m_visual_rotation = kp->getSkidVisual() * m_real_steering;
m_visual_rotation = kp->getSkidVisual() * steer_result;
break;
}
} // SKID_ACCUMULATE_RIGHT
case SKID_ACCUMULATE_LEFT:
{
float f = (-1.0f+steer)*0.5f; // map [-1,1] --> [-1, 0]
m_real_steering = -kp->getSkidReduceTurnMin()
steer_result = -kp->getSkidReduceTurnMin()
+ m_skid_reduce_turn_delta * f;
if(m_skid_time < kp->getSkidVisualTime())
m_visual_rotation = kp->getSkidVisual()
* m_real_steering * m_skid_time
* steer_result * m_skid_time
/ kp->getSkidVisualTime();
else
m_visual_rotation = kp->getSkidVisual() * m_real_steering;
m_visual_rotation = kp->getSkidVisual() * steer_result;
break;
}
} // case SKID_ACCUMULATE_LEFT
} // switch m_skid_state
return steer_result;
} // updateSteering
// ----------------------------------------------------------------------------
@ -279,7 +312,7 @@ void Skidding::update(float dt, bool is_on_ground,
// a potential bonus. Also the rotation of the physical body to
// be in synch with the graphical kart is started (which is
// independently handled in the kart physics).
// SKID_SHOW_GFX_{LEFT<RIGHT}
// SKID_SHOW_GFX_{LEFT,RIGHT}
// Shows the skidding gfx while the bonus is available.
// FIXME: what should we do if skid key is pressed while still in
// SKID_SHOW_GFX??? Adjusting the body rotation is difficult.
@ -318,21 +351,17 @@ void Skidding::update(float dt, bool is_on_ground,
#ifdef SKID_DEBUG
#define SPEED 20.0f
updateSteering(steering, dt);
m_real_steering = updateSteering(steering, dt);
m_actual_curve->clear();
m_actual_curve->setVisible(true);
m_predicted_curve->clear();
m_predicted_curve->setVisible(true);
m_predicted_curve->setPosition(m_kart->getXYZ());
m_predicted_curve->setHeading(m_kart->getHeading());
float angle = kp
->getMaxSteerAngle(m_kart->getSpeed())
* fabsf(getSteeringFraction());
angle = kp
->getMaxSteerAngle(SPEED)
float angle = kp->getMaxSteerAngle(SPEED)
* fabsf(getSteeringFraction());
float r = kp->getWheelBase()
/ asin(angle)*1.0f;
/ asin(angle)*1.0f;
const int num_steps = 50;
@ -444,7 +473,8 @@ void Skidding::update(float dt, bool is_on_ground,
m_skid_state = SKID_NONE;
}
} // switch
updateSteering(steering, dt);
m_real_steering = updateSteering(steering, dt);
} // update
// ----------------------------------------------------------------------------

View File

@ -23,6 +23,7 @@
#include "utils/leak_check.hpp"
#include "utils/no_copy.hpp"
class BareNetworkString;
class Kart;
class ShowCurve;
@ -98,13 +99,15 @@ private:
unsigned int getSkidBonus(float *bonus_time, float *bonus_speed,
float *bonus_force) const;
void updateSteering(float steer, float dt);
float updateSteering(float steer, float dt);
public:
Skidding(Kart *kart);
~Skidding();
void reset();
void update(float dt, bool is_on_ground, float steer,
KartControl::SkidControl skidding);
void saveState(BareNetworkString *buffer);
void rewindTo(BareNetworkString *buffer);
// ------------------------------------------------------------------------
/** Determines how much the graphics model of the kart should be rotated
* additionally (for skidding), depending on how long the kart has been

View File

@ -179,6 +179,7 @@
#include "modes/profile_world.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp"
#include "network/rewind_manager.hpp"
#include "network/servers_manager.hpp"
#include "network/stk_host.hpp"
#include "online/profile_manager.hpp"
@ -395,9 +396,11 @@ void handleXmasMode()
*/
bool isEasterMode(int day, int month, int year, int before_after_days)
{
switch (UserConfigParams::m_easter_ear_mode)
{
case 0:
if (UserConfigParams::m_easter_ear_mode == 1) {
return true;
}
if (UserConfigParams::m_easter_ear_mode == 0)
{
// Compute Easter date, based on wikipedia formula
// http://en.wikipedia.org/wiki/Computus
@ -429,12 +432,9 @@ bool isEasterMode(int day, int month, int year, int before_after_days)
}
return (month > easter_start_month || (month == easter_start_month && day >= easter_start_day)) &&
(month < easter_end_month || (month == easter_end_month && day <= easter_end_day));
break;
}
case 1: return true; break;
default: return false; break;
} // switch m_xmas_mode
return false;
} // isEasterMode(day, month, year, before_after_days)
// ============================================================================
@ -880,6 +880,8 @@ int handleCmdLine()
AIBaseController::setTestAI(n);
if (CommandLine::has("--fps-debug"))
UserConfigParams::m_fps_debug = true;
if (CommandLine::has("--rewind") )
RewindManager::setEnable(true);
if(CommandLine::has("--soccer-ai-stats"))
{
UserConfigParams::m_arena_ai_stats=true;

View File

@ -35,6 +35,7 @@
#include "network/race_event_manager.hpp"
#include "network/stk_host.hpp"
#include "online/request_manager.hpp"
#include "race/history.hpp"
#include "race/race_manager.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/profiler.hpp"
@ -61,6 +62,15 @@ MainLoop::~MainLoop()
*/
float MainLoop::getLimitedDt()
{
float dt = 0;
// If we are doing a replay, use the dt from the history file
if (World::getWorld() && history->replayHistory() )
{
dt = history->updateReplayAndGetDT();
return dt;
}
// In profile mode without graphics, run with a fixed dt of 1/60
if ((ProfileWorld::isProfileMode() && ProfileWorld::isNoGraphics()) ||
UserConfigParams::m_arena_ai_stats)
@ -71,7 +81,6 @@ float MainLoop::getLimitedDt()
IrrlichtDevice* device = irr_driver->getDevice();
m_prev_time = m_curr_time;
float dt; // needed outside of the while loop
while( 1 )
{
m_curr_time = device->getTimer()->getRealTime();
@ -135,6 +144,72 @@ void MainLoop::updateRace(float dt)
//-----------------------------------------------------------------------------
/** Run the actual main loop.
* The sequnce in which various parts of STK are updated is:
* - Determine next time step size (`getLimitedDt`). This takes maximum fps
* into account (i.e. sleep if the fps would be too high), and will actually
* slow down the in-game clock if the fps are too low (if more than 3/60 of
* a second have passed, more than 3 physics time steps would be needed,
* and physics do at most 3 time steps).
* - if a race is taking place (i.e. not only a menu being shown), call
* `updateRace()`, which is a thin wrapper around a call to
* `World::updateWorld()`:
* - Update history manager (which will either set the kart position and/or
* controls when replaying, or store the current info for a replay).
* This is mostly for debugging only (though available even in release
* mode).
* - Updates Replays - either storing data when not replaying, or
* updating kart positions/control when replaying).
* - Calls `WorldStatus::update()`, which updates the race state (e.g.
* go from 'ready' to 'set' etc), and clock.
* - Updates the physics (`Physics::update()`). This will simulate all
* physical objects for the specified time with bullet.
* - Updates all karts (`Kart::update()`). Obviously the update function
* does a lot more than what is described here, this is only supposed to
* be a _very_ high level overview:
* - Updates its rewinder (to store potentially changed controls
* as events) in `KartRewinder::update()`.
* - Calls `Moveable::update()`, which takes the new position from
* the physics and saves it (and computes dependent values, like
* heading, local velocity).
* - Updates its controller. This is either:
* - an AI using `SkiddingController::update()` (which then will
* compute the new controls), or
* - a player controller using `PlayerController::update()`, which will
* handle smooth steering (in case of digital input devices steering
* is adjusted a bit over time to avoid an instant change from all
* left to all right). Input events will be handled when updating
* the irrlicht driver later at the end of the main loop.
* - Updates kart animation (like rescue, ...) if one is shown atm.
* - Update attachments.
* - update physics, i.e. taking the current steering and updating
* the bullet raycast vehicle with that data. The settings are actually
* only used in the next frame when the physics are updated.
* - Updates all cameras via `Camera::update()`. The camera position and
* rotation is adjusted according to the position etc of the kart (and
* special circumstances like rescue, falling).
* - Updates all projectiles using the projectile manager. Some of the
* projectiles are mostly handled by the physics (e.g. a cake will mainly
* check if it's out of bounds), others (like basket ball) do all
* their aiming and movement here.
* - Updates the rewind manager to store rewind states.
* - Updates the music manager.
* - Updates the input manager (which only updates internal time, actual
* input handling follows late)
* - Updates the wiimote manager. This will read the data of all wiimotes
* and feed the corresponding events to the irrlicht event system.
* - Updates the STK internal gui engine. This updates all widgets, and
* e.g. takes care of the rotation of the karts in the KartSelection
* screen using the ModelViewWidget.
* - Updates STK's irrlicht driver `IrrDriver::update()`:
* - Calls Irrlicht's `beginScene()` .
* - Renders the scene (several times with different viewport if
* split screen is being used)
* - Calls `GUIEngine::render()`, which renders all widgets with the
* help of Irrlicht's GUIEnvironment (`drawAll()`). This will also
* handle all events, i.e. all input is now handled (e.g. steering,
* firing etc are all set in the corresponding karts depending on
* user input).
* - Calls Irrlicht's `endScene()`
*/
void MainLoop::run()
{
@ -147,6 +222,11 @@ void MainLoop::run()
m_prev_time = m_curr_time;
float dt = getLimitedDt();
// Render the previous frame, and also handle all user input.
PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F);
irr_driver->update(dt);
PROFILER_POP_CPU_MARKER();
if (World::getWorld()) // race is active if world exists
{
@ -172,10 +252,6 @@ void MainLoop::run()
GUIEngine::update(dt);
PROFILER_POP_CPU_MARKER();
PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F);
irr_driver->update(dt);
PROFILER_POP_CPU_MARKER();
// Update sfx and music after graphics, so that graphics code
// can use as many threads as possible without interfering
// with audio
@ -209,6 +285,10 @@ void MainLoop::run()
PROFILER_POP_CPU_MARKER();
}
// Update world time if world exists
if (World::getWorld())
World::getWorld()->updateTime(dt);
PROFILER_POP_CPU_MARKER();
PROFILER_SYNC_FRAME();
} // while !m_abort

View File

@ -42,10 +42,12 @@
#include "karts/controller/network_player_controller.hpp"
#include "karts/kart.hpp"
#include "karts/kart_properties_manager.hpp"
#include "karts/kart_rewinder.hpp"
#include "modes/overworld.hpp"
#include "modes/profile_world.hpp"
#include "modes/soccer_world.hpp"
#include "network/network_config.hpp"
#include "network/rewind_manager.hpp"
#include "physics/btKart.hpp"
#include "physics/physics.hpp"
#include "physics/triangle_mesh.hpp"
@ -163,9 +165,11 @@ void World::init()
// constructor is called, so the wrong race gui would be created.
createRaceGUI();
RewindManager::create();
// Grab the track file
m_track = track_manager->getTrack(race_manager->getTrackName());
m_script_engine = new Scripting::ScriptEngine();
m_script_engine = new Scripting::ScriptEngine();
if(!m_track)
{
std::ostringstream msg;
@ -238,6 +242,8 @@ void World::init()
*/
void World::reset()
{
RewindManager::get()->reset();
// If m_saved_race_gui is set, it means that the restart was done
// when the race result gui was being shown. In this case restore the
// race gui (note that the race result gui is cached and so never really
@ -335,8 +341,13 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index,
int position = index+1;
btTransform init_pos = getStartTransform(index - gk);
AbstractKart *new_kart = new Kart(kart_ident, index, position, init_pos,
difficulty, KRT_DEFAULT);
AbstractKart *new_kart;
if (RewindManager::get()->isEnabled())
new_kart = new KartRewinder(kart_ident, index, position, init_pos,
difficulty);
else
new_kart = new Kart(kart_ident, index, position, init_pos, difficulty);
new_kart->init(race_manager->getKartType(index));
Controller *controller = NULL;
switch(kart_type)
@ -415,6 +426,8 @@ Controller* World::loadAIController(AbstractKart *kart)
//-----------------------------------------------------------------------------
World::~World()
{
RewindManager::destroy();
irr_driver->onUnloadWorld();
// In case that a race is aborted (e.g. track not found) m_track is 0.
@ -827,6 +840,11 @@ void World::updateWorld(float dt)
getPhase() == IN_GAME_MENU_PHASE )
return;
if (!history->replayHistory())
{
history->updateSaving(dt); // updating the saved state
}
try
{
update(dt);
@ -916,7 +934,7 @@ void World::scheduleTutorial()
{
m_schedule_exit_race = true;
m_schedule_tutorial = true;
}
} // scheduleTutorial
//-----------------------------------------------------------------------------
/** Updates the physics, all karts, the track, and projectile manager.
@ -928,7 +946,6 @@ void World::update(float dt)
assert(m_magic_number == 0xB01D6543);
#endif
PROFILER_PUSH_CPU_MARKER("World::update()", 0x00, 0x7F, 0x00);
#if MEASURE_FPS
@ -942,19 +959,15 @@ void World::update(float dt)
#endif
PROFILER_PUSH_CPU_MARKER("World::update (sub-updates)", 0x20, 0x7F, 0x00);
history->update(dt);
if(race_manager->isRecordingRace()) ReplayRecorder::get()->update(dt);
if(history->replayHistory()) dt=history->getNextDelta();
WorldStatus::update(dt);
if (m_script_engine) m_script_engine->update(dt);
RewindManager::get()->saveStates();
PROFILER_POP_CPU_MARKER();
if (!history->dontDoPhysics())
{
m_physics->update(dt);
}
PROFILER_PUSH_CPU_MARKER("World::update (Kart::upate)", 0x40, 0x7F, 0x00);
PROFILER_PUSH_CPU_MARKER("World::update (Kart::update)", 0x40, 0x7F, 0x00);
// Update all the karts. This in turn will also update the controller,
// which causes all AI steering commands set. So in the following
// physics update the new steering is taken into account.
const int kart_amount = (int)m_karts.size();
for (int i = 0 ; i < kart_amount; ++i)
{
@ -970,6 +983,14 @@ void World::update(float dt)
}
PROFILER_POP_CPU_MARKER();
if(race_manager->isRecordingRace()) ReplayRecorder::get()->update(dt);
if (m_script_engine) m_script_engine->update(dt);
if (!history->dontDoPhysics())
{
m_physics->update(dt);
}
PROFILER_PUSH_CPU_MARKER("World::update (weather)", 0x80, 0x7F, 0x00);
if (UserConfigParams::m_graphical_effects && m_weather)
{
@ -988,6 +1009,17 @@ void World::update(float dt)
#endif
} // update
// ----------------------------------------------------------------------------
/** Compute the new time, and set this new time to be used in the rewind
* manager.
* \param dt Time step size.
*/
void World::updateTime(const float dt)
{
WorldStatus::updateTime(dt);
RewindManager::get()->setCurrentTime(getTime(), dt);
} // updateTime
// ----------------------------------------------------------------------------
/** Only updates the track. The order in which the various parts of STK are
* updated is quite important (i.e. the track can't be updated as part of

View File

@ -178,7 +178,7 @@ protected:
virtual void onGo() OVERRIDE;
/** Returns true if the race is over. Must be defined by all modes. */
virtual bool isRaceOver() = 0;
virtual void update(float dt);
virtual void update(float dt) OVERRIDE;
virtual void createRaceGUI();
void updateTrack(float dt);
// ------------------------------------------------------------------------
@ -252,9 +252,10 @@ public:
// =================
virtual void init();
virtual void terminateRace() OVERRIDE;
virtual void reset();
virtual void reset() OVERRIDE;
virtual void pause(Phase phase) OVERRIDE;
virtual void unpause() OVERRIDE;
virtual void updateTime(const float dt) OVERRIDE;
virtual void getDefaultCollectibles(int *collectible_type,
int *amount );
virtual void endRaceEarly() { return; }

View File

@ -153,10 +153,21 @@ void WorldStatus::terminateRace()
} // terminateRace
//-----------------------------------------------------------------------------
/** Updates all status information, called once per frame.
/** Update, called once per frame. Called early on before physics are
* updated.
* \param dt Time step.
*/
void WorldStatus::update(float dt)
{
} // update
//-----------------------------------------------------------------------------
/** Updates the world time and clock (which might be running backwards), and
* all status information, called once per frame at the end of the main
* loop.
* \param dt Duration of time step.
*/
void WorldStatus::update(const float dt)
void WorldStatus::updateTime(const float dt)
{
switch (m_phase)
{

View File

@ -122,13 +122,14 @@ public:
WorldStatus();
virtual ~WorldStatus();
void reset();
void update(const float dt);
void setTime(const float time);
virtual void reset();
virtual void updateTime(const float dt);
virtual void update(float dt);
virtual void pause(Phase phase);
virtual void unpause();
virtual void enterRaceOverState();
virtual void terminateRace();
void setTime(const float time);
// ------------------------------------------------------------------------
// Note: GO_PHASE is both: start phase and race phase

View File

@ -0,0 +1,35 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/event_rewinder.hpp"
#include "network/rewind_manager.hpp"
/** Constructor. It will add this object to the list of all rewindable
* objects in the rewind manager.
*/
EventRewinder::EventRewinder()
{
} // Rewinder
// ----------------------------------------------------------------------------
/** Destructor.
*/
EventRewinder::~EventRewinder()
{
} // ~EventRewinder

View File

@ -0,0 +1,41 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_EVENT_REWINDER_HPP
#define HEADER_EVENT_REWINDER_HPP
class BareNetworkString;
class EventRewinder
{
public:
EventRewinder();
virtual ~EventRewinder();
/** Called when an event needs to be undone. This is called while going
* backwards for rewinding - all stored events will get an 'undo' call.
*/
virtual void undo(BareNetworkString *buffer) = 0;
/** Called when an event needs to be replayed. This is called during
* rewind, i.e. when going forward in time again.
*/
virtual void rewind(BareNetworkString *buffer) = 0;
}; // EventRewinder
#endif

View File

@ -137,6 +137,9 @@ public:
memcpy(m_buffer.data(), data, len);
} // BareNetworkString
// ------------------------------------------------------------------------
/** Allows to read a buffer from the beginning again. */
void reset() { m_current_offset = 0; }
// ------------------------------------------------------------------------
BareNetworkString& encodeString(const std::string &value);
BareNetworkString& encodeString(const irr::core::stringw &value);
@ -225,6 +228,12 @@ public:
} // operator+=
// ------------------------------------------------------------------------
/** Adds a floating point number */
BareNetworkString& add(float f)
{
return addFloat(f);
} // add
// ------------------------------------------------------------------------
/** Adds the xyz components of a Vec3 to the string. */
BareNetworkString& add(const Vec3 &xyz)
{

View File

@ -72,12 +72,12 @@ bool ControllerEventsProtocol::notifyEventAsynchronous(Event* event)
Controller *controller = World::getWorld()->getKart(kart_id)
->getController();
KartControl *controls = controller->getControls();
controls->m_brake = (serialized_1 & 0x40)!=0;
controls->m_nitro = (serialized_1 & 0x20)!=0;
controls->m_rescue = (serialized_1 & 0x10)!=0;
controls->m_fire = (serialized_1 & 0x08)!=0;
controls->m_look_back = (serialized_1 & 0x04)!=0;
controls->m_skid = KartControl::SkidControl(serialized_1 & 0x03);
controls->setBrake( (serialized_1 & 0x40)!=0);
controls->setNitro( (serialized_1 & 0x20)!=0);
controls->setRescue( (serialized_1 & 0x10)!=0);
controls->setFire( (serialized_1 & 0x08)!=0);
controls->setLookBack((serialized_1 & 0x04)!=0);
controls->setSkidControl(KartControl::SkidControl(serialized_1 & 0x03));
controller->action(action, action_value);
}
@ -110,19 +110,19 @@ void ControllerEventsProtocol::controllerAction(Controller* controller,
KartControl* controls = controller->getControls();
uint8_t serialized_1 = 0;
serialized_1 |= (controls->m_brake==true);
serialized_1 |= (controls->getBrake()==true);
serialized_1 <<= 1;
serialized_1 |= (controls->m_nitro==true);
serialized_1 |= (controls->getNitro()==true);
serialized_1 <<= 1;
serialized_1 |= (controls->m_rescue==true);
serialized_1 |= (controls->getRescue()==true);
serialized_1 <<= 1;
serialized_1 |= (controls->m_fire==true);
serialized_1 |= (controls->getFire()==true);
serialized_1 <<= 1;
serialized_1 |= (controls->m_look_back==true);
serialized_1 |= (controls->getLookBack()==true);
serialized_1 <<= 2;
serialized_1 += controls->m_skid;
uint8_t serialized_2 = (uint8_t)(controls->m_accel*255.0);
uint8_t serialized_3 = (uint8_t)(controls->m_steer*127.0);
serialized_1 += controls->getSkidControl();
uint8_t serialized_2 = (uint8_t)(controls->getAccel()*255.0);
uint8_t serialized_3 = (uint8_t)(controls->getSteer()*127.0);
NetworkString *ns = getNetworkString(13);
ns->addFloat(World::getWorld()->getTime());

View File

@ -0,0 +1,57 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/rewind_info.hpp"
#include "modes/world.hpp"
#include "physics/physics.hpp"
/** Constructor for a state: it only takes the size, and allocates a buffer
* for all state info.
* \param size Necessary buffer size for a state.
*/
RewindInfo::RewindInfo(float time, bool is_confirmed)
{
m_time = time;
m_is_confirmed = is_confirmed;
} // RewindInfo
// ============================================================================
RewindInfoTime::RewindInfoTime(float time)
: RewindInfo(time, /*is_confirmed*/true)
{
} // RewindInfoTime
// ============================================================================
RewindInfoState::RewindInfoState(float time, Rewinder *rewinder,
BareNetworkString *buffer, bool is_confirmed)
: RewindInfoRewinder(time, rewinder, buffer, is_confirmed)
{
m_local_physics_time = World::getWorld()->getPhysics()->getPhysicsWorld()
->getLocalTime();
} // RewindInfoState
// ============================================================================
RewindInfoEvent::RewindInfoEvent(float time, EventRewinder *event_rewinder,
BareNetworkString *buffer, bool is_confirmed)
: RewindInfo(time, is_confirmed)
{
m_event_rewinder = event_rewinder;
m_buffer = buffer;
} // RewindInfoEvent

222
src/network/rewind_info.hpp Normal file
View File

@ -0,0 +1,222 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_REWIND_INFO_HPP
#define HEADER_REWIND_INFO_HPP
#include "network/event_rewinder.hpp"
#include "network/network_string.hpp"
#include "network/rewinder.hpp"
#include "utils/leak_check.hpp"
#include "utils/ptr_vector.hpp"
#include <assert.h>
#include <vector>
/** Used to store rewind information for a given time for all rewind
* instances.
* Rewind information can either be a state (for example a kart would
* have position, rotation, linear and angular velocity, ... as state),
* or an event (for a kart that would be pressing or releasing of a key).
* State changes and events can be delivered in different frequencies,
* and might be released (to save memory) differently: A state can be
* reproduced from a previous state by replaying the simulation taking
* all events into account.
*/
class RewindInfo
{
private:
LEAK_CHECK();
/** Time when this state was taken. */
float m_time;
/** A confirmed event is one that was sent from the server. When
* rewinding we have to start with a confirmed state for each
* object. */
bool m_is_confirmed;
public:
RewindInfo(float time, bool is_confirmed);
/** Called when going back in time to undo any rewind information. */
virtual void undo() = 0;
/** This is called while going forwards in time again to reach current
* time. */
virtual void rewind() = 0;
// ------------------------------------------------------------------------
virtual ~RewindInfo() { }
// ------------------------------------------------------------------------
/** Returns the time at which this rewind state was saved. */
float getTime() const { return m_time; }
// ------------------------------------------------------------------------
/** Sets if this RewindInfo is confirmed or not. */
void setConfirmed(bool b) { m_is_confirmed = b; }
// ------------------------------------------------------------------------
/** Returns if this state is confirmed. */
bool isConfirmed() const { return m_is_confirmed; }
// ------------------------------------------------------------------------
/** If this rewind info is an event. Subclasses will overwrite this. */
virtual bool isEvent() const { return false; }
// ------------------------------------------------------------------------
/** If this rewind info is time info. Subclasses will overwrite this. */
virtual bool isTime() const { return false; }
// ------------------------------------------------------------------------
/** If this rewind info is an event. Subclasses will overwrite this. */
virtual bool isState() const { return false; }
// ------------------------------------------------------------------------
}; // RewindInfo
// ============================================================================
/** A rewind info abstract class that keeps track of a rewinder object, and
* has a BareNetworkString buffer which is used to store a state or event.
*/
class RewindInfoRewinder : public RewindInfo
{
private:
/** Pointer to the buffer which stores all states. */
BareNetworkString *m_buffer;
protected:
/** The Rewinder instance for which this data is. */
Rewinder *m_rewinder;
public:
RewindInfoRewinder(float time, Rewinder *rewinder,
BareNetworkString *buffer, bool is_confirmed)
: RewindInfo(time, is_confirmed)
{
m_rewinder = rewinder;
m_buffer = buffer;
} // RewindInfoRewinder
// ------------------------------------------------------------------------
virtual ~RewindInfoRewinder()
{
delete m_buffer;
} // ~RewindInfoRewinder
// ------------------------------------------------------------------------
/** Returns a pointer to the state buffer. */
BareNetworkString *getBuffer() const { return m_buffer; }
}; // RewindInfoRewinder
// ============================================================================
class RewindInfoTime : public RewindInfo
{
private:
public:
RewindInfoTime(float time);
virtual ~RewindInfoTime() {};
// ------------------------------------------------------------------------
virtual bool isTime() const { return true; }
// ------------------------------------------------------------------------
/** Called when going back in time to undo any rewind information.
* Does actually nothing. */
virtual void undo() {}
// ------------------------------------------------------------------------
/** Rewinds to this state. Nothing to be done for time info. */
virtual void rewind() {}
}; // class RewindInfoTime
// ============================================================================
class RewindInfoState: public RewindInfoRewinder
{
private:
/** The 'left over' time from the physics. */
float m_local_physics_time;
public:
RewindInfoState(float time, Rewinder *rewinder,
BareNetworkString *buffer, bool is_confirmed);
virtual ~RewindInfoState() {};
// ------------------------------------------------------------------------
/** Returns the left-over physics time. */
float getLocalPhysicsTime() const { return m_local_physics_time; }
// ------------------------------------------------------------------------
virtual bool isState() const { return true; }
// ------------------------------------------------------------------------
/** Called when going back in time to undo any rewind information.
* It calls undoState in the rewinder. */
virtual void undo()
{
m_rewinder->undoState(getBuffer());
} // undo
// ------------------------------------------------------------------------
/** Rewinds to this state. This is called while going forwards in time
* again to reach current time. It will call rewindToState().
* if the state is a confirmed state. */
virtual void rewind()
{
if (isConfirmed())
m_rewinder->rewindToState(getBuffer());
else
{
// TODO
// Handle replacing of stored states.
}
} // rewind
}; // class RewindInfoState
// ============================================================================
class RewindInfoEvent : public RewindInfo
{
private:
/** Pointer to the event rewinder responsible for this event. */
EventRewinder *m_event_rewinder;
/** Buffer with the event data. */
BareNetworkString *m_buffer;
public:
RewindInfoEvent(float time, EventRewinder *event_rewinder,
BareNetworkString *buffer, bool is_confirmed);
virtual ~RewindInfoEvent()
{
delete m_buffer;
} // ~RewindInfoEvent
// ------------------------------------------------------------------------
virtual bool isEvent() const { return true; }
// ------------------------------------------------------------------------
/** Called when going back in time to undo any rewind information.
* It calls undoEvent in the rewinder. */
virtual void undo()
{
m_buffer->reset();
m_event_rewinder->undo(m_buffer);
} // undo
// ------------------------------------------------------------------------
/** This is called while going forwards in time again to reach current
* time. Calls rewindEvent().
*/
virtual void rewind()
{
// Make sure to reset the buffer so we read from the beginning
m_buffer->reset();
m_event_rewinder->rewind(m_buffer);
} // rewind
// ------------------------------------------------------------------------
/** Returns the buffer with the event information in it. */
BareNetworkString *getBuffer() { return m_buffer; }
}; // class RewindIndoEvent
#endif

View File

@ -0,0 +1,405 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2013 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/rewind_manager.hpp"
#include "graphics/irr_driver.hpp"
#include "modes/world.hpp"
#include "network/network_string.hpp"
#include "network/rewinder.hpp"
#include "network/rewind_info.hpp"
#include "physics/physics.hpp"
#include "race/history.hpp"
#include "utils/log.hpp"
RewindManager* RewindManager::m_rewind_manager = NULL;
bool RewindManager::m_enable_rewind_manager = false;
/** Creates the singleton. */
RewindManager *RewindManager::create()
{
assert(!m_rewind_manager);
m_rewind_manager = new RewindManager();
return m_rewind_manager;
} // create
// ----------------------------------------------------------------------------
/** Destroys the singleton. */
void RewindManager::destroy()
{
assert(m_rewind_manager);
delete m_rewind_manager;
m_rewind_manager = NULL;
} // destroy
// ============================================================================
/** The constructor.
*/
RewindManager::RewindManager()
{
reset();
} // RewindManager
// ----------------------------------------------------------------------------
/** Frees all saved state information. Note that the Rewinder data must be
* freed elsewhere.
*/
RewindManager::~RewindManager()
{
// Destroying the
for(unsigned int i=0; i<m_rewind_info.size(); i++)
{
delete m_rewind_info[i];
m_rewind_info[i] = NULL;
}
m_rewind_info.clear();
} // ~RewindManager
// ----------------------------------------------------------------------------
/** Frees all saved state information and all destroyable rewinder.
*/
void RewindManager::reset()
{
#ifdef REWIND_SEARCH_STATS
m_count_of_comparisons = 0;
m_count_of_searches = 0;
#endif
m_is_rewinding = false;
m_overall_state_size = 0;
m_state_frequency = 0.1f; // save 10 states a second
m_last_saved_state = -9999.9f; // forces initial state save
if(!m_enable_rewind_manager) return;
AllRewinder::iterator r = m_all_rewinder.begin();
while(r!=m_all_rewinder.end())
{
if(!(*r)->canBeDestroyed())
{
r++;
continue;
}
Rewinder *rewinder = *r;
r = m_all_rewinder.erase(r);
// FIXME Do we really want to delete this here?
delete rewinder;
}
for(unsigned int i=0; i<m_rewind_info.size(); i++)
{
delete m_rewind_info[i];
}
m_rewind_info.clear();
} // reset
// ----------------------------------------------------------------------------
void RewindManager::insertRewindInfo(RewindInfo *ri)
{
#ifdef REWIND_SEARCH_STATS
m_count_of_searches++;
#endif
float t = ri->getTime();
if(ri->isEvent())
{
// If there are several infos for the same time t,
// events must be inserted at the end
AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin();
while(i!=m_rewind_info.rend() &&
(*i)->getTime() > t)
{
#ifdef REWIND_SEARCH_STATS
m_count_of_comparisons++;
#endif
i++;
}
AllRewindInfo::iterator insert_point = i.base();
m_rewind_info.insert(insert_point,ri);
return;
}
else // is a state
{
// If there are several infos for the same time t,
// a state must be inserted first
AllRewindInfo::reverse_iterator i = m_rewind_info.rbegin();
while(i!=m_rewind_info.rend() && (*i)->getTime() >= t)
{
#ifdef REWIND_SEARCH_STATS
m_count_of_comparisons++;
#endif
i++;
}
AllRewindInfo::iterator insert_point = i.base();
m_rewind_info.insert(insert_point,ri);
return;
}
} // insertRewindInfo
// ----------------------------------------------------------------------------
/** Returns the first (i.e. lowest) index i in m_rewind_info which fulfills
* time(i) < target_time <= time(i+1) and is a state. This is the state
* from which a rewind can start - all states for the karts will be well
* defined.
* \param time Time for which an index is searched.
* \return Index in m_rewind_info after which to add rewind data.
*/
unsigned int RewindManager::findFirstIndex(float target_time) const
{
// For now do a linear search, even though m_rewind_info is sorted
// I would expect that most insertions will be towards the (very)
// end of the list, since rewinds should be for short periods of time.
// Note that after finding an entry in a binary search, you still
// have to do a linear search to find the last entry with the same
// time in order to minimise the later necessary memory move.
// Gather some statistics about search for now:
#ifdef REWIND_SEARCH_STATS
m_count_of_searches++;
#endif
int index = m_rewind_info.size()-1;
int index_last_state = -1;
while(index>=0)
{
#ifdef REWIND_SEARCH_STATS
m_count_of_comparisons++;
#endif
if(m_rewind_info[index]->isState())
{
if(m_rewind_info[index]->getTime()<target_time)
{
return index;
}
index_last_state = index;
}
index--;
}
if(index_last_state<0)
{
Log::fatal("RewindManager",
"Can't find any state when rewinding to %f - aborting.",
target_time);
}
// Otherwise use the last found state - not much we can do in this case.
Log::error("RewindManager",
"Can't find state to rewind to for time %f, using %f.",
target_time, m_rewind_info[index_last_state]->getTime());
return index_last_state; // avoid compiler warning
} // findFirstIndex
// ----------------------------------------------------------------------------
/** Adds an event to the rewind data. The data to be stored must be allocated
* and not freed by the caller!
* \param time Time at which the event was recorded.
* \param buffer Pointer to the event data.
*/
void RewindManager::addEvent(EventRewinder *event_rewinder,
BareNetworkString *buffer)
{
if(m_is_rewinding)
{
delete buffer;
Log::error("RewindManager", "Adding event when rewinding");
return;
}
RewindInfo *ri = new RewindInfoEvent(getCurrentTime(), event_rewinder,
buffer, /*is confirmed*/true);
insertRewindInfo(ri);
} // addEvent
// ----------------------------------------------------------------------------
/** Determines if a new state snapshot should be taken, and if so calls all
* rewinder to do so.
* \param dt Time step size.
*/
void RewindManager::saveStates()
{
if(!m_enable_rewind_manager ||
m_all_rewinder.size()==0 ||
m_is_rewinding ) return;
float time = World::getWorld()->getTime();
if(time - m_last_saved_state < m_state_frequency)
{
// No full state necessary, add a dummy entry for the time
// which increases replay precision (same time step size)
RewindInfo *ri = new RewindInfoTime(getCurrentTime());
insertRewindInfo(ri);
return;
}
// For now always create a snapshot.
for(unsigned int i=0; i<m_all_rewinder.size(); i++)
{
BareNetworkString *buffer = m_all_rewinder[i]->saveState();
if(buffer && buffer->size()>=0)
{
m_overall_state_size += buffer->size();
RewindInfo *ri = new RewindInfoState(getCurrentTime(),
m_all_rewinder[i], buffer,
/*is_confirmed*/true);
assert(ri);
insertRewindInfo(ri);
} // size >= 0
else
delete buffer; // NULL or 0 byte buffer
}
Log::verbose("RewindManager", "%f allocated %ld bytes search %d/%d=%f",
World::getWorld()->getTime(), m_overall_state_size,
m_count_of_comparisons, m_count_of_searches,
float(m_count_of_comparisons)/ float(m_count_of_searches) );
m_last_saved_state = time;
} // saveStates
// ----------------------------------------------------------------------------
/** Rewinds to the specified time.
* \param t Time to rewind to.
*/
void RewindManager::rewindTo(float rewind_time)
{
assert(!m_is_rewinding);
m_is_rewinding = true;
Log::info("rewind", "Rewinding to %f", rewind_time);
history->doReplayHistory(History::HISTORY_NONE);
// First find the state to which we need to rewind
// ------------------------------------------------
unsigned int index = findFirstIndex(rewind_time);
if(!m_rewind_info[index]->isState())
{
Log::error("RewindManager", "No state for rewind to %f, state %d.",
rewind_time, index);
return;
}
// Then undo the rewind infos going backwards in time
// --------------------------------------------------
for(int i=m_rewind_info.size()-1; i>=(int)index; i--)
{
m_rewind_info[i]->undo();
// Now all states after the time we rewind to are not confirmed
// anymore. They need to be rewritten when going forward during
// the rewind.
if(m_rewind_info[i]->isState() &&
m_rewind_info[i]->getTime() > m_rewind_info[index]->getTime() )
m_rewind_info[i]->setConfirmed(false);
} // for i>state
// Rewind the required state(s)
// ----------------------------
World *world = World::getWorld();
float current_time = world->getTime();
// Get the (first) full state to which we have to rewind
RewindInfoState *state =
dynamic_cast<RewindInfoState*>(m_rewind_info[index]);
// Store the time to which we have to replay to
float exact_rewind_time = state->getTime();
// Now start the rewind with the full state:
world->setTime(exact_rewind_time);
float local_physics_time = state->getLocalPhysicsTime();
world->getPhysics()->getPhysicsWorld()->setLocalTime(local_physics_time);
// Restore all states from the current time - the full state of a race
// will be potentially stored in several state objects. State can be NULL
// if the next event is not a state
while(state && state->getTime()==exact_rewind_time)
{
state->rewind();
index++;
if(index>=m_rewind_info.size()) break;
state = dynamic_cast<RewindInfoState*>(m_rewind_info[index]);
}
// Now go forward through the list of rewind infos:
// ------------------------------------------------
while( world->getTime() < current_time &&
index < (int)m_rewind_info.size() )
{
// Now handle all states and events at the current time before
// updating the world:
while(index < (int)m_rewind_info.size() &&
m_rewind_info[index]->getTime()<=world->getTime()+0.001f)
{
if(m_rewind_info[index]->isState())
{
// TOOD: replace the old state with a new state.
// For now just set it to confirmed
m_rewind_info[index]->setConfirmed(true);
}
else if(m_rewind_info[index]->isEvent())
{
m_rewind_info[index]->rewind();
}
index++;
}
float dt = determineTimeStepSize(index, current_time);
world->updateWorld(dt);
#define SHOW_ROLLBACK
#ifdef SHOW_ROLLBACK
irr_driver->update(dt);
#endif
world->updateTime(dt);
}
m_is_rewinding = false;
} // rewindTo
// ----------------------------------------------------------------------------
/** Determines the next time step size to use when recomputing the physics.
* The time step size is either 1/60 (default physics), or less, if there
* is an even to handle before that time.
* \param next_state The next state to replay.
* \param end_time The end time to which we must replay forward. Don't
* return a dt that would be bigger tham this value.
* \return The time step size to use in the next simulation step.
*/
float RewindManager::determineTimeStepSize(int next_state, float end_time)
{
// If there is a next state (which is known to have a different time)
// use the time difference to determine the time step size.
if(next_state < (int)m_rewind_info.size())
return m_rewind_info[next_state]->getTime() - World::getWorld()->getTime();
// Otherwise, i.e. we are rewinding the last state/event, take the
// difference between that time and the world time at which the rewind
// was triggered.
return end_time - m_rewind_info[next_state-1]->getTime();
float dt = 1.0f/60.0f;
float t = World::getWorld()->getTime();
if(m_rewind_info[next_state]->getTime() < t + dt)
{
// Since we have RewindInfo at that time, it is certain that
/// this time is before (or at) end_time, not after.
return m_rewind_info[next_state]->getTime()-t;
}
return t+dt < end_time ? dt : end_time - t;
} // determineTimeStepSize

View File

@ -0,0 +1,194 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2013 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_REWIND_MANAGER_HPP
#define HEADER_REWIND_MANAGER_HPP
#include "network/rewinder.hpp"
#include "utils/ptr_vector.hpp"
#include <assert.h>
#include <vector>
class RewindInfo;
class EventRewinder;
/** \ingroup network
* This class manages rewinding. It keeps track of:
* - states for each rewindable object (for example a kart would have
* its position, rotation, linear and angular velocity etc as state)
* States can be confirmed (i.e. were received by the network server
* and are therefore confirmed to be conrrect), or not (just a snapshot
* on this client, which can save time in rewinding later).
* - events for each rewindable object (for example any change in the kart
* controls, like steering, fire, ... are an event). While states can be
* discarded (especially unconfirmed ones), e.g. to save space, events
* will always be kept (in order to allow replaying).
* For each object that is to be rewinded an instance of Rewinder needs to be
* declared (usually inside of the object it can rewind). This instance
* is automatically registered with the RewindManager.
* All states and events are stored in a RewindInfo object. All RewindInfo
* objects are stored in a list sorted by time.
* When a rewind to time T is requested, the following takes place:
* 1. Go back in time:
* Determine the latest time t_min < T so that each rewindable objects
* has at least one state before T. For each state that is skipped during
* this process `undoState()` is being called, and for each event
* `undoEvent()` of the Rewinder.
* 2. Restore state at time `t_min`
* For each Rewinder the state at time t_min is restored by calling
* `rewindToState(char *)`.
* TODO: atm there is no guarantee that each object will have a state
* at a given time. We either need to work around that, or make sure
* to store at least an unconfirmed state whenever we receive a
* confirmed state.
* 3. Rerun the simulation till the current time t_current is reached:
* 1. Determine the time `t_next` of the next frame. This is either
* current_time + 1/60 (physics default time step size), or less
* if RewindInfo at an earlier time is available).
* This determines the time step size for the next frame (i.e.
* `t_next - t_current`).
* 2. For all RewindInfo at time t_next call:
* - `restoreState()` if the RewindInfo is a confirmed state
* - `discardState()` if the RewindInfo is an unconfirmed state
* TODO: still missing, and instead of discard perhaps
* store a new state??
* - `rewindToEvent()` if the RewindInfo is an event
* 3. Do one step of world simulation, using the updated (confirmed)
* states and newly set events (e.g. kart input).
*/
class RewindManager
{
private:
/** Singleton pointer. */
static RewindManager *m_rewind_manager;
/** En- or Disable the rewind manager. This is used to disable storing
* rewind data in case of local races only. */
static bool m_enable_rewind_manager;
typedef std::vector<Rewinder *> AllRewinder;
/** A list of all objects that can be rewound. */
AllRewinder m_all_rewinder;
/** Pointer to all saved states. */
typedef std::vector<RewindInfo*> AllRewindInfo;
AllRewindInfo m_rewind_info;
/** Overall amount of memory allocated by states. */
unsigned int m_overall_state_size;
/** Indicates if currently a rewind is happening. */
bool m_is_rewinding;
/** How much time between consecutive state saves. */
float m_state_frequency;
/** Time at which the last state was saved. */
float m_last_saved_state;
/** The current time to be used in all states/events. This is used to
* give all states and events during one frame the same time, even
* if e.g. states are saved before world time is increased, other
* events later. */
float m_current_time;
/** The current time step size. */
float m_time_step;
#define REWIND_SEARCH_STATS
#ifdef REWIND_SEARCH_STATS
/** Gather some statistics about how many comparisons we do,
* to find out if it's worth doing a binary search.*/
mutable int m_count_of_comparisons;
mutable int m_count_of_searches;
#endif
RewindManager();
~RewindManager();
unsigned int findFirstIndex(float time) const;
void insertRewindInfo(RewindInfo *ri);
float determineTimeStepSize(int state, float max_time);
public:
// First static functions to manage rewinding.
// ===========================================
static RewindManager *create();
static void destroy();
// ------------------------------------------------------------------------
/** Sets the time that is to be used for all further states or events,
* and the time step size. This is necessary so that states/events before
* and after World::m_time is increased have the same time stamp.
* \param t Time.
* \param dt Time step size.
*/
void setCurrentTime(float t, float dt)
{
m_current_time = t;
m_time_step = dt;
} // setCurrentTime
// ------------------------------------------------------------------------
/** Returns the current time. */
float getCurrentTime() const { return m_current_time; }
// ------------------------------------------------------------------------
float getCurrentTimeStep() const { return m_time_step; }
// ------------------------------------------------------------------------
/** En- or disables rewinding. */
static void setEnable(bool m) { m_enable_rewind_manager = m;}
// ------------------------------------------------------------------------
/** Returns if rewinding is enabled or not. */
static bool isEnabled() { return m_enable_rewind_manager; }
// ------------------------------------------------------------------------
/** Returns the singleton. This function will not automatically create
* the singleton. */
static RewindManager *get()
{
assert(m_rewind_manager);
return m_rewind_manager;
} // get
// ------------------------------------------------------------------------
void reset();
void saveStates();
void rewindTo(float target_time);
void addEvent(EventRewinder *event_rewinder, BareNetworkString *buffer);
// ------------------------------------------------------------------------
/** Adds a Rewinder to the list of all rewinders.
* \return true If rewinding is enabled, false otherwise.
*/
bool addRewinder(Rewinder *rewinder)
{
if(!m_enable_rewind_manager) return false;
m_all_rewinder.push_back(rewinder);
return true;
} // addRewinder
// ------------------------------------------------------------------------
/** Returns true if currently a rewind is happening. */
bool isRewinding() const { return m_is_rewinding; }
}; // RewindManager
#endif

37
src/network/rewinder.cpp Normal file
View File

@ -0,0 +1,37 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2013 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/rewinder.hpp"
#include "network/rewind_manager.hpp"
/** Constructor. It will add this object to the list of all rewindable
* objects in the rewind manager.
*/
Rewinder::Rewinder(bool can_be_destroyed)
{
m_can_be_destroyed = can_be_destroyed;
RewindManager::get()->addRewinder(this);
} // Rewinder
// ----------------------------------------------------------------------------
/** Destructor.
*/
Rewinder::~Rewinder()
{
} // ~Rewinder

71
src/network/rewinder.hpp Normal file
View File

@ -0,0 +1,71 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2013 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_REWINDER_HPP
#define HEADER_REWINDER_HPP
class BareNetworkString;
class Rewinder
{
private:
bool m_can_be_destroyed;
public:
Rewinder(bool can_be_destroyed);
virtual ~Rewinder();
/** Provides a copy of the state of the object in one memory buffer.
* The memory is managed by the RewindManager.
* \param[out] buffer The address of the memory buffer with the state.
* \return Size of the buffer, or -1 in case of an error.
*/
virtual BareNetworkString* saveState() const = 0;
/** Called when an event needs to be undone. This is called while going
* backwards for rewinding - all stored events will get an 'undo' call.
*/
virtual void undoEvent(BareNetworkString *buffer) = 0;
/** Called when an event needs to be replayed. This is called during
* rewind, i.e. when going forward in time again.
*/
virtual void rewindToEvent(BareNetworkString *buffer) = 0;
/** Called when a state needs to be replayed. This is called during
* rewind, i.e. when going forward in time again, and only for confirmed
* states.
*/
virtual void rewindToState(BareNetworkString *buffer) = 0;
/** Undo the effects of the given state, but do not rewind to that
* state (which is done by rewindTo). This is called while going
* backwards for rewinding - all stored events will get an 'undo' call.
*/
virtual void undoState(BareNetworkString *buffer) = 0;
// -------------------------------------------------------------------------
/** Nothing to do here. */
virtual void reset() {};
// -------------------------------------------------------------------------
/** True if this rewinder can be destroyed. Karts can not be destroyed,
* cakes can. This is used by the RewindManager in reset. */
bool canBeDestroyed() const { return m_can_be_destroyed; }
}; // Rewinder
#endif

View File

@ -91,10 +91,9 @@ namespace Online
/** Request a login using the saved credentials of the user. */
void OnlinePlayerProfile::requestSavedSession()
{
SignInRequest *request = NULL;
if (m_online_state == OS_SIGNED_OUT && hasSavedSession())
{
request = new SignInRequest(true);
SignInRequest *request = new SignInRequest(true);
setUserDetails(request, "saved-session");
// The userid must be taken from the saved data,

View File

@ -117,8 +117,7 @@ void btKart::reset()
updateWheelTransform(i, true);
}
m_visual_wheels_touch_ground = false;
m_zipper_active = false;
m_zipper_velocity = btScalar(0);
m_zipper_speed = btScalar(0);
m_skid_angular_velocity = 0;
m_is_skidding = false;
m_allow_sliding = false;
@ -813,7 +812,7 @@ void btKart::updateFriction(btScalar timeStep)
(btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject;
if(!groundObject) continue;
if(m_zipper_active && m_zipper_velocity > 0)
if(m_zipper_speed > 0)
{
if (wheel==2 || wheel==3)
{
@ -821,7 +820,7 @@ void btKart::updateFriction(btScalar timeStep)
// reached. So compute the impulse to accelerate the
// kart up to that speed:
m_forwardImpulse[wheel] =
0.5f*(m_zipper_velocity -
0.5f*(m_zipper_speed -
getRigidBody()->getLinearVelocity().length())
/ m_chassisBody->getInvMass();
}
@ -850,7 +849,7 @@ void btKart::updateFriction(btScalar timeStep)
// bullet then tries to offset by applying a backward
// impulse - which is a bit too big, causing a impulse
// backwards, ... till the kart is shaking backwards and
// forwards. By onlyu applying half of the impulse in cae
// forwards. By only applying half of the impulse in case
// of low friction this goes away.
if(wheelInfo.m_brake && fabsf(rollingFriction)<10)
rollingFriction*=0.5f;
@ -885,8 +884,8 @@ void btKart::updateFriction(btScalar timeStep)
} // for (int wheel=0; wheel<getNumWheels(); wheel++)
m_zipper_active = false;
m_zipper_velocity = 0;
// Note: don't reset zipper speed, or the kart rewinder will
// get incorrect zipper information.
// The kart just stopped skidding. Adjust the velocity so that
// it points in the right direction.
@ -1064,8 +1063,12 @@ void btKart::setSliding(bool active)
*/
void btKart::instantSpeedIncreaseTo(float speed)
{
m_zipper_active = true;
m_zipper_velocity = speed;
// Avoid that a speed 'increase' might cause a slowdown
if (m_chassisBody->getLinearVelocity().length2() > speed*speed)
{
return;
}
m_zipper_speed = speed;
} // activateZipper
// ----------------------------------------------------------------------------

View File

@ -69,12 +69,9 @@ private:
btScalar m_damping;
btVehicleRaycaster *m_vehicleRaycaster;
/** True if a zipper is active for that kart. */
bool m_zipper_active;
/** The zipper velocity (i.e. the velocity the kart should reach in
/** The zipper speed (i.e. the velocity the kart should reach in
* the first frame that the zipper is active). */
btScalar m_zipper_velocity;
btScalar m_zipper_speed;
/** The angular velocity to be applied when the kart skids.
* 0 means no skidding. */
@ -274,6 +271,11 @@ public:
m_additional_rotation = rot/t;
m_time_additional_rotation = t;
} // setTimedTorque
// ------------------------------------------------------------------------
/** Returns the current zipper speed. */
float getInstantSpeedIncrease() const { return m_zipper_speed; }
// ------------------------------------------------------------------------
void resetInstantSpeed() { m_zipper_speed = 0; }
}; // class btKart
#endif //BT_KART_HPP

View File

@ -38,8 +38,13 @@ public:
/** Resets m_localTime to 0. This allows more precise replay of
* physics, which is important for replaying histories. */
virtual void resetLocalTime() { m_localTime = 0; }
void resetLocalTime() { m_localTime = 0; }
// ------------------------------------------------------------------------
/** Sets the local time to a specified value. Used in rewinding. */
void setLocalTime(float t) { m_localTime = t; }
// ------------------------------------------------------------------------
/** Gets the local time. */
float getLocalTime() const { return m_localTime; }
}; // STKDynamicsWorld
#endif
/* EOF */

View File

@ -23,6 +23,7 @@
#include "io/file_manager.hpp"
#include "modes/world.hpp"
#include "karts/abstract_kart.hpp"
#include "network/rewind_manager.hpp"
#include "physics/physics.hpp"
#include "race/race_manager.hpp"
#include "tracks/track.hpp"
@ -74,19 +75,6 @@ void History::allocateMemory(int number_of_frames)
m_all_rotations.resize(number_of_frames*num_karts);
} // allocateMemory
//-----------------------------------------------------------------------------
/** Depending on mode either saves the data for the current time step, or
* replays the data.
* /param dt Time step.
*/
void History::update(float dt)
{
if(m_replay_mode==HISTORY_NONE)
updateSaving(dt);
else
updateReplay(dt);
} // update
//-----------------------------------------------------------------------------
/** Saves the current history.
* \param dt Time step size.
@ -123,7 +111,7 @@ void History::updateSaving(float dt)
/** Sets the kart position and controls to the recorded history value.
* \param dt Time step size.
*/
void History::updateReplay(float dt)
float History::updateReplayAndGetDT()
{
m_current++;
World *world = World::getWorld();
@ -131,9 +119,17 @@ void History::updateReplay(float dt)
{
Log::info("History", "Replay finished");
m_current = 0;
// This is useful to use a reproducable rewind problem:
// replay it with history, for debugging only
#undef DO_REWIND_AT_END_OF_HISTORY
#ifdef DO_REWIND_AT_END_OF_HISTORY
RewindManager::get()->rewindTo(5.0f);
exit(-1);
#else
// Note that for physics replay all physics parameters
// need to be reset, e.g. velocity, ...
world->reset();
#endif
}
unsigned int num_karts = world->getNumKarts();
for(unsigned k=0; k<num_karts; k++)
@ -147,10 +143,11 @@ void History::updateReplay(float dt)
}
else
{
kart->setControls(m_all_controls[index]);
kart->getControls().set(m_all_controls[index]);
}
}
} // updateReplay
return m_all_deltas[m_current];
} // updateReplayAndGetDT
//-----------------------------------------------------------------------------
/** Saves the history stored in the internal data structures into a file called
@ -208,8 +205,8 @@ void History::Save()
for(int k=0; k<num_karts; k++)
{
fprintf(fd, "%f %f %d %f %f %f %f %f %f %f\n",
m_all_controls[index+k].m_steer,
m_all_controls[index+k].m_accel,
m_all_controls[index+k].getSteer(),
m_all_controls[index+k].getAccel(),
m_all_controls[index+k].getButtonsCompressed(),
m_all_xyz[index+k].getX(), m_all_xyz[index+k].getY(),
m_all_xyz[index+k].getZ(),
@ -314,6 +311,11 @@ void History::Load()
sscanf(s, "delta: %f\n",&m_all_deltas[i]);
}
// We need to disable the rewind manager here (otherwise setting the
// KartControl data would access the rewind manager).
bool rewind_manager_was_enabled = RewindManager::isEnabled();
RewindManager::setEnable(false);
for(int i=0; i<m_size; i++)
{
for(unsigned int k=0; k<num_karts; k++)
@ -321,19 +323,21 @@ void History::Load()
unsigned int index = num_karts * i+k;
fgets(s, 1023, fd);
int buttonsCompressed;
float x,y,z,rx,ry,rz,rw;
float x,y,z,rx,ry,rz,rw, steer, accel;
sscanf(s, "%f %f %d %f %f %f %f %f %f %f\n",
&m_all_controls[index].m_steer,
&m_all_controls[index].m_accel,
&buttonsCompressed,
&steer, &accel, &buttonsCompressed,
&x, &y, &z,
&rx, &ry, &rz, &rw
);
m_all_controls[index].setSteer(steer);
m_all_controls[index].setAccel(accel);
m_all_xyz[index] = Vec3(x,y,z);
m_all_rotations[index] = btQuaternion(rx,ry,rz,rw);
m_all_controls[index].setButtonsCompressed(char(buttonsCompressed));
} // for i
} // for k
RewindManager::setEnable(rewind_manager_was_enabled);
fprintf(fd, "History file end.\n");
fclose(fd);
} // Load

View File

@ -77,26 +77,21 @@ private:
std::vector<std::string> m_kart_ident;
void allocateMemory(int number_of_frames);
void updateSaving(float dt);
void updateReplay(float dt);
public:
History ();
void startReplay ();
void initRecording ();
void update (float dt);
void Save ();
void Load ();
void updateSaving(float dt);
float updateReplayAndGetDT();
// -------------------I-----------------------------------------------------
/** Returns the identifier of the n-th kart. */
const std::string& getKartIdent(unsigned int n)
{
return m_kart_ident[n];
}
// ------------------------------------------------------------------------
/** Returns the size of the next timestep. */
float getNextDelta () const { return m_all_deltas[m_current]; }
} // getKartIdent
// ------------------------------------------------------------------------
/** Returns if a history is replayed, i.e. the history mode is not none. */
bool replayHistory () const { return m_replay_mode != HISTORY_NONE; }

View File

@ -378,7 +378,7 @@ namespace Scripting
Log::error("Scripting", "The script ended with an exception.");
// Write some information about the script exception
asIScriptFunction *func = ctx->GetExceptionFunction();
//asIScriptFunction *func = ctx->GetExceptionFunction();
//std::cout << "func: " << func->GetDeclaration() << std::endl;
//std::cout << "modl: " << func->GetModuleName() << std::endl;
//std::cout << "sect: " << func->GetScriptSectionName() << std::endl;
@ -520,7 +520,6 @@ namespace Scripting
{
if (m_callback_delegate != NULL)
{
asIScriptEngine* engine = World::getWorld()->getScriptEngine()->getEngine();
m_callback_delegate->Release();
}
}

View File

@ -975,8 +975,6 @@ bool CScriptArray::Less(const void *a, const void *b, bool asc, asIScriptContext
}
else
{
int r = 0;
if( subTypeId & asTYPEID_OBJHANDLE )
{
// Allow sort to work even if the array contains null handles
@ -988,7 +986,7 @@ bool CScriptArray::Less(const void *a, const void *b, bool asc, asIScriptContext
if( cache && cache->cmpFunc )
{
// TODO: Add proper error handling
r = ctx->Prepare(cache->cmpFunc); assert(r >= 0);
int r = ctx->Prepare(cache->cmpFunc); assert(r >= 0);
if( subTypeId & asTYPEID_OBJHANDLE )
{

View File

@ -215,7 +215,7 @@ void CreditsScreen::loadedFromFile()
irr::core::stringw translators_credits = _("translator-credits");
const int MAX_PER_SCREEN = 6;
const unsigned MAX_PER_SCREEN = 6;
if (translators_credits != L"translator-credits")
{

View File

@ -177,8 +177,6 @@ void EditTrackScreen::init()
// -----------------------------------------------------------------------------
void EditTrackScreen::loadTrackList()
{
bool belongs_to_group;
DynamicRibbonWidget* tracks_widget = getWidget<DynamicRibbonWidget>("tracks");
assert(tracks_widget != NULL);
@ -187,7 +185,7 @@ void EditTrackScreen::loadTrackList()
for (unsigned int i = 0; i < track_manager->getNumberOfTracks(); i++)
{
Track* t = track_manager->getTrack(i);
belongs_to_group = (m_track_group.empty() ||
bool belongs_to_group = (m_track_group.empty() ||
m_track_group == ALL_TRACKS_GROUP_ID ||
t->isInGroup(m_track_group) );
if (!t->isArena() && !t->isSoccer() &&

View File

@ -1158,13 +1158,12 @@ void KartSelectionScreen::allPlayersDone()
if (selected_kart == RANDOM_KART_ID)
{
// don't select an already selected kart
int random_id;
// to prevent infinite loop in case they are all locked
int count = 0;
bool done = false;
do
{
random_id = random.get(item_count);
int random_id = random.get(item_count);
// valid kart if it can bt used, and is either not locked,
// or it's a multiplayer race.
if (items[random_id].m_code_name != ID_DONT_USE &&

View File

@ -621,11 +621,10 @@ bool OptionsScreenDevice::conflictsBetweenKbdConfig(PlayerAction action,
PlayerAction from,
PlayerAction to)
{
KeyboardConfig* other_kbd_config;
int id = m_config->getBinding(action).getId();
for (int i=0; i < input_manager->getDeviceManager()->getKeyboardAmount(); i++)
{
other_kbd_config =
KeyboardConfig* other_kbd_config =
input_manager->getDeviceManager()->getKeyboardConfig(i);
if (m_config != other_kbd_config &&

View File

@ -604,7 +604,7 @@ void RaceGUI::drawEnergyMeter(int x, int y, const AbstractKart *kart,
video::SMaterial m;
if(kart->getControls().m_nitro || kart->isOnMinNitroTime())
if(kart->getControls().getNitro() || kart->isOnMinNitroTime())
m.setTexture(0, m_gauge_full_bright);
else
m.setTexture(0, m_gauge_full);

View File

@ -241,7 +241,6 @@ void ServerSelection::onUpdate(float dt)
m_server_list_widget->clear();
ServersManager *manager = ServersManager::get();
loadList();
m_server_list_widget->addItem("spacer", L"");
m_server_list_widget->addItem("loading",

View File

@ -30,6 +30,7 @@
#include "irrlicht.h"
#include <algorithm>
#include <cstdint>
#include <string>
/** Constructor for a checkline.
@ -171,7 +172,7 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos,
bool previous_sign;
if (kart_index == -1)
if (kart_index == UINT_MAX)
{
core::vector2df p = old_pos.toIrrVector2d();
previous_sign = (m_line.getPointOrientation(p) >= 0);
@ -212,10 +213,10 @@ bool CheckLine::isTriggered(const Vec3 &old_pos, const Vec3 &new_pos,
else
result = false;
if (kart_index != -1)
if (kart_index != UINT_MAX)
m_previous_sign[kart_index] = sign;
if (result && kart_index != -1)
if (result && kart_index != UINT_MAX)
{
LinearWorld* lw = dynamic_cast<LinearWorld*>(w);
if (lw != NULL)