Files
stk-code_catmod/src/items/item.hpp
2022-01-25 13:07:04 +08:00

443 lines
18 KiB
C++

// SuperTuxKart - a fun racing game with go-kart
//
// Copyright (C) 2004-2015 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_ITEM_HPP
#define HEADER_ITEM_HPP
/** \defgroup items
* Defines the various collectibles and weapons of STK.
*/
#include "utils/cpp2011.hpp"
#include "utils/leak_check.hpp"
#include "utils/log.hpp"
#include "utils/no_copy.hpp"
#include "utils/vec3.hpp"
#include <line3d.h>
class BareNetworkString;
class AbstractKart;
class LODNode;
namespace irr
{
namespace scene { class IMesh; class ISceneNode; }
}
using namespace irr;
// ============================================================================
/** \ingroup items
* Contains the state information of an item, i.e. all non-visual information
* only, which also can change (e.g. position and AI information is constant
* and therefore not stored here). This class is used as a base class for
* item and for networking to save item states.
*/
class ItemState
{
LEAK_CHECK();
public:
/**
* The list of all items. Important for the switch item function:
* bubblegum must be the last item (since bubble gum can't be
* switched with any other item, since it's a different objecct).
*/
enum ItemType
{
ITEM_FIRST,
ITEM_BONUS_BOX = ITEM_FIRST,
ITEM_BANANA,
ITEM_NITRO_BIG,
ITEM_NITRO_SMALL,
ITEM_BUBBLEGUM,
ITEM_BUBBLEGUM_NOLOK,
/** For easter egg mode only. */
ITEM_EASTER_EGG,
ITEM_LAST = ITEM_EASTER_EGG,
ITEM_COUNT,
ITEM_NONE
};
private:
/** Item type. */
ItemType m_type;
/** If the item is switched, this contains the original type.
* It is ITEM_NONE if the item is not switched. */
ItemType m_original_type;
/** Time till a collected item reappears. When this value is <=0 this
* means that the item is availabe to be collected. When the value is
* > 0 it means that the item is not available. */
int m_ticks_till_return;
/** Index in item_manager field. This field can also take on a negative
* value when used in the NetworkItemManager. */
int m_item_id;
/** Optionally if item was placed by a kart, a timer can be used to
* temporarly deactivate collision so a kart is not hit by its own item */
int m_deactive_ticks;
/** Counts how often an item is used before it disappears. Used for
* bubble gum to make them disappear after a while. A value >0
* indicates that the item still exists, =0 that the item can be
* deleted, and <0 that the item will never be deleted, i.e. it
* will always reappear after a while. */
int m_used_up_counter;
/** The position of this ItemState. */
Vec3 m_xyz;
/** The original rotation of the item. While this is technically a visual
* only value (atm, it could be used for collision detection), it is
* required to make sure a client can display items with the right normal
* (in case that a client would get a different (or no) normal from a
* raycast).
*/
btQuaternion m_original_rotation;
/** The 'owner' of the item, i.e. the kart that dropped this item.
* Is NULL if the item is part of the track. */
const AbstractKart *m_previous_owner;
protected:
friend class ItemManager;
friend class NetworkItemManager;
// ------------------------------------------------------------------------
virtual void setType(ItemType type) { m_type = type; }
// ------------------------------------------------------------------------
// Some convenient functions for the AI only
friend class SkiddingAI;
friend class TestAI;
/** Returns true if the specified line segment would come close enough
* to this item so that this item would be collected.
* \param line The line segment which is tested if it is close enough
* to this item so that this item would be collected.
*/
bool hitLine(const core::line3df &line,
const AbstractKart *kart = NULL) const
{
if (getPreviousOwner() == kart && getDeactivatedTicks() > 0)
return false;
Vec3 closest = line.getClosestPoint(getXYZ().toIrrVector());
return hitKart(closest, kart);
} // hitLine
public:
// ------------------------------------------------------------------------
ItemState(ItemType type, const AbstractKart *owner=NULL, int id = -1);
// ------------------------------------------------------------------------
ItemState(const BareNetworkString& buffer);
// ------------------------------------------------------------------------
void initItem(ItemType type, const Vec3& xyz, const Vec3& normal);
void update(int ticks);
void setDisappearCounter();
virtual void collected(const AbstractKart *kart);
// ------------------------------------------------------------------------
virtual ~ItemState() {}
// -----------------------------------------------------------------------
/** Dummy implementation, causing an abort if it should be called to
* catch any errors early. */
virtual void updateGraphics(float dt)
{
Log::fatal("ItemState", "updateGraphics() called for ItemState.");
} // updateGraphics
// -----------------------------------------------------------------------
virtual bool hitKart(const Vec3 &xyz,
const AbstractKart *kart = NULL) const
{
Log::fatal("ItemState", "hitKart() called for ItemState.");
return false;
} // hitKart
// -----------------------------------------------------------------------
virtual int getGraphNode() const
{
Log::fatal("ItemState", "getGraphNode() called for ItemState.");
return 0;
} // getGraphNode
// -----------------------------------------------------------------------
virtual const Vec3 *getAvoidancePoint(bool left) const
{
Log::fatal("ItemState", "getAvoidancePoint() called for ItemState.");
// Return doesn't matter, fatal aborts
return &m_xyz;
} // getAvoidancePoint
// -----------------------------------------------------------------------
virtual float getDistanceFromCenter() const
{
Log::fatal("itemState",
"getDistanceFromCentre() called for ItemState.");
return 0;
} // getDistanceFromCentre
// -----------------------------------------------------------------------
/** Resets an item to its start state. */
virtual void reset()
{
m_deactive_ticks = 0;
m_ticks_till_return = 0;
setDisappearCounter();
// If the item was switched:
if (m_original_type != ITEM_NONE)
{
setType(m_original_type);
m_original_type = ITEM_NONE;
}
} // reset
// -----------------------------------------------------------------------
/** Switches an item to be of a different type. Used for the switch
* powerup.
* \param type New type for this item.
*/
virtual void switchTo(ItemType type)
{
// triggers and easter eggs should not be switched
if (m_type == ITEM_EASTER_EGG) return;
m_original_type = m_type;
setType(type);
return;
} // switchTo
// ------------------------------------------------------------------------
/** Returns true if this item was not actually switched (e.g. trigger etc)
*/
virtual bool switchBack()
{
// If the item is not switched, do nothing. This can happen if a bubble
// gum is dropped while items are switched - when switching back, this
// bubble gum has no original type.
if (m_original_type == ITEM_NONE)
return true;
setType(m_original_type);
m_original_type = ITEM_NONE;
return false;
} // switchBack
// ------------------------------------------------------------------------
/** Returns if this item is negative, i.e. a banana or bubblegum. */
bool isNegativeItem() const
{
return m_type == ITEM_BANANA || m_type == ITEM_BUBBLEGUM ||
m_type == ITEM_BUBBLEGUM_NOLOK;
}
// ------------------------------------------------------------------------
/** Sets how long an item should be disabled. While item itself sets
* a default, this time is too short in case that a kart that has a bomb
* hits a banana: by the time the explosion animation is ended and the
* kart is back at its original position, the banana would be back again
* and therefore hit the kart again. See Attachment::hitBanana for more
* details.
* \param f Time till the item can be used again.
*/
void setTicksTillReturn(int t) { m_ticks_till_return = t; }
// ------------------------------------------------------------------------
/** Returns the time the item is disabled for. */
int getTicksTillReturn() const { return m_ticks_till_return; }
// ------------------------------------------------------------------------
/** Returns true if this item is currently collected. */
bool isAvailable() const { return m_ticks_till_return <= 0; }
// ------------------------------------------------------------------------
/** Returns the type of this item. */
ItemType getType() const { return m_type; }
// ------------------------------------------------------------------------
ItemType getGrahpicalType() const;
// ------------------------------------------------------------------------
/** Returns the original type of this item. */
ItemType getOriginalType() const { return m_original_type; }
// ------------------------------------------------------------------------
/** Sets the index of this item in the item manager list. */
void setItemId(unsigned int n) { m_item_id = n; }
// ------------------------------------------------------------------------
/** Returns the index of this item in the item manager list. */
unsigned int getItemId() const { return m_item_id; }
// ------------------------------------------------------------------------
/** Returns true if this item is used up and can be removed. */
bool isUsedUp() const { return m_used_up_counter == 0; }
// ------------------------------------------------------------------------
/** Returns true if this item can be used up, and therefore needs to
* be removed when the game is reset. */
bool canBeUsedUp() const { return m_used_up_counter>-1; }
// ------------------------------------------------------------------------
/** Returns the number of ticks during which the item is deactivated (i.e.
* it was collected). */
int getDeactivatedTicks() const { return m_deactive_ticks; }
// ------------------------------------------------------------------------
/** Sets the number of ticks during which the item is deactivated (i.e.
* it was collected). */
void setDeactivatedTicks(int ticks) { m_deactive_ticks = ticks; }
// ------------------------------------------------------------------------
/** Returns the kart that dropped this item (or NULL if the item was not
* dropped by a kart. */
const AbstractKart *getPreviousOwner() const { return m_previous_owner; }
// ------------------------------------------------------------------------
void setXYZ(const Vec3& xyz) { m_xyz = xyz; }
// ------------------------------------------------------------------------
/** Returns the XYZ position of the item. */
const Vec3& getXYZ() const { return m_xyz; }
// ------------------------------------------------------------------------
/** Returns the normal of the ItemState. */
const Vec3 getNormal() const
{
return quatRotate(m_original_rotation, Vec3(0.0f, 1.0f, 0.0f));
}
// ------------------------------------------------------------------------
/** Returns the original rotation of the item. */
const btQuaternion& getOriginalRotation() const
{
return m_original_rotation;
}
// ------------------------------------------------------------------------
void saveCompleteState(BareNetworkString* buffer) const;
}; // class ItemState
// ============================================================================
/**
* \ingroup items
*/
class Item : public ItemState, public NoCopy
{
private:
/** Scene node of this item. */
LODNode *m_node;
/** Graphical type of the mesh. */
ItemType m_graphical_type;
/** Vector containing the sparks */
std::vector<scene::ISceneNode*> m_spark_nodes;
/** Billboard that shows when the item is about to respawn */
scene::ISceneNode* m_icon_node;
/** Stores if the item was available in the previously rendered frame. */
bool m_was_available_previously;
/** square distance at which item is collected */
float m_distance_2;
/** The graph node this item is on. */
int m_graph_node;
/** Distance from the center of the quad this item is in. This value is
* >0 if it is to the right of the center, and undefined if this quad
* is not on any quad. */
float m_distance_from_center;
/** Time ticks since the item last respawned */
int m_animation_start_ticks;
/** The closest point to the left and right of this item at which it
* would not be collected. Used by the AI to avoid items. */
Vec3 *m_avoidance_points[2];
void initItem(ItemType type, const Vec3 &xyz, const Vec3 &normal);
void setMesh(scene::IMesh* mesh, scene::IMesh* lowres_mesh);
void handleNewMesh(ItemType type);
public:
Item(ItemType type, const Vec3& xyz, const Vec3& normal,
scene::IMesh* mesh, scene::IMesh* lowres_mesh,
const std::string& icon, const AbstractKart *owner);
virtual ~Item ();
virtual void updateGraphics(float dt) OVERRIDE;
virtual void reset() OVERRIDE;
//-------------------------------------------------------------------------
/** Is called when the item is hit by a kart. It sets the flag that the
* item has been collected, and the time to return to the parameter.
* \param kart The kart that collected the item.
*/
virtual void collected(const AbstractKart *kart) OVERRIDE
{
ItemState::collected(kart);
} // isCollected
//-------------------------------------------------------------------------
/** Switch backs to the original item. Returns true if the item was not
* actually switched (e.g. trigger, or bubblegum dropped during switch
* time). The return value is not actually used, but necessary in order
* to overwrite ItemState::switchBack()
*/
virtual bool switchBack() OVERRIDE
{
if (ItemState::switchBack())
return true;
return false;
} // switchBack
// ------------------------------------------------------------------------
/** Returns true if the Kart is close enough to hit this item, the item is
* not deactivated anymore, and it wasn't placed by this kart (this is
* e.g. used to avoid that a kart hits a bubble gum it just dropped).
* \param kart Kart to test.
* \param xyz Location of kart (avoiding to use kart->getXYZ() so that
* kart.hpp does not need to be included here).
*/
virtual bool hitKart(const Vec3 &xyz, const AbstractKart *kart=NULL) const
OVERRIDE
{
if (getPreviousOwner() == kart && getDeactivatedTicks() > 0)
return false;
Vec3 lc = quatRotate(getOriginalRotation(), xyz - getXYZ());
// Don't be too strict if the kart is a bit above the item
lc.setY(lc.getY() / 2.0f);
return lc.length2() < m_distance_2;
} // hitKart
// ------------------------------------------------------------------------
bool rotating() const { return getType() != ITEM_BUBBLEGUM; }
public:
// ------------------------------------------------------------------------
/** Returns the index of the graph node this item is on. */
virtual int getGraphNode() const OVERRIDE { return m_graph_node; }
// ------------------------------------------------------------------------
/** Returns the distance from center: negative means left of center,
* positive means right of center. */
virtual float getDistanceFromCenter() const OVERRIDE
{
return m_distance_from_center;
} // getDistanceFromCenter
// ------------------------------------------------------------------------
/** Returns a point to the left or right of the item which will not trigger
* a collection of this item.
* \param left If true, return a point to the left, else a point to
* the right. */
virtual const Vec3 *getAvoidancePoint(bool left) const OVERRIDE
{
if(left) return m_avoidance_points[0];
return m_avoidance_points[1];
} // getAvoidancePoint
// ------------------------------------------------------------------------
scene::ISceneNode *getSceneNode()
{
return (scene::ISceneNode *) m_node;
}
}; // class Item
#endif