Redo swatter for network

This commit is contained in:
Benau 2019-03-02 16:41:32 +08:00
parent 17fd68e662
commit 9b8b0b7c1e
8 changed files with 296 additions and 243 deletions

View File

@ -43,6 +43,8 @@
#include "tracks/track.hpp" #include "tracks/track.hpp"
#include "utils/constants.hpp" #include "utils/constants.hpp"
#include <IAnimatedMeshSceneNode.h>
/** Initialises the attachment each kart has. /** Initialises the attachment each kart has.
*/ */
Attachment::Attachment(AbstractKart* kart) Attachment::Attachment(AbstractKart* kart)
@ -70,7 +72,6 @@ Attachment::Attachment(AbstractKart* kart)
std::string debug_name = kart->getIdent()+" (attachment)"; std::string debug_name = kart->getIdent()+" (attachment)";
m_node->setName(debug_name.c_str()); m_node->setName(debug_name.c_str());
#endif #endif
m_node->setAnimationEndCallback(this);
m_node->setParent(m_kart->getNode()); m_node->setParent(m_kart->getNode());
m_node->setVisible(false); m_node->setVisible(false);
} // Attachment } // Attachment
@ -109,46 +110,19 @@ Attachment::~Attachment()
*/ */
void Attachment::set(AttachmentType type, int ticks, void Attachment::set(AttachmentType type, int ticks,
AbstractKart *current_kart, AbstractKart *current_kart,
bool disable_swatter_animation,
bool set_by_rewind_parachute) bool set_by_rewind_parachute)
{ {
bool was_bomb = m_type == ATTACH_BOMB; bool was_bomb = m_type == ATTACH_BOMB;
// Don't override currently player swatter removing bomb animation int16_t prev_ticks = m_ticks_left;
/*Swatter* s = dynamic_cast<Swatter*>(m_plugin);
if (s && s->isRemovingBomb())
return;
bool was_bomb = (m_type == ATTACH_BOMB) && !disable_swatter_animation;
scene::ISceneNode* bomb_scene_node = NULL;
if (was_bomb && type == ATTACH_SWATTER)
{
// let's keep the bomb node, and create a new one for
// the new attachment
bomb_scene_node = m_node;
m_node = irr_driver->addAnimatedMesh(
attachment_manager->getMesh(Attachment::ATTACH_BOMB), "bomb");
#ifdef DEBUG
std::string debug_name = m_kart->getIdent() + " (attachment)";
m_node->setName(debug_name.c_str());
#endif
m_node->setAnimationEndCallback(this);
m_node->setParent(m_kart->getNode());
m_node->setVisible(false);
}*/
clear(); clear();
// If necessary create the appropriate plugin which encapsulates // If necessary create the appropriate plugin which encapsulates
// the associated behavior // the associated behavior
switch(type) switch(type)
{ {
case ATTACH_SWATTER : case ATTACH_SWATTER:
//if (m_kart->getIdent() == "nolok") m_plugin =
// m_node->setMesh(attachment_manager->getMesh(ATTACH_NOLOKS_SWATTER)); new Swatter(m_kart, was_bomb ? prev_ticks : -1, ticks, this);
//else
// m_node->setMesh(attachment_manager->getMesh(type));
//m_plugin = new Swatter(m_kart, was_bomb, bomb_scene_node, ticks);
break; break;
default: default:
break; break;
@ -214,14 +188,12 @@ void Attachment::clear(bool update_graphical_now)
void Attachment::saveState(BareNetworkString *buffer) const void Attachment::saveState(BareNetworkString *buffer) const
{ {
// We use bit 6 to indicate if a previous owner is defined for a bomb, // We use bit 6 to indicate if a previous owner is defined for a bomb,
// bit 7 to indicate if the attachment is swatter removing animation // bit 7 to indicate if the attachment is a plugin
assert(ATTACH_MAX < 64); assert(ATTACH_MAX < 64);
uint8_t bit_7 = 0; uint8_t bit_7 = 0;
Swatter* s = dynamic_cast<Swatter*>(m_plugin); if (m_plugin)
if (s)
{ {
bit_7 = s->isRemovingBomb() ? 1 : 0; bit_7 = 1 << 7;
bit_7 <<= 7;
} }
uint8_t type = m_type | (( (m_type==ATTACH_BOMB) && (m_previous_owner!=NULL) ) uint8_t type = m_type | (( (m_type==ATTACH_BOMB) && (m_previous_owner!=NULL) )
? (1 << 6) : 0 ) | bit_7; ? (1 << 6) : 0 ) | bit_7;
@ -233,6 +205,8 @@ void Attachment::saveState(BareNetworkString *buffer) const
buffer->addUInt8(m_previous_owner->getWorldKartId()); buffer->addUInt8(m_previous_owner->getWorldKartId());
// m_initial_speed is not saved, on restore state it will // m_initial_speed is not saved, on restore state it will
// be set to the kart speed, which has already been restored // be set to the kart speed, which has already been restored
if (m_plugin)
m_plugin->saveState(buffer);
} }
} // saveState } // saveState
@ -243,19 +217,14 @@ void Attachment::saveState(BareNetworkString *buffer) const
void Attachment::rewindTo(BareNetworkString *buffer) void Attachment::rewindTo(BareNetworkString *buffer)
{ {
uint8_t type = buffer->getUInt8(); uint8_t type = buffer->getUInt8();
bool is_removing_bomb = (type >> 7 & 1) == 1; bool is_plugin = (type >> 7 & 1) == 1;
Swatter* s = dynamic_cast<Swatter*>(m_plugin);
// If locally removing a bomb
if (s)
is_removing_bomb = s->isRemovingBomb();
// mask out bit 6 and 7 // mask out bit 6 and 7
AttachmentType new_type = AttachmentType(type & 63); AttachmentType new_type = AttachmentType(type & 63);
type &= 127; type &= 127;
// If there is no attachment, clear the attachment if necessary and exit // If there is no attachment, clear the attachment if necessary and exit
if (new_type == ATTACH_NOTHING && !is_removing_bomb) if (new_type == ATTACH_NOTHING)
{ {
if (m_type != new_type) if (m_type != new_type)
clear(); clear();
@ -277,9 +246,18 @@ void Attachment::rewindTo(BareNetworkString *buffer)
m_previous_owner = NULL; m_previous_owner = NULL;
} }
// If playing kart animation, don't rewind to any attacment if (is_plugin)
if (is_removing_bomb) {
return; if (!m_plugin)
m_plugin = new Swatter(m_kart, -1, 0, this);
m_plugin->restoreState(buffer);
}
else
{
// Remove unconfirmed plugin
delete m_plugin;
m_plugin = NULL;
}
m_type = new_type; m_type = new_type;
m_ticks_left = ticks_left; m_ticks_left = ticks_left;
@ -475,17 +453,10 @@ void Attachment::update(int ticks)
if (m_plugin) if (m_plugin)
{ {
int discard_ticks = m_plugin->updateAndTestFinished(ticks); if (m_plugin->updateAndTestFinished(ticks))
if (discard_ticks != -1)
{ {
// Save it for rewinding clear(); // also removes the plugin
m_ticks_left = return;
discard_ticks - World::getWorld()->getTicksSinceStart();
if (m_ticks_left <= 0)
{
clear(); // also removes the plugin
return;
}
} }
} }
@ -555,7 +526,11 @@ void Attachment::updateGraphics(float dt)
// Add the suitable graphical effects if different attachment is set // Add the suitable graphical effects if different attachment is set
if (m_type != m_graphical_type) if (m_type != m_graphical_type)
{ {
// Old attachement is cleared, add suitable sfx effects // Attachement is different, reset and add suitable sfx effects
m_node->setPosition(core::vector3df(0.0f, 0.0f, 0.0f));
m_node->setRotation(core::vector3df(0.0f, 0.0f, 0.0f));
m_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
m_node->setLoopMode(true);
switch (m_type) switch (m_type)
{ {
case ATTACH_NOTHING: case ATTACH_NOTHING:
@ -605,6 +580,9 @@ void Attachment::updateGraphics(float dt)
m_graphical_type = m_type; m_graphical_type = m_type;
} }
if (m_plugin)
m_plugin->updateGrahpics(dt);
if (m_type != ATTACH_NOTHING) if (m_type != ATTACH_NOTHING)
{ {
m_node->setVisible(true); m_node->setVisible(true);
@ -644,9 +622,6 @@ void Attachment::updateGraphics(float dt)
else else
m_node->setVisible(false); m_node->setVisible(false);
if (m_plugin)
m_plugin->updateGrahpics(dt);
switch (m_type) switch (m_type)
{ {
case ATTACH_BOMB: case ATTACH_BOMB:
@ -690,11 +665,3 @@ float Attachment::weightAdjust() const
? m_kart->getKartProperties()->getAnvilWeight() ? m_kart->getKartProperties()->getAnvilWeight()
: 0.0f; : 0.0f;
} // weightAdjust } // weightAdjust
// ----------------------------------------------------------------------------
/** Inform any eventual plugin when an animation is done. */
void Attachment::OnAnimationEnd(scene::IAnimatedMeshSceneNode* node)
{
if(m_plugin)
m_plugin->onAnimationEnd();
} // OnAnimationEnd

View File

@ -19,13 +19,16 @@
#ifndef HEADER_ATTACHMENT_HPP #ifndef HEADER_ATTACHMENT_HPP
#define HEADER_ATTACHMENT_HPP #define HEADER_ATTACHMENT_HPP
#include "config/stk_config.hpp"
#include "items/attachment_plugin.hpp" #include "items/attachment_plugin.hpp"
#include "utils/no_copy.hpp" #include "utils/no_copy.hpp"
#include <IAnimatedMeshSceneNode.h>
using namespace irr; using namespace irr;
namespace irr
{
namespace scene { class IAnimatedMeshSceneNode; }
}
class AbstractKart; class AbstractKart;
class BareNetworkString; class BareNetworkString;
class ItemState; class ItemState;
@ -44,7 +47,7 @@ class SFXBase;
* a scene node). * a scene node).
* \ingroup items * \ingroup items
*/ */
class Attachment: public NoCopy, public scene::IAnimationEndCallBack class Attachment: public NoCopy
{ {
public: public:
// Some loop in attachment.cpp depend on ATTACH_FIRST and ATTACH_MAX. // Some loop in attachment.cpp depend on ATTACH_FIRST and ATTACH_MAX.
@ -124,7 +127,6 @@ public:
void handleCollisionWithKart(AbstractKart *other); void handleCollisionWithKart(AbstractKart *other);
void set (AttachmentType type, int ticks, void set (AttachmentType type, int ticks,
AbstractKart *previous_kart=NULL, AbstractKart *previous_kart=NULL,
bool disable_swatter_animation = false,
bool set_by_rewind_parachute = false); bool set_by_rewind_parachute = false);
void rewindTo(BareNetworkString *buffer); void rewindTo(BareNetworkString *buffer);
void saveState(BareNetworkString *buffer) const; void saveState(BareNetworkString *buffer) const;
@ -153,13 +155,6 @@ public:
/** Return the currently associated scene node (used by e.g the swatter) */ /** Return the currently associated scene node (used by e.g the swatter) */
scene::IAnimatedMeshSceneNode* getNode() {return m_node;} scene::IAnimatedMeshSceneNode* getNode() {return m_node;}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** 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) { }
// ------------------------------------------------------------------------
void reset() void reset()
{ {
clear(); clear();

View File

@ -23,6 +23,7 @@
class AbstractKart; class AbstractKart;
class Attachment; class Attachment;
class BareNetworkString;
/** /**
* \ingroup items * \ingroup items
@ -38,13 +39,15 @@ class AttachmentPlugin
{ {
protected: protected:
/** Kart the attachment is attached to. */ /** Kart the attachment is attached to. */
AbstractKart *m_kart; AbstractKart* m_kart;
Attachment* m_attachment;
public: public:
/** Constructor for a plugin. */ /** Constructor for a plugin. */
AttachmentPlugin(AbstractKart *kart) AttachmentPlugin(AbstractKart *kart, Attachment* attachment)
{ {
m_kart = kart; m_kart = kart;
m_attachment = attachment;
} }
virtual ~AttachmentPlugin() {} virtual ~AttachmentPlugin() {}
@ -53,13 +56,13 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Updates a plugin. This is called once each time frame. If the /** Updates a plugin. This is called once each time frame. If the
* function returns a non-negative number, the attachment is discarded * function returns true, the attachment is discarded. */
* when world ticks >= that number. */ virtual bool updateAndTestFinished(int ticks) = 0;
virtual int updateAndTestFinished(int ticks) = 0;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Called when the animation of the Attachment's node is done. */ virtual void restoreState(BareNetworkString *buffer) {}
virtual void onAnimationEnd() {} // ------------------------------------------------------------------------
virtual void saveState(BareNetworkString *buffer) const {}
}; // AttachmentPlugin }; // AttachmentPlugin
#endif #endif

View File

@ -40,8 +40,7 @@
#include "karts/explosion_animation.hpp" #include "karts/explosion_animation.hpp"
#include "karts/kart_properties.hpp" #include "karts/kart_properties.hpp"
#include "modes/capture_the_flag.hpp" #include "modes/capture_the_flag.hpp"
#include "network/network_config.hpp" #include "network/network_string.hpp"
#include "network/rewind_info.hpp"
#include "network/rewind_manager.hpp" #include "network/rewind_manager.hpp"
#define SWAT_POS_OFFSET core::vector3df(0.0, 0.2f, -0.4f) #define SWAT_POS_OFFSET core::vector3df(0.0, 0.2f, -0.4f)
@ -50,59 +49,39 @@
#define SWAT_ANGLE_OFFSET (90.0f + 15.0f) #define SWAT_ANGLE_OFFSET (90.0f + 15.0f)
#define SWATTER_ANIMATION_SPEED 100.0f #define SWATTER_ANIMATION_SPEED 100.0f
// ----------------------------------------------------------------------------
/** Constructor: creates a swatter at a given attachment for a kart. If there /** Constructor: creates a swatter at a given attachment for a kart. If there
* was a bomb attached, it triggers the replace bomb animations. * was a bomb attached, it triggers the replace bomb animations.
* \param attachment The attachment instance where the swatter is attached to. * \param attachment The attachment instance where the swatter is attached to.
* \param kart The kart to which the swatter is attached. * \param kart The kart to which the swatter is attached.
* \param was_bomb True if the kart had a bomb as attachment. * \param bomb_ticks Remaining bomb time in ticks, -1 if none.
* \param bomb_scene_node The scene node of the bomb (i.e. the previous * \param ticks Swatter duration.
* attachment scene node). * \param attachment class attachment from karts.
*/ */
Swatter::Swatter(AbstractKart *kart, bool was_bomb, Swatter::Swatter(AbstractKart *kart, int16_t bomb_ticks, int ticks,
scene::ISceneNode* bomb_scene_node, int ticks) Attachment* attachment)
: AttachmentPlugin(kart), : AttachmentPlugin(kart, attachment)
m_swatter_start_ticks(World::getWorld()->getTicksSinceStart()),
m_swatter_end_ticks(World::getWorld()->getTicksSinceStart() + ticks)
{ {
m_animation_phase = SWATTER_AIMING; m_animation_phase = SWATTER_AIMING;
m_discard_now = false; m_discard_now = false;
m_target = NULL;
m_closest_kart = NULL; m_closest_kart = NULL;
m_bomb_scene_node = bomb_scene_node; m_discard_ticks = World::getWorld()->getTicksSinceStart() + ticks;
m_swat_bomb_frame = 0.0f; m_bomb_remaining = bomb_ticks;
m_scene_node = NULL;
// Setup the node m_bomb_scene_node = NULL;
m_scene_node = kart->getAttachment()->getNode(); m_swatter_duration = stk_config->time2Ticks(
m_scene_node->setPosition(SWAT_POS_OFFSET); kart->getKartProperties()->getSwatterDuration());
if (m_bomb_remaining != -1)
if (was_bomb)
{ {
m_scene_node->setMesh(attachment_manager
->getMesh(Attachment::ATTACH_SWATTER_ANIM));
m_scene_node->setRotation(core::vector3df(0.0, -180.0, 0.0));
m_scene_node->setAnimationSpeed(0.9f);
m_scene_node->setCurrentFrame(0.0f);
m_scene_node->setLoopMode(false);
// There are 40 frames in blender for swatter_anim.blender // There are 40 frames in blender for swatter_anim.blender
// so 40 / 25 * 120 // so 40 / 25 * 120
m_removed_bomb_ticks = m_discard_ticks =
World::getWorld()->getTicksSinceStart() + 192; World::getWorld()->getTicksSinceStart() +
stk_config->time2Ticks(40.0f / 25.0f);
} }
else
{
m_removed_bomb_ticks = std::numeric_limits<int>::max();
m_scene_node->setAnimationSpeed(0);
}
m_swat_sound = NULL; m_swat_sound = NULL;
m_start_swat_ticks = std::numeric_limits<int>::max(); m_swatter_animation_ticks = 0;
m_end_swat_ticks = std::numeric_limits<int>::max(); m_played_swatter_animation = false;
#ifndef SERVER_ONLY
if (kart->getIdent() == "nolok")
m_swat_sound = SFXManager::get()->createSoundSource("hammer");
else
m_swat_sound = SFXManager::get()->createSoundSource("swatter");
#endif
} // Swatter } // Swatter
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -127,55 +106,139 @@ Swatter::~Swatter()
void Swatter::updateGrahpics(float dt) void Swatter::updateGrahpics(float dt)
{ {
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
if (m_removed_bomb_ticks != std::numeric_limits<int>::max()) if (m_bomb_remaining != -1)
{ {
m_swat_bomb_frame += dt*25.0f; if (!m_scene_node)
m_scene_node->setRotation(core::vector3df(0.0, -180.0, 0.0));
m_scene_node->setCurrentFrame(m_swat_bomb_frame);
if (m_swat_bomb_frame >= 32.5f && m_bomb_scene_node != NULL)
{ {
m_bomb_scene_node->setPosition(m_bomb_scene_node m_scene_node = m_kart->getAttachment()->getNode();
->getPosition() + core::vector3df(-dt*15.0f, 0.0f, 0.0f) ); m_scene_node->setPosition(SWAT_POS_OFFSET);
m_bomb_scene_node->setRotation(m_bomb_scene_node m_scene_node->setMesh(attachment_manager
->getRotation() + core::vector3df(-dt*15.0f, 0.0f, 0.0f) ); ->getMesh(Attachment::ATTACH_SWATTER_ANIM));
m_scene_node->setRotation(core::vector3df(0.0, -180.0, 0.0));
m_scene_node->setAnimationSpeed(0.9f);
m_scene_node->setCurrentFrame(0.0f);
m_scene_node->setLoopMode(false);
} }
if (!m_bomb_scene_node)
if (m_swat_bomb_frame >= m_scene_node->getEndFrame())
{ {
return; m_bomb_scene_node = irr_driver->addAnimatedMesh(
} attachment_manager->getMesh(Attachment::ATTACH_BOMB), "bomb");
else if (m_swat_bomb_frame >= 35) #ifdef DEBUG
{ std::string debug_name = m_kart->getIdent() + " (attachment)";
if (m_bomb_scene_node != NULL) m_bomb_scene_node->setName(debug_name.c_str());
#endif
m_bomb_scene_node->setParent(m_kart->getNode());
float time_left = stk_config->ticks2Time(m_bomb_remaining);
if (time_left <= (m_bomb_scene_node->getEndFrame() -
m_bomb_scene_node->getStartFrame() - 1))
{ {
irr_driver->removeNode(m_bomb_scene_node); m_bomb_scene_node->setCurrentFrame(
m_bomb_scene_node = NULL; m_bomb_scene_node->getEndFrame()
- m_bomb_scene_node->getStartFrame() - 1 - time_left);
} }
m_bomb_scene_node->setAnimationSpeed(0.0f);
}
float swat_bomb_frame = stk_config->ticks2Time(
World::getWorld()->getTicksSinceStart() -
(m_discard_ticks - stk_config->time2Ticks(40.0f / 25.0f)))
* 25.0f;
if (swat_bomb_frame >= (float)m_scene_node->getEndFrame())
swat_bomb_frame = (float)m_scene_node->getEndFrame();
m_scene_node->setRotation(core::vector3df(0.0, -180.0, 0.0));
m_scene_node->setCurrentFrame(swat_bomb_frame);
if (swat_bomb_frame >= 32.5f && m_bomb_scene_node != NULL)
{
m_bomb_scene_node->setPosition(
core::vector3df(-(swat_bomb_frame - 32.5f), 0.0f, 0.0f));
m_bomb_scene_node->setRotation(
core::vector3df(-(swat_bomb_frame - 32.5f), 0.0f, 0.0f));
}
if (swat_bomb_frame >= 35)
{
m_bomb_scene_node->setVisible(false);
} // bom_frame > 35 } // bom_frame > 35
} // if removing bomb } // if removing bomb
else
{
if (!m_scene_node)
{
m_scene_node = m_kart->getAttachment()->getNode();
if (m_kart->getIdent() == "nolok")
{
m_scene_node->setMesh(attachment_manager
->getMesh(Attachment::ATTACH_NOLOKS_SWATTER));
}
else
{
m_scene_node->setMesh(attachment_manager
->getMesh(Attachment::ATTACH_SWATTER));
}
m_scene_node->setPosition(SWAT_POS_OFFSET);
m_scene_node->setLoopMode(false);
m_scene_node->setAnimationSpeed(0.0f);
}
if (!m_swat_sound)
{
if (m_kart->getIdent() == "nolok")
m_swat_sound = SFXManager::get()->createSoundSource("hammer");
else
m_swat_sound = SFXManager::get()->createSoundSource("swatter");
}
if (!m_discard_now)
{
switch (m_animation_phase)
{
case SWATTER_AIMING:
{
pointToTarget();
m_played_swatter_animation = false;
}
break;
case SWATTER_TO_TARGET:
{
if (!m_played_swatter_animation)
{
m_played_swatter_animation = true;
// Setup the animation
m_scene_node->setCurrentFrame(0.0f);
m_scene_node->setAnimationSpeed(SWATTER_ANIMATION_SPEED);
Vec3 swatter_pos =
m_kart->getTrans()(Vec3(SWAT_POS_OFFSET));
m_swat_sound->setPosition(swatter_pos);
m_swat_sound->play();
}
pointToTarget();
}
break;
case SWATTER_FROM_TARGET:
break;
}
}
}
#endif #endif
} // updateGrahpics } // updateGrahpics
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Updates an armed swatter: it checks for any karts that are close enough /** Updates an armed swatter: it checks for any karts that are close enough
* and not invulnerable, it swats the kart. * and not invulnerable, it swats the kart.
* \param dt Time step size. * \param ticks Time step size.
* \return World ticks to discard the swatter. * \return True if the attachment should be discarded.
*/ */
int Swatter::updateAndTestFinished(int ticks) bool Swatter::updateAndTestFinished(int ticks)
{ {
const int ticks_start = World::getWorld()->getTicksSinceStart(); const int ticks_start = World::getWorld()->getTicksSinceStart();
if (m_removed_bomb_ticks != std::numeric_limits<int>::max()) if (World::getWorld()->getTicksSinceStart() > m_discard_ticks)
{ return true;
if (ticks_start >= m_removed_bomb_ticks)
return m_removed_bomb_ticks;
return -1;
} // if removing bomb
if (RewindManager::get()->isRewinding()) // If removing bomb animation playing, it will only ends when
return -1; // m_discard_ticks > world ticks
if (m_bomb_remaining != -1)
return false;
if (!m_discard_now) if (!m_discard_now)
{ {
@ -185,13 +248,13 @@ int Swatter::updateAndTestFinished(int ticks)
{ {
// Avoid swatter near the start and the end lifetime of swatter // Avoid swatter near the start and the end lifetime of swatter
// to make sure all clients know the existence of swatter each other // to make sure all clients know the existence of swatter each other
if (ticks_start - m_swatter_start_ticks < 60 || if (m_swatter_duration - m_attachment->getTicksLeft() < 60 ||
m_swatter_end_ticks - ticks_start < 60) m_attachment->getTicksLeft() < 90) // ~20 and ~60 below
return -1; return false;
chooseTarget(); chooseTarget();
pointToTarget(); if (!m_closest_kart)
if(!m_target || !m_closest_kart) break; break;
// Get the node corresponding to the joint at the center of the // Get the node corresponding to the joint at the center of the
// swatter (by swatter, I mean the thing hold in the hand, not // swatter (by swatter, I mean the thing hold in the hand, not
@ -212,61 +275,45 @@ int Swatter::updateAndTestFinished(int ticks)
{ {
// Start squashing // Start squashing
m_animation_phase = SWATTER_TO_TARGET; m_animation_phase = SWATTER_TO_TARGET;
m_start_swat_ticks = ticks_start + 20; m_swatter_animation_ticks =
// Setup the animation m_attachment->getTicksLeft() - 20;
m_scene_node->setCurrentFrame(0.0f);
m_scene_node->setLoopMode(false);
m_scene_node->setAnimationSpeed(SWATTER_ANIMATION_SPEED);
#ifndef SERVER_ONLY
// Play swat sound
m_swat_sound->setPosition(swatter_pos);
m_swat_sound->play();
#endif
} }
} }
break; break;
case SWATTER_TO_TARGET: case SWATTER_TO_TARGET:
{ {
pointToTarget();
// Did we just finish the first part of the movement? // Did we just finish the first part of the movement?
if (ticks_start > m_start_swat_ticks) if (m_attachment->getTicksLeft() < m_swatter_animation_ticks &&
m_attachment->getTicksLeft() > 60)
{ {
m_start_swat_ticks = std::numeric_limits<int>::max();
// Squash the karts and items around and // Squash the karts and items around and
// change the current phase // change the current phase
squashThingsAround(); squashThingsAround();
m_animation_phase = SWATTER_FROM_TARGET; m_animation_phase = SWATTER_FROM_TARGET;
const int end_ticks = ticks_start + 60; const int end_ticks = ticks_start + 60;
if (race_manager->isBattleMode() || if (race_manager->isBattleMode() ||
race_manager race_manager->isSoccerMode())
->getMinorMode()==RaceManager::MINOR_MODE_SOCCER)
{ {
// Remove swatter from kart in arena gameplay // Remove swatter from kart in arena gameplay
// after one successful hit // after one successful hit
m_discard_now = true; m_discard_now = true;
m_discard_ticks = end_ticks;
} }
m_end_swat_ticks = end_ticks; m_swatter_animation_ticks =
m_attachment->getTicksLeft() - 60;
} }
} }
break; break;
case SWATTER_FROM_TARGET: case SWATTER_FROM_TARGET:
{
if (m_attachment->getTicksLeft() < m_swatter_animation_ticks &&
m_attachment->getTicksLeft() > 0)
m_animation_phase = SWATTER_AIMING;
break; break;
}
} }
} }
return false;
if (m_discard_now)
{
return m_end_swat_ticks;
}
else if (ticks_start > m_end_swat_ticks)
{
m_animation_phase = SWATTER_AIMING;
m_end_swat_ticks = std::numeric_limits<int>::max();
return -1;
}
return -1;
} // updateAndTestFinished } // updateAndTestFinished
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -303,8 +350,11 @@ void Swatter::chooseTarget()
closest_kart = kart; closest_kart = kart;
} }
} }
m_target = closest_kart; // may be NULL // Not larger than 2^5 - 1 for kart id for optimizing state saving
m_closest_kart = closest_kart; if (closest_kart->getWorldKartId() < 31)
m_closest_kart = closest_kart;
else
m_closest_kart = NULL;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -313,16 +363,17 @@ void Swatter::chooseTarget()
void Swatter::pointToTarget() void Swatter::pointToTarget()
{ {
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
if (m_kart->isGhostKart()) return; if (m_kart->isGhostKart() || !m_scene_node)
return;
if(!m_target) if (!m_closest_kart)
{ {
m_scene_node->setRotation(core::vector3df()); m_scene_node->setRotation(core::vector3df());
} }
else else
{ {
Vec3 swatter_to_target = Vec3 swatter_to_target =
m_kart->getTrans().inverse()(m_target->getXYZ()); m_kart->getTrans().inverse()(m_closest_kart->getXYZ());
float dy = -swatter_to_target.getZ(); float dy = -swatter_to_target.getZ();
float dx = swatter_to_target.getX(); float dx = swatter_to_target.getX();
float angle = SWAT_ANGLE_OFFSET + atan2f(dy, dx) * 180 / M_PI; float angle = SWAT_ANGLE_OFFSET + atan2f(dy, dx) * 180 / M_PI;
@ -341,28 +392,13 @@ void Swatter::squashThingsAround()
const KartProperties *kp = m_kart->getKartProperties(); const KartProperties *kp = m_kart->getKartProperties();
AbstractKart* closest_kart = m_closest_kart;
float duration = kp->getSwatterSquashDuration(); float duration = kp->getSwatterSquashDuration();
float slowdown = kp->getSwatterSquashSlowdown(); float slowdown = kp->getSwatterSquashSlowdown();
// The squash attempt may fail because of invulnerability, shield, etc. // The squash attempt may fail because of invulnerability, shield, etc.
// Making a bomb explode counts as a success // Making a bomb explode counts as a success
bool success = closest_kart->setSquash(duration, slowdown); bool success = m_closest_kart->setSquash(duration, slowdown);
const bool has_created_explosion_animation = const bool has_created_explosion_animation =
success && closest_kart->getKartAnimation() != NULL; success && m_closest_kart->getKartAnimation() != NULL;
// Locally add a event to replay the squash during rewind
if (NetworkConfig::get()->isNetworking() &&
NetworkConfig::get()->isClient() &&
closest_kart->getKartAnimation() == NULL)
{
RewindManager::get()->addRewindInfoEventFunction(new
RewindInfoEventFunction(World::getWorld()->getTicksSinceStart(),
/*undo_function*/[](){},
/*replay_function*/[closest_kart, duration, slowdown]()
{
closest_kart->setSquash(duration, slowdown);
}));
}
if (success) if (success)
{ {
@ -390,7 +426,7 @@ void Swatter::squashThingsAround()
} }
} }
if (has_created_explosion_animation) if (has_created_explosion_animation && !RewindManager::get()->isRewinding())
{ {
HitEffect *he = new Explosion(m_kart->getXYZ(), "explosion", "explosion.xml"); HitEffect *he = new Explosion(m_kart->getXYZ(), "explosion", "explosion.xml");
if(m_kart->getController()->isLocalPlayerController()) if(m_kart->getController()->isLocalPlayerController())
@ -400,3 +436,52 @@ void Swatter::squashThingsAround()
// TODO: squash items // TODO: squash items
} // squashThingsAround } // squashThingsAround
// ----------------------------------------------------------------------------
void Swatter::restoreState(BareNetworkString* buffer)
{
int16_t prev_bomb_remaing = m_bomb_remaining;
m_bomb_remaining = buffer->getUInt16();
if (prev_bomb_remaing != m_bomb_remaining)
{
// Wrong state, clear mesh and let updateGraphics reset itself
m_scene_node = NULL;
if (m_bomb_scene_node)
{
irr_driver->removeNode(m_bomb_scene_node);
m_bomb_scene_node = NULL;
}
}
if (m_bomb_remaining == -1)
{
uint8_t combined = buffer->getUInt8();
int kart_id = combined & 31;
if (kart_id == 31)
m_closest_kart = NULL;
else
m_closest_kart = World::getWorld()->getKart(kart_id);
m_animation_phase = AnimationPhase((combined >> 5) & 3);
m_discard_now = (combined >> 7) == 1;
m_discard_ticks = buffer->getUInt32();
m_swatter_animation_ticks = buffer->getUInt16();
}
else
m_discard_ticks = buffer->getUInt32();
} // restoreState
// ----------------------------------------------------------------------------
void Swatter::saveState(BareNetworkString* buffer) const
{
buffer->addUInt16(m_bomb_remaining);
if (m_bomb_remaining == -1)
{
uint8_t combined =
m_closest_kart ? (uint8_t)m_closest_kart->getWorldKartId() : 31;
combined |= m_animation_phase << 5;
combined |= (m_discard_now ? (1 << 7) : 0);
buffer->addUInt8(combined).addUInt32(m_discard_ticks)
.addUInt16(m_swatter_animation_ticks);
}
else
buffer->addUInt32(m_discard_ticks);
} // saveState

View File

@ -31,6 +31,7 @@
using namespace irr; using namespace irr;
class Attachment;
class AbstractKart; class AbstractKart;
class Item; class Item;
class Moveable; class Moveable;
@ -48,46 +49,49 @@ private:
* - going down to the target * - going down to the target
* - going up from the target * - going up from the target
*/ */
enum {SWATTER_AIMING, SWATTER_TO_TARGET, SWATTER_FROM_TARGET} enum AnimationPhase : uint8_t
m_animation_phase; {
SWATTER_AIMING = 0,
/** True if the swatter will be discarded now. */ SWATTER_TO_TARGET = 1,
bool m_discard_now; SWATTER_FROM_TARGET = 2
};
AnimationPhase m_animation_phase;
/** The kart the swatter is aiming at. */ /** The kart the swatter is aiming at. */
Moveable *m_target;
AbstractKart *m_closest_kart; AbstractKart *m_closest_kart;
SFXBase *m_swat_sound; SFXBase *m_swat_sound;
/** Set the end ticks to complete the removing an attached bomb animation. */ /** Set the end ticks to complete the removing an attached bomb animation. */
int m_removed_bomb_ticks;
/** The scene node of the attachment. */ /** The scene node of the attachment. */
scene::IAnimatedMeshSceneNode *m_scene_node; scene::IAnimatedMeshSceneNode *m_scene_node;
/** The scene node where a bomb is saved (in case that the swatter /** The scene node where a bomb is saved (in case that the swatter
* replaces a bomb. */ * replaces a bomb. */
scene::ISceneNode *m_bomb_scene_node; scene::IAnimatedMeshSceneNode *m_bomb_scene_node;
/** For some reason the built-in animation system doesn't work correctly here?? */ int m_discard_ticks;
float m_swat_bomb_frame;
int m_start_swat_ticks; int m_swatter_duration;
int m_end_swat_ticks; /** Set the bomb remaing ticks so we can set the timer on the removing
* bomb animation. */
int16_t m_bomb_remaining;
const int m_swatter_start_ticks; int16_t m_swatter_animation_ticks;
const int m_swatter_end_ticks; /** True if the swatter will be discarded now. */
bool m_discard_now;
/** True if the swatter animation has been played. */
bool m_played_swatter_animation;
public: public:
Swatter(AbstractKart *kart, bool was_bomb, Swatter(AbstractKart *kart, int16_t bomb_ticks, int ticks,
scene::ISceneNode* bomb_scene_node, int ticks); Attachment* attachment);
virtual ~Swatter(); virtual ~Swatter();
void updateGrahpics(float dt) OVERRIDE; void updateGrahpics(float dt) OVERRIDE;
int updateAndTestFinished(int ticks) OVERRIDE; bool updateAndTestFinished(int ticks) OVERRIDE;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns if the swatter is currently aiming, i.e. can be used to /** Returns if the swatter is currently aiming, i.e. can be used to
@ -97,10 +101,9 @@ public:
return m_animation_phase == SWATTER_AIMING; return m_animation_phase == SWATTER_AIMING;
} // isSwatterReady } // isSwatterReady
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
bool isRemovingBomb() const virtual void restoreState(BareNetworkString *buffer) OVERRIDE;
{ // ------------------------------------------------------------------------
return m_removed_bomb_ticks != std::numeric_limits<int>::max(); virtual void saveState(BareNetworkString *buffer) const OVERRIDE;
}
private: private:
/** Determine the nearest kart or item and update the current target accordingly */ /** Determine the nearest kart or item and update the current target accordingly */

View File

@ -169,7 +169,6 @@ void GhostKart::update(int ticks)
else if (attach_type != m_attachment->getType()) else if (attach_type != m_attachment->getType())
{ {
m_attachment->set(attach_type, attach_ticks, NULL, m_attachment->set(attach_type, attach_ticks, NULL,
/*disable_swatter_animation*/false,
/*set_by_rewind_parachute*/true); /*set_by_rewind_parachute*/true);
} }

View File

@ -354,7 +354,7 @@ void Kart::reset()
m_saved_controller = NULL; m_saved_controller = NULL;
} }
m_kart_model->setAnimation(KartModel::AF_DEFAULT); m_kart_model->setAnimation(KartModel::AF_DEFAULT);
m_attachment->clear(); m_attachment->reset();
m_kart_gfx->reset(); m_kart_gfx->reset();
m_skidding->reset(); m_skidding->reset();

View File

@ -37,6 +37,7 @@
#include "physics/btKart.hpp" #include "physics/btKart.hpp"
#include "utils/vec3.hpp" #include "utils/vec3.hpp"
#include <ISceneNode.h>
#include <string.h> #include <string.h>
KartRewinder::KartRewinder(const std::string& ident, KartRewinder::KartRewinder(const std::string& ident,