Added first test version of 'rubber ball' (which might replace

the anchor). Known bugs: atm it does not yet bounce (it follows
just the drivelines and might be partly in the track); the first
kart can't avoid it (long term breaking might allow you to avoid it);
it might hit another kart first and explode.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@9302 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2011-07-20 00:39:32 +00:00
parent cc20387a95
commit afb3728995
17 changed files with 397 additions and 27 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
data/models/rubber_ball.b3d Normal file

Binary file not shown.

BIN
data/models/rubber_ball.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -15,6 +15,9 @@
model="anchor.b3d" />
<item name="switch" icon="swap-icon.png" />
<item name="swatter" icon="swatter-icon.png" />
<item name="rubber-ball" icon="rubber_ball-icon.png"
model="rubber_ball.b3d" speed="10.0"
min-height="0.2" max-height="1.0" />
<item name="parachute" icon="parachute-icon.png"
model="parachute.b3d" />
<item name="plunger" icon="plunger-icon.png"
@ -37,19 +40,19 @@
be quite rare, since otherwise the item might be used
too often (compared with many items which will only
affect a karts or two). -->
<!-- bubble cake bowl zipper plunger switch glove para anvil -->
<first w="25 5 15 5 10 10 10 0 0"
w-multi=" 0 0 5 0 0 0 5 0 0" />
<top33 w="30 30 30 30 30 10 30 10 0"
w-multi=" 0 10 10 0 10 0 10 0 0" />
<mid33 w="30 30 30 30 30 10 30 20 5"
w-multi=" 0 20 20 20 20 0 0 5 0" />
<end33 w=" 0 30 30 30 30 10 30 30 30"
w-multi=" 0 30 30 30 30 0 30 20 0" />
<last w=" 0 30 30 60 60 0 0 60 60"
w-multi=" 0 30 30 60 60 0 0 0 0" />
<battle w=" 0 30 60 0 0 10 40 0 0"
w-multi=" 0 0 5 0 0 0 0 0 0" />
<!-- bubble cake bowl zipper plunger switch glove rubber para anvil -->
<first w="25 5 15 5 10 10 10 0 0 0"
w-multi=" 0 0 5 0 0 0 5 0 0 0" />
<top33 w="30 30 30 30 30 10 30 10 10 0"
w-multi=" 0 10 10 0 10 0 10 0 0 0" />
<mid33 w="30 30 30 30 30 10 30 20 20 5"
w-multi=" 0 20 20 20 20 0 0 5 5 0" />
<end33 w=" 0 30 30 30 30 10 30 30 30 30"
w-multi=" 0 30 30 30 30 0 30 20 20 0" />
<last w=" 0 30 30 60 60 0 0 60 60 60"
w-multi=" 0 30 30 60 60 0 0 0 0 0" />
<battle w=" 0 30 60 0 0 10 40 0 0 0"
w-multi=" 0 0 5 0 0 0 0 0 0 0" />
</powerup>

View File

@ -185,6 +185,8 @@ supertuxkart_SOURCES = \
items/powerup_manager.hpp \
items/projectile_manager.cpp \
items/projectile_manager.hpp \
items/rubber_ball.cpp \
items/rubber_ball.hpp \
items/rubber_band.cpp \
items/rubber_band.hpp \
items/swatter.cpp \

View File

@ -574,6 +574,10 @@
RelativePath="..\..\items\projectile_manager.cpp"
>
</File>
<File
RelativePath="..\..\items\rubber_ball.cpp"
>
</File>
<File
RelativePath="..\..\items\rubber_band.cpp"
>

View File

@ -196,7 +196,11 @@ void InputManager::handleStaticAction(int key, int value)
if (UserConfigParams::m_artist_debug_mode && world)
{
Kart* kart = world->getLocalPlayerKart(0);
kart->setPowerup(PowerupManager::POWERUP_SWATTER, 10000);
if(control_is_pressed)
kart->setPowerup(PowerupManager::POWERUP_RUBBERBALL,
10000);
else
kart->setPowerup(PowerupManager::POWERUP_SWATTER, 10000);
}
break;

View File

@ -202,7 +202,8 @@ void Flyable::createPhysics(float forw_offset, const Vec3 &velocity,
m_body->setLinearVelocity(v);
if(!rotates) m_body->setAngularFactor(0.0f); // prevent rotations
}
m_body->setCollisionFlags(btCollisionObject::CF_NO_CONTACT_RESPONSE);
m_body->setCollisionFlags(m_body->getCollisionFlags() |
btCollisionObject::CF_NO_CONTACT_RESPONSE);
} // createPhysics
// -----------------------------------------------------------------------------
@ -466,6 +467,8 @@ void Flyable::hit(Kart *kart_hit, PhysicalObject* object)
case PowerupManager::POWERUP_PLUNGER:
// Handled by plunger.cpp Plunger::hit
break;
case PowerupManager::POWERUP_RUBBERBALL:
break;
case PowerupManager::POWERUP_BOWLING:
{
if (kart_hit == m_owner)

View File

@ -228,6 +228,7 @@ void Powerup::use()
break;
}
case PowerupManager::POWERUP_CAKE:
case PowerupManager::POWERUP_RUBBERBALL:
case PowerupManager::POWERUP_BOWLING:
case PowerupManager::POWERUP_PLUNGER:

View File

@ -28,6 +28,7 @@
#include "items/bowling.hpp"
#include "items/cake.hpp"
#include "items/plunger.hpp"
#include "items/rubber_ball.hpp"
#include "modes/world.hpp"
#include "utils/constants.hpp"
#include "utils/string_utils.hpp"
@ -93,7 +94,7 @@ PowerupManager::PowerupType
static std::string powerup_names[] = {
"", /* Nothing */
"bubblegum", "cake", "bowling", "zipper", "plunger", "switch",
"swatter", "parachute", "anchor"
"swatter", "rubber-ball", "parachute", "anchor"
};
for(unsigned int i=POWERUP_FIRST; i<=POWERUP_LAST; i++)
@ -188,11 +189,13 @@ void PowerupManager::LoadPowerup(PowerupType type, const XMLNode &node)
// Load special attributes for certain powerups
switch (type) {
case POWERUP_BOWLING:
Bowling::init(node, m_all_meshes[type]); break;
Bowling::init(node, m_all_meshes[type]); break;
case POWERUP_PLUNGER:
Plunger::init(node, m_all_meshes[type]); break;
Plunger::init(node, m_all_meshes[type]); break;
case POWERUP_CAKE:
Cake::init(node, m_all_meshes[type]); break;
Cake::init(node, m_all_meshes[type]); break;
case POWERUP_RUBBERBALL:
RubberBall::init(node, m_all_meshes[type]); break;
default:;
} // switch
} // LoadNode

View File

@ -79,7 +79,7 @@ public:
POWERUP_BUBBLEGUM = POWERUP_FIRST,
POWERUP_CAKE,
POWERUP_BOWLING, POWERUP_ZIPPER, POWERUP_PLUNGER,
POWERUP_SWITCH, POWERUP_SWATTER,
POWERUP_SWITCH, POWERUP_SWATTER, POWERUP_RUBBERBALL,
POWERUP_PARACHUTE,
POWERUP_ANVIL, //powerup.cpp assumes these two come last
POWERUP_LAST=POWERUP_ANVIL,

View File

@ -25,6 +25,7 @@
#include "items/plunger.hpp"
#include "items/powerup_manager.hpp"
#include "items/powerup.hpp"
#include "items/rubber_ball.hpp"
#include "network/network_manager.hpp"
#include "network/race_state.hpp"
@ -174,9 +175,10 @@ Flyable *ProjectileManager::newProjectile(Kart *kart,
Flyable *f;
switch(type)
{
case PowerupManager::POWERUP_BOWLING: f = new Bowling(kart); break;
case PowerupManager::POWERUP_PLUNGER: f = new Plunger(kart); break;
case PowerupManager::POWERUP_CAKE: f = new Cake(kart); break;
case PowerupManager::POWERUP_BOWLING: f = new Bowling(kart); break;
case PowerupManager::POWERUP_PLUNGER: f = new Plunger(kart); break;
case PowerupManager::POWERUP_CAKE: f = new Cake(kart); break;
case PowerupManager::POWERUP_RUBBERBALL: f = new RubberBall(kart);break;
default: return NULL;
}
m_active_projectiles.push_back(f);

264
src/items/rubber_ball.cpp Normal file
View File

@ -0,0 +1,264 @@
// $Id$
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2011 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 "items/rubber_ball.hpp"
#include "karts/kart.hpp"
#include "modes/linear_world.hpp"
#include "tracks/track.hpp"
RubberBall::RubberBall(Kart *kart) : Flyable(kart, PowerupManager::POWERUP_RUBBERBALL,
0.0f /* mass */)
{
float forw_offset = 0.5f*kart->getKartLength() + m_extend.getZ()*0.5f+5.0f;
float min_speed = m_speed*4.0f;
m_speed = kart->getSpeed() + m_speed;
if(m_speed < min_speed) m_speed = min_speed;
createPhysics(forw_offset, btVector3(0.0f, 0.0f, m_speed*2),
new btSphereShape(0.5f*m_extend.getY()),
-70.0f /*gravity*/,
true /*rotates*/);
// Do not adjust the up velocity
setAdjustUpVelocity(false);
// should not live forever, auto-destruct after 30 seconds
m_max_lifespan = 30;
m_target = NULL;
computeTarget();
const LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld());
m_quad_graph = &(world->getTrack()->getQuadGraph());
m_current_graph_node = world->getSectorForKart(kart->getWorldKartId());
// Determine distance along track
Vec3 ball_distance_vec;
m_quad_graph->spatialToTrack(&ball_distance_vec, getXYZ(),
m_current_graph_node);
m_distance_along_track = ball_distance_vec[2];
std::vector<unsigned int> succ;
m_quad_graph->getSuccessors(m_current_graph_node, succ, /* ai */ true);
// We have to start aiming at the next sector (since it might be possible
// that the kart is ahead of the center of the current sector).
m_current_graph_node = succ[0];
// At the start the ball aims at quads till it gets close enough to the
// target:
m_aiming_at_target = false;
m_wrapped_around = false;
} // RubberBall
// -----------------------------------------------------------------------------
/** Determines the first kart. If a target has already been identified in an
* earlier call, it is tested first, avoiding a loop over all karts.
*/
void RubberBall::computeTarget()
{
LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld());
// FIXME: what does the rubber ball do in case of battle mode??
if(!world) return;
for(unsigned int p = race_manager->getFinishedKarts()+1;
p < world->getNumKarts()+1; p++)
{
m_target = world->getKartAtPosition(p);
if(!m_target->isEliminated() && !m_target->hasFinishedRace())
{
// Don't aim at yourself
if(m_target == m_owner) m_target = NULL;
return;
}
}
} // computeTarget
// -----------------------------------------------------------------------------
/** Initialises this object with data from the power.xml file (this is a static
* function).
* \param node XML Node
* \param bowling The bowling ball mesh
*/
void RubberBall::init(const XMLNode &node, scene::IMesh *bowling)
{
Flyable::init(node, bowling, PowerupManager::POWERUP_RUBBERBALL);
} // init
// -----------------------------------------------------------------------------
/** Updates the rubber ball.
* \param dt Time step size.
*/
void RubberBall::update(float dt)
{
Flyable::update(dt);
// Update the target in case that the first kart was overtaken (or has
// finished the race).
computeTarget();
if(!m_target)
{
// Remove this item from the game
hit(NULL);
return;
}
Vec3 aim_xyz;
determineTargetCoordinates(dt, &aim_xyz);
Vec3 delta = aim_xyz - getXYZ();
// Determine the next point for the ball.
// FIXME: use interpolation here for smooth curves
Vec3 next_xyz = getXYZ() + delta * (m_speed * dt / delta.length());
// Determine new distance along track
Vec3 ball_distance_vec;
m_quad_graph->spatialToTrack(&ball_distance_vec, getXYZ(),
m_current_graph_node);
float old_distance = m_distance_along_track;
m_distance_along_track = ball_distance_vec[2];
float track_length = World::getWorld()->getTrack()->getTrackLength();
// Detect if the ball crossed the start line
m_wrapped_around = old_distance > 0.9f * track_length &&
m_distance_along_track < 10.0f;
// FIXME: do we want to test if we have overtaken the target kart?
#if 0
float height = 3*fabsf(sinf(10.0f*logf(target_distance
- m_distance_along_track)));
next_xyz.setY(next_xyz.getY()*0.9f+(getHoT() + height)*0.1f);
#endif
setXYZ(next_xyz);
} // update
// -----------------------------------------------------------------------------
/** Determines which coordinates the ball should aim at next. If the ball is
* still 'far' away from the target (>20), then it will aim at the next
* graph node. If it's closer, the ball will aim directly at the kart and
* keep on aiming at the kart, it will not follow the drivelines anymore.
* \param aim_xyz On return contains the xyz coordinates to aim at.
*/
void RubberBall::determineTargetCoordinates(float dt, Vec3 *aim_xyz)
{
// If aiming at target phase, keep on aiming at target.
// ----------------------------------------------------
if(m_aiming_at_target)
{
*aim_xyz = m_target->getXYZ();
return;
}
// Aiming at a graph node
// ----------------------
const Vec3 &ball_xyz = getXYZ();
GraphNode *gn = &(m_quad_graph->getNode(m_current_graph_node));
// At this stage m_distance_along track is already the new distance (set
// in the previous time step when aiming). It has to be detected if the
// ball is now ahead of the graph node, and if so, the graph node has to
// be updated till it is again ahead of the ball. Three distinct cases
// have to be considered:
// 1) The ball just crossed the start line (-> distance close to 0),
// but the graph node is still before the start line, in which case
// a new graph node has to be determined.
// 2) The ball hasn't crossed the start line, but the graph node has
// (i.e. graph node is 0), in which case the graph node is correct.
// This happens after the first iteration, i.e. graph node initially
// is the last one (distance close to track length), but if the ball
// is ahead (distance of a graph node is measured to the beginning of
// the quad, so if the ball is in the middle of the last quad it will
// be ahead of the graph node!) the graph node will be set to the
// first graph node (distance close to 0). In this case the graph node
// should not be changed anymore.
// 3) All other cases that do not involve the start line at all
// (including e.g. ball and graph node crossed start line, neither
// ball nor graph node crossed start line), which means that a new
// graph node need to be determined only if the distance along track
// of the ball is greater than the distance for
float gn_distance = gn->getDistanceFromStart();
float track_length = World::getWorld()->getTrack()->getTrackLength();
// Test 1: ball wrapped around, and graph node is close to end of track
bool ball_ahead = m_wrapped_around && gn_distance >0.9f*track_length;
// Test 3: distance of ball greater than distance of graph node
if(!ball_ahead && gn_distance < m_distance_along_track)
// The distance test only applies if the graph node hasn't wrapped
// around
ball_ahead = true;
while(ball_ahead)
{
// FIXME: aim better if necessary!
m_current_graph_node = gn->getSuccessor(0);
gn = &(m_quad_graph->getNode(m_current_graph_node));
// Detect a wrap around of the graph node. We could just test if
// the index of the new graph node is 0, but since it's possible
// that we might have tracks with a more complicated structure, e.g
// with two different start lines, we use this more general test:
// If the previous distance was close to the end of the track, and
// the new distance is close to 0, the graph node wrapped around.
// This test prevents an infinite loop if the ball is on the last
// quad, in which case no grpah node would fulfill the distance test.
if(gn_distance > 0.9f*track_length &&
gn->getDistanceFromStart()<10.0f)
break;
gn_distance = gn->getDistanceFromStart();
ball_ahead = m_wrapped_around && gn_distance >0.9f*track_length;
ball_ahead = !ball_ahead &&
gn->getDistanceFromStart() < m_distance_along_track;
}
const LinearWorld *world = dynamic_cast<LinearWorld*>(World::getWorld());
if(!world) return; // FIXME battle mode
float target_distance =
world->getDistanceDownTrackForKart(m_target->getWorldKartId());
// Handle wrap around of distance if target crosses finishing line
if(m_distance_along_track > target_distance)
target_distance += world->getTrack()->getTrackLength();
// If the ball is close enough, start aiming directly at the target kart
// ---------------------------------------------------------------------
if(target_distance-m_distance_along_track < 20)
{
m_aiming_at_target = true;
*aim_xyz = m_target->getXYZ();
return;
}
// ------------------------------
*aim_xyz = gn->getQuad().getCenter();
} // determineTargetCoordinates

68
src/items/rubber_ball.hpp Normal file
View File

@ -0,0 +1,68 @@
// $Id$
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2011 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_RUBBER_BALL_HPP
#define HEADER_RUBBER_BALL_HPP
#include "items/flyable.hpp"
class Kart;
class QuadGraph;
/**
* \ingroup items
*/
class RubberBall: public Flyable
{
private:
/** A pointer to the target kart. */
const Kart *m_target;
/** The current quad this ball is aiming at. */
int m_current_graph_node;
/** The previous distance to the graph node we are aiming
* at atm. If the distance increases, we have passed the
* point we aimed at and have to aim at the next point. */
float m_distance_along_track;
/** True if the ball just crossed the start line, i.e. its
* distance changed from close to length of track in the
* previous time step to a bit over zero now. */
bool m_wrapped_around;
/** Once the ball is close enough, it will aim for the kart. If the
* kart should be able to then increase the distance to the ball,
* the ball will be removed and the kart escapes. This boolean is
* used to keep track of the state of this ball. */
bool m_aiming_at_target;
/** For convenience keep a pointer to the quad graph. */
QuadGraph *m_quad_graph;
void computeTarget();
void determineTargetCoordinates(float dt, Vec3 *aim_xyz);
public:
RubberBall (Kart* kart);
static void init(const XMLNode &node, scene::IMesh *bowling);
virtual void update (float dt);
}; // RubberBall
#endif

View File

@ -32,7 +32,7 @@
#include "modes/world.hpp"
#include "karts/kart.hpp"
#define SWAT_POS_OFFSET core::vector3df(0.0, 0.2, -0.4)
#define SWAT_POS_OFFSET core::vector3df(0.0, 0.2f, -0.4f)
#define SWAT_ANGLE_MIN 45
#define SWAT_ANGLE_MAX 135
#define SWAT_ANGLE_OFFSET (90.0f + 15.0f)

View File

@ -577,6 +577,12 @@ void DefaultAIController::handleItems(const float dt)
m_controls->m_fire = true;
break;
}
case PowerupManager::POWERUP_RUBBERBALL:
// 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;
break;
default:
printf("Invalid or unhandled powerup '%d' in default AI.\n",
m_kart->getPowerup()->getType());
@ -610,7 +616,7 @@ void DefaultAIController::computeNearestKarts()
for(unsigned int i=0; i<m_world->getNumKarts(); i++)
{
Kart *k = m_world->getKart(i);
if(k->isEliminated() || k==m_kart) continue;
if(k->isEliminated() || k->hasFinishedRace() || k==m_kart) continue;
if(k->getPosition()==my_position+1)
{
m_kart_behind = k;

View File

@ -117,7 +117,8 @@ void Moveable::stopFlying()
*/
void Moveable::update(float dt)
{
m_motion_state->getWorldTransform(m_transform);
if(m_body->getInvMass()!=0)
m_motion_state->getWorldTransform(m_transform);
m_velocityLC = getVelocity()*m_transform.getBasis();
Vec3 forw_vec = m_transform.getBasis().getColumn(0);
m_heading = -atan2f(forw_vec.getZ(), forw_vec.getX());
@ -145,12 +146,21 @@ void Moveable::createBody(float mass, btTransform& trans,
m_transform = trans;
m_motion_state = new KartMotionState(trans);
btRigidBody::btRigidBodyConstructionInfo info(mass, m_motion_state, shape, inertia);
btRigidBody::btRigidBodyConstructionInfo info(mass, m_motion_state,
shape, inertia);
info.m_restitution=0.5f;
// Then create a rigid body
// ------------------------
m_body = new btRigidBody(info);
if(mass==0)
{
// Create a kinematic object
m_body->setCollisionFlags(m_body->getCollisionFlags() |
btCollisionObject::CF_KINEMATIC_OBJECT );
m_body->setActivationState(DISABLE_DEACTIVATION);
}
// The value of user_pointer must be set from the actual class, otherwise this
// is only a pointer to moveable, not to (say) kart, and virtual
// functions are not called correctly. So only init the pointer to zero.