Added a very first version of a swatter (or inflatable hammer).

Details are in the wiki (http://supertuxkart.sourceforge.net/Items).
Note that the model is _obviously_ a placeholder, and I  used
the icon for the bomb (which isn't used in game atm) for the
swatter. I hope someone can provide a better model and icon.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@9011 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2011-06-22 12:27:48 +00:00
parent 39ec2ffd74
commit da8ec214ce
29 changed files with 751 additions and 244 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
data/models/swatter.b3d Normal file

Binary file not shown.

View File

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

View File

@ -176,6 +176,20 @@
add-power="3" min-speed="10" add-power="3" min-speed="10"
max-speed-increase="5" duration="1" fade-out-time="2"/> max-speed-increase="5" duration="1" fade-out-time="2"/>
<!-- Kart-specific settings for the swatter:
duration: how long can the swatter be readied.
count: how often can it be used.
distance: How close a kart must be before it can be hit.
animation-time: How long it takes for the swatter to hit a target.
item-animation-time: How long the swatter will swat when a
projectile hits.
squash-duration: How long a kart will remain squashed.
squash-slowdown: percentage of max speed that a kart is
restricted to. -->
<swatter duration="10" count="2" distance="3" animation-time="0.2"
item-animation-time="0.04" squash-duration="5"
squash-slowdown="0.5"/>
<!-- min-speed-radius and max-speed-radius define the smallest turn <!-- min-speed-radius and max-speed-radius define the smallest turn
radius at lowest speed (4.64 m at speed 0) and at high speed radius at lowest speed (4.64 m at speed 0) and at high speed
(13.5 m at speed 12 m/s). Maximum steering angles for speeds (13.5 m at speed 12 m/s). Maximum steering angles for speeds

View File

@ -115,7 +115,8 @@ void InputManager::handleStaticAction(int key, int value)
break; break;
} }
case KEY_F1: case KEY_F1:
if (UserConfigParams::m_artist_debug_mode && world && race_manager->getNumPlayers() ==1 ) if (UserConfigParams::m_artist_debug_mode && world &&
race_manager->getNumPlayers() ==1 )
{ {
Kart* kart = world->getLocalPlayerKart(0); Kart* kart = world->getLocalPlayerKart(0);
kart->setPowerup(PowerupManager::POWERUP_BUBBLEGUM, 10000); kart->setPowerup(PowerupManager::POWERUP_BUBBLEGUM, 10000);
@ -127,42 +128,48 @@ void InputManager::handleStaticAction(int key, int value)
} }
break; break;
case KEY_F2: case KEY_F2:
if (UserConfigParams::m_artist_debug_mode && world && race_manager->getNumPlayers() ==1 ) if (UserConfigParams::m_artist_debug_mode && world &&
race_manager->getNumPlayers() ==1 )
{ {
Kart* kart = world->getLocalPlayerKart(0); Kart* kart = world->getLocalPlayerKart(0);
kart->setPowerup(PowerupManager::POWERUP_PLUNGER, 10000); kart->setPowerup(PowerupManager::POWERUP_PLUNGER, 10000);
} }
break; break;
case KEY_F3: case KEY_F3:
if (UserConfigParams::m_artist_debug_mode && world && race_manager->getNumPlayers() ==1 ) if (UserConfigParams::m_artist_debug_mode && world &&
race_manager->getNumPlayers() ==1 )
{ {
Kart* kart = world->getLocalPlayerKart(0); Kart* kart = world->getLocalPlayerKart(0);
kart->setPowerup(PowerupManager::POWERUP_CAKE, 10000); kart->setPowerup(PowerupManager::POWERUP_CAKE, 10000);
} }
break; break;
case KEY_F4: case KEY_F4:
if (UserConfigParams::m_artist_debug_mode && world && race_manager->getNumPlayers() ==1 ) if (UserConfigParams::m_artist_debug_mode && world &&
race_manager->getNumPlayers() ==1 )
{ {
Kart* kart = world->getLocalPlayerKart(0); Kart* kart = world->getLocalPlayerKart(0);
kart->setPowerup(PowerupManager::POWERUP_SWITCH, 10000); kart->setPowerup(PowerupManager::POWERUP_SWITCH, 10000);
} }
break; break;
case KEY_F5: case KEY_F5:
if (UserConfigParams::m_artist_debug_mode && world && race_manager->getNumPlayers() ==1 ) if (UserConfigParams::m_artist_debug_mode && world &&
race_manager->getNumPlayers() ==1 )
{ {
Kart* kart = world->getLocalPlayerKart(0); Kart* kart = world->getLocalPlayerKart(0);
kart->setPowerup(PowerupManager::POWERUP_BOWLING, 10000); kart->setPowerup(PowerupManager::POWERUP_BOWLING, 10000);
} }
break; break;
case KEY_F6: case KEY_F6:
if (UserConfigParams::m_artist_debug_mode && world && race_manager->getNumPlayers() == 1) if (UserConfigParams::m_artist_debug_mode && world &&
race_manager->getNumPlayers() == 1)
{ {
Kart* kart = world->getLocalPlayerKart(0); Kart* kart = world->getLocalPlayerKart(0);
kart->setPowerup(PowerupManager::POWERUP_PARACHUTE, 10000); kart->setPowerup(PowerupManager::POWERUP_PARACHUTE, 10000);
} }
break; break;
case KEY_F7: case KEY_F7:
if (UserConfigParams::m_artist_debug_mode && world && race_manager->getNumPlayers() == 1) if (UserConfigParams::m_artist_debug_mode && world &&
race_manager->getNumPlayers() == 1)
{ {
Kart* kart = world->getLocalPlayerKart(0); Kart* kart = world->getLocalPlayerKart(0);
kart->setPowerup(PowerupManager::POWERUP_ZIPPER, 10000); kart->setPowerup(PowerupManager::POWERUP_ZIPPER, 10000);
@ -180,7 +187,8 @@ void InputManager::handleStaticAction(int key, int value)
const int count = World::getWorld()->getNumKarts(); const int count = World::getWorld()->getNumKarts();
for (int n=0; n<count; n++) for (int n=0; n<count; n++)
{ {
World::getWorld()->getKart(n)->getNode()->setVisible(gui->m_enabled); World::getWorld()->getKart(n)->getNode()
->setVisible(gui->m_enabled);
} }
} }
else else
@ -191,8 +199,17 @@ void InputManager::handleStaticAction(int key, int value)
} }
break; break;
case KEY_F9:
if (UserConfigParams::m_artist_debug_mode && world &&
race_manager->getNumPlayers() == 1)
{
Kart* kart = world->getLocalPlayerKart(0);
kart->setPowerup(PowerupManager::POWERUP_SWATTER, 10000);
}
case KEY_F11: case KEY_F11:
if (UserConfigParams::m_artist_debug_mode && value && control_is_pressed) if (UserConfigParams::m_artist_debug_mode && value &&
control_is_pressed)
{ {
if (world != NULL) if (world != NULL)
{ {
@ -203,17 +220,9 @@ void InputManager::handleStaticAction(int key, int value)
case KEY_F12: case KEY_F12:
if(value) if(value)
UserConfigParams::m_display_fps = !UserConfigParams::m_display_fps; UserConfigParams::m_display_fps =
!UserConfigParams::m_display_fps;
break; break;
#ifndef WIN32
// For now disable F9 toggling fullscreen, since windows requires
// to reload all textures, display lists etc. Fullscreen can
// be toggled from the main menu (options->display).
case KEY_F9:
// TODO; toggle fullscreen
//irrDriver->toggleFullscreen(false); // 0: do not reset textures
// Fall through to put the game into pause mode.
#endif
case KEY_F10: case KEY_F10:
if(world) history->Save(); if(world) history->Save();
break; break;
@ -227,17 +236,20 @@ void InputManager::handleStaticAction(int key, int value)
/** /**
* Handles input when an input sensing mode (when configuring input) * Handles input when an input sensing mode (when configuring input)
*/ */
void InputManager::inputSensing(Input::InputType type, int deviceID, int button, void InputManager::inputSensing(Input::InputType type, int deviceID,
Input::AxisDirection axisDirection, int value) int button, Input::AxisDirection axisDirection,
int value)
{ {
#if INPUT_MODE_DEBUG #if INPUT_MODE_DEBUG
std::cout << "INPUT SENSING... "; std::cout << "INPUT SENSING... ";
#endif #endif
// don't store if we're trying to do something like bindings keyboard keys on a gamepad // don't store if we're trying to do something like bindings keyboard
// keys on a gamepad
if (m_mode == INPUT_SENSE_KEYBOARD && type != Input::IT_KEYBOARD) if (m_mode == INPUT_SENSE_KEYBOARD && type != Input::IT_KEYBOARD)
return; return;
if (m_mode == INPUT_SENSE_GAMEPAD && type != Input::IT_STICKMOTION && type != Input::IT_STICKBUTTON) if (m_mode == INPUT_SENSE_GAMEPAD && type != Input::IT_STICKMOTION &&
type != Input::IT_STICKBUTTON)
return; return;
#if INPUT_MODE_DEBUG #if INPUT_MODE_DEBUG
@ -282,7 +294,8 @@ void InputManager::inputSensing(Input::InputType type, int deviceID, int button,
case Input::IT_STICKMOTION: case Input::IT_STICKMOTION:
{ {
std::cout << "%% storing new axis binding, value=" << value << std::cout << "%% storing new axis binding, value=" << value <<
" deviceID=" << deviceID << " button=" << button << " axisDirection=" << " deviceID=" << deviceID << " button=" << button <<
" axisDirection=" <<
(axisDirection == Input::AD_NEGATIVE ? "-" : "+") << "\n"; (axisDirection == Input::AD_NEGATIVE ? "-" : "+") << "\n";
// We have to save the direction in which the axis was moved. // We have to save the direction in which the axis was moved.
// This is done by storing it as a sign (and since button can // This is done by storing it as a sign (and since button can
@ -343,8 +356,10 @@ void InputManager::inputSensing(Input::InputType type, int deviceID, int button,
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
int InputManager::getPlayerKeyboardID() const int InputManager::getPlayerKeyboardID() const
{ {
// In no-assign mode, just return the GUI player ID (devices not assigned yet) // In no-assign mode, just return the GUI player ID (devices not
if (m_device_manager->getAssignMode() == NO_ASSIGN) return PLAYER_ID_GAME_MASTER; // assigned yet)
if (m_device_manager->getAssignMode() == NO_ASSIGN)
return PLAYER_ID_GAME_MASTER;
// Otherwise, after devices are assigned, we can check in more depth // Otherwise, after devices are assigned, we can check in more depth
// Return the first keyboard that is actually being used // Return the first keyboard that is actually being used
@ -373,7 +388,8 @@ int InputManager::getPlayerKeyboardID() const
* Note: It is the obligation of the called menu to switch of the sense mode. * Note: It is the obligation of the called menu to switch of the sense mode.
* *
*/ */
void InputManager::dispatchInput(Input::InputType type, int deviceID, int button, void InputManager::dispatchInput(Input::InputType type, int deviceID,
int button,
Input::AxisDirection axisDirection, int value) Input::AxisDirection axisDirection, int value)
{ {
// Act different in input sensing mode. // Act different in input sensing mode.
@ -386,20 +402,27 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
StateManager::ActivePlayer* player = NULL; StateManager::ActivePlayer* player = NULL;
PlayerAction action; PlayerAction action;
bool action_found = m_device_manager->translateInput( type, deviceID, button, axisDirection, value, bool action_found = m_device_manager->translateInput(type, deviceID,
m_mode, &player, &action); button, axisDirection,
value, m_mode,
&player, &action);
// if didn't find a _menu_ action, try finding a corresponding game action as fallback // if didn't find a _menu_ action, try finding a corresponding game action
// (the GUI can handle them too) // as fallback (the GUI can handle them too)
if (!action_found && m_mode == MENU) if (!action_found && m_mode == MENU)
{ {
action_found = m_device_manager->translateInput(type, deviceID, button, axisDirection, value, action_found = m_device_manager->translateInput(type, deviceID,
INGAME, &player, &action); button, axisDirection,
value, INGAME, &player,
&action);
} }
// in menus, some keyboard keys are standard (before each player selected his device) // in menus, some keyboard keys are standard (before each player selected
// So if a key could not be mapped to any known binding, fall back to check the defaults. // his device). So if a key could not be mapped to any known binding,
if (!action_found && StateManager::get()->getGameState() != GUIEngine::GAME && type == Input::IT_KEYBOARD && // fall back to check the defaults.
if (!action_found &&
StateManager::get()->getGameState() != GUIEngine::GAME &&
type == Input::IT_KEYBOARD &&
m_mode == MENU && m_device_manager->getAssignMode() == NO_ASSIGN) m_mode == MENU && m_device_manager->getAssignMode() == NO_ASSIGN)
{ {
action = PA_BEFORE_FIRST; action = PA_BEFORE_FIRST;
@ -431,7 +454,8 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
if (m_device_manager->getAssignMode() == DETECT_NEW) if (m_device_manager->getAssignMode() == DETECT_NEW)
{ {
// Player is unjoining // Player is unjoining
if ((player != NULL) && (action == PA_RESCUE || action == PA_MENU_CANCEL)) if ((player != NULL) && (action == PA_RESCUE ||
action == PA_MENU_CANCEL ) )
{ {
// returns true if the event was handled // returns true if the event was handled
if (KartSelectionScreen::getInstance()->playerQuit( player )) if (KartSelectionScreen::getInstance()->playerQuit( player ))
@ -457,25 +481,30 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
InputDevice *device = NULL; InputDevice *device = NULL;
if (type == Input::IT_KEYBOARD) if (type == Input::IT_KEYBOARD)
{ {
//std::cout << "==== New Player Joining with Key " << button << " ====" << std::endl; //std::cout << "==== New Player Joining with Key " <<
// button << " ====" << std::endl;
device = m_device_manager->getKeyboardFromBtnID(button); device = m_device_manager->getKeyboardFromBtnID(button);
} }
else if (type == Input::IT_STICKBUTTON || type == Input::IT_STICKMOTION) else if (type == Input::IT_STICKBUTTON ||
type == Input::IT_STICKMOTION )
{ {
device = m_device_manager->getGamePadFromIrrID(deviceID); device = m_device_manager->getGamePadFromIrrID(deviceID);
} }
if (device != NULL) if (device != NULL)
{ {
KartSelectionScreen::getInstance()->playerJoin( device, false ); KartSelectionScreen::getInstance()->playerJoin(device,
false );
} }
} }
return; // we're done here, ignore devices that aren't associated with players return; // we're done here, ignore devices that aren't
// associated with players
} }
} }
// ... when in-game // ... when in-game
if (StateManager::get()->getGameState() == GUIEngine::GAME && !GUIEngine::ModalDialog::isADialogActive()) if (StateManager::get()->getGameState() == GUIEngine::GAME &&
!GUIEngine::ModalDialog::isADialogActive() )
{ {
// Find the corresponding PlayerKart from our ActivePlayer instance // Find the corresponding PlayerKart from our ActivePlayer instance
Kart* pk; Kart* pk;
@ -490,7 +519,8 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
if (pk == NULL) if (pk == NULL)
{ {
std::cerr << "Error, trying to process action for an unknown player\n"; std::cerr <<
"Error, trying to process action for an unknown player\n";
return; return;
} }
@ -501,28 +531,30 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
{ {
// reset timer when released // reset timer when released
if (abs(value) == 0 && (/*type == Input::IT_KEYBOARD ||*/ type == Input::IT_STICKBUTTON)) if (abs(value) == 0 && type == Input::IT_STICKBUTTON)
{ {
//if(type == Input::IT_STICKBUTTON) std::cout << "resetting because type == Input::IT_STICKBUTTON\n";
//else std::cout << "resetting for another reason\n";
m_timer_in_use = false; m_timer_in_use = false;
m_timer = 0; m_timer = 0;
} }
// When in master-only mode, we can safely assume that players are set up, contrarly to // When in master-only mode, we can safely assume that players
// early menus where we accept every input because players are not set-up yet // are set up, contrarly to early menus where we accept every
// input because players are not set-up yet
if (m_master_player_only && player == NULL) if (m_master_player_only && player == NULL)
{ {
if (type == Input::IT_STICKMOTION || type == Input::IT_STICKBUTTON) if (type == Input::IT_STICKMOTION ||
type == Input::IT_STICKBUTTON)
{ {
GamePadDevice* gp = getDeviceList()->getGamePadFromIrrID(deviceID); GamePadDevice* gp =
getDeviceList()->getGamePadFromIrrID(deviceID);
if (gp != NULL && if (gp != NULL &&
abs(value)>gp->m_deadzone) abs(value)>gp->m_deadzone)
{ {
//I18N: message shown when an input device is used but is not associated to any player //I18N: message shown when an input device is used but
GUIEngine::showMessage(_("Ignoring '%s', you needed to join earlier to play!", // is not associated to any player
GUIEngine::showMessage(
_("Ignoring '%s', you needed to join earlier to play!",
irr::core::stringw(gp->m_name.c_str()).c_str()) ); irr::core::stringw(gp->m_name.c_str()).c_str()) );
} }
} }
@ -538,27 +570,36 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
m_timer = 0.25; m_timer = 0.25;
} }
// player may be NULL in early menus, before player setup has been performed // player may be NULL in early menus, before player setup has
// been performed
int playerID = (player == NULL ? 0 : player->getID()); int playerID = (player == NULL ? 0 : player->getID());
// If only the master player can act, and this player is not the master, ignore his input // If only the master player can act, and this player is not
if (m_device_manager->getAssignMode() == ASSIGN && m_master_player_only && // the master, ignore his input
if (m_device_manager->getAssignMode() == ASSIGN &&
m_master_player_only &&
playerID != PLAYER_ID_GAME_MASTER) playerID != PLAYER_ID_GAME_MASTER)
{ {
//I18N: message shown when a player that isn't game master tries to modify options that //I18N: message shown when a player that isn't game master
//I18N: only the game master is allowed to //I18N: tries to modify options that only the game master
GUIEngine::showMessage(_("Only the Game Master may act at this point!")); //I18N: is allowed to
GUIEngine::showMessage(
_("Only the Game Master may act at this point!"));
return; return;
} }
// all is good, pass the translated input event on to the event handler // all is good, pass the translated input event on to the
GUIEngine::EventHandler::get()->processGUIAction(action, deviceID, abs(value), type, playerID); // event handler
GUIEngine::EventHandler::get()
->processGUIAction(action, deviceID, abs(value), type,
playerID);
} }
} }
} }
else if (type == Input::IT_KEYBOARD) else if (type == Input::IT_KEYBOARD)
{ {
// keyboard press not handled by device manager / bindings. Check static bindings... // keyboard press not handled by device manager / bindings.
// Check static bindings...
handleStaticAction( button, value ); handleStaticAction( button, value );
} }
} // input } // input
@ -568,14 +609,16 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
void InputManager::setMasterPlayerOnly(bool enabled) void InputManager::setMasterPlayerOnly(bool enabled)
{ {
#if INPUT_MODE_DEBUG #if INPUT_MODE_DEBUG
std::cout << "====== InputManager::setMasterPlayerOnly(" << enabled << ") ======\n"; std::cout <<
"====== InputManager::setMasterPlayerOnly(" << enabled << ") ======\n";
#endif #endif
m_master_player_only = enabled; m_master_player_only = enabled;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Returns whether only the master player should be allowed to perform changes in menus */ /** Returns whether only the master player should be allowed to perform changes
* in menus */
bool InputManager::masterPlayerOnly() const bool InputManager::masterPlayerOnly() const
{ {
return m_device_manager->getAssignMode() == ASSIGN && m_master_player_only; return m_device_manager->getAssignMode() == ASSIGN && m_master_player_only;
@ -598,8 +641,10 @@ EventPropagation InputManager::input(const SEvent& event)
{ {
if (event.EventType == EET_JOYSTICK_INPUT_EVENT) if (event.EventType == EET_JOYSTICK_INPUT_EVENT)
{ {
// Axes - FIXME, instead of checking all of them, ask the bindings which ones to poll // Axes - FIXME, instead of checking all of them, ask the bindings
for (int axis_id=0; axis_id<SEvent::SJoystickEvent::NUMBER_OF_AXES ; axis_id++) // which ones to poll
for (int axis_id=0; axis_id<SEvent::SJoystickEvent::NUMBER_OF_AXES ;
axis_id++)
{ {
int value = event.JoystickEvent.Axis[axis_id]; int value = event.JoystickEvent.Axis[axis_id];
@ -609,27 +654,32 @@ EventPropagation InputManager::input(const SEvent& event)
event.JoystickEvent.Joystick, axis_id, value); event.JoystickEvent.Joystick, axis_id, value);
} }
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, axis_id, dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
Input::AD_NEUTRAL, value); axis_id, Input::AD_NEUTRAL, value);
} }
if (event.JoystickEvent.POV == 65535) if (event.JoystickEvent.POV == 65535)
{ {
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_H_ID, Input::AD_NEUTRAL, dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
0); Input::HAT_H_ID, Input::AD_NEUTRAL, 0);
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_V_ID, Input::AD_NEUTRAL, dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
0); Input::HAT_V_ID, Input::AD_NEUTRAL, 0);
} }
else else
{ {
// *0.017453925f is to convert degrees to radians // *0.017453925f is to convert degrees to radians
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_H_ID, Input::AD_NEUTRAL, dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
(int)(cos(event.JoystickEvent.POV*0.017453925f/100.0f)*Input::MAX_VALUE)); Input::HAT_H_ID, Input::AD_NEUTRAL,
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_V_ID, Input::AD_NEUTRAL, (int)(cos(event.JoystickEvent.POV*0.017453925f/100.0f)
(int)(sin(event.JoystickEvent.POV*0.017453925f/100.0f)*Input::MAX_VALUE)); *Input::MAX_VALUE));
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
Input::HAT_V_ID, Input::AD_NEUTRAL,
(int)(sin(event.JoystickEvent.POV*0.017453925f/100.0f)
*Input::MAX_VALUE));
} }
GamePadDevice* gp = getDeviceList()->getGamePadFromIrrID(event.JoystickEvent.Joystick); GamePadDevice* gp =
getDeviceList()->getGamePadFromIrrID(event.JoystickEvent.Joystick);
if (gp == NULL) if (gp == NULL)
{ {
@ -642,14 +692,17 @@ EventPropagation InputManager::input(const SEvent& event)
const bool isButtonPressed = event.JoystickEvent.IsButtonPressed(i); const bool isButtonPressed = event.JoystickEvent.IsButtonPressed(i);
// Only report button events when the state of the button changes // Only report button events when the state of the button changes
if ((!gp->isButtonPressed(i) && isButtonPressed) || (gp->isButtonPressed(i) && !isButtonPressed)) if ((!gp->isButtonPressed(i) && isButtonPressed) ||
(gp->isButtonPressed(i) && !isButtonPressed) )
{ {
if (UserConfigParams::m_gamepad_debug) if (UserConfigParams::m_gamepad_debug)
{ {
printf("button %i, status=%i\n", i, isButtonPressed); printf("button %i, status=%i\n", i, isButtonPressed);
} }
dispatchInput(Input::IT_STICKBUTTON, event.JoystickEvent.Joystick, i, Input::AD_POSITIVE, dispatchInput(Input::IT_STICKBUTTON,
event.JoystickEvent.Joystick, i,
Input::AD_POSITIVE,
isButtonPressed ? Input::MAX_VALUE : 0); isButtonPressed ? Input::MAX_VALUE : 0);
} }
gp->setButtonPressed(i, isButtonPressed); gp->setButtonPressed(i, isButtonPressed);
@ -676,9 +729,10 @@ EventPropagation InputManager::input(const SEvent& event)
StateManager::get()->escapePressed(); StateManager::get()->escapePressed();
return EVENT_BLOCK; return EVENT_BLOCK;
} }
// 'backspace' in a text control must never be mapped, since user can be in a text // 'backspace' in a text control must never be mapped, since user
// area trying to erase text (and if it's mapped to rescue that would dismiss the // can be in a text area trying to erase text (and if it's mapped
// dialog instead of erasing a single letter). Same for spacebar. Same for letters. // to rescue that would dismiss the dialog instead of erasing a
// single letter). Same for spacebar. Same for letters.
if (GUIEngine::isWithinATextBox()) if (GUIEngine::isWithinATextBox())
{ {
if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT) if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT)
@ -696,7 +750,8 @@ EventPropagation InputManager::input(const SEvent& event)
dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key, dispatchInput(Input::IT_KEYBOARD, event.KeyInput.Char, key,
Input::AD_POSITIVE, Input::MAX_VALUE); Input::AD_POSITIVE, Input::MAX_VALUE);
// if this action took us into a text box, don't let event continue (FIXME not the cleanest solution) // if this action took us into a text box, don't let event continue
// (FIXME not the cleanest solution)
if (!wasInTextBox && GUIEngine::isWithinATextBox()) if (!wasInTextBox && GUIEngine::isWithinATextBox())
{ {
return EVENT_BLOCK; return EVENT_BLOCK;
@ -705,9 +760,10 @@ EventPropagation InputManager::input(const SEvent& event)
} }
else else
{ {
// 'backspace' in a text control must never be mapped, since user can be in a text // 'backspace' in a text control must never be mapped, since user
// area trying to erase text (and if it's mapped to rescue that would dismiss the // can be in a text area trying to erase text (and if it's mapped
// dialog instead of erasing a single letter). Same for spacebar. Same for letters. // to rescue that would dismiss the dialog instead of erasing a
// single letter). Same for spacebar. Same for letters.
if (GUIEngine::isWithinATextBox()) if (GUIEngine::isWithinATextBox())
{ {
if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT) if (key == KEY_BACK || key == KEY_SPACE || key == KEY_SHIFT)
@ -745,16 +801,21 @@ EventPropagation InputManager::input(const SEvent& event)
EMIE_RMOUSE_LEFT_UP Right mouse button was left up. EMIE_RMOUSE_LEFT_UP Right mouse button was left up.
EMIE_MMOUSE_LEFT_UP Middle mouse button was left up. EMIE_MMOUSE_LEFT_UP Middle mouse button was left up.
EMIE_MOUSE_MOVED The mouse cursor changed its position. EMIE_MOUSE_MOVED The mouse cursor changed its position.
EMIE_MOUSE_WHEEL The mouse wheel was moved. Use Wheel value in event data to find out in what direction and how fast. EMIE_MOUSE_WHEEL The mouse wheel was moved. Use Wheel value in
event data to find out in what direction and
how fast.
*/ */
} }
#endif #endif
// block events in all modes but initial menus (except in text boxes to allow typing, // block events in all modes but initial menus (except in text boxes to
// and except in modal dialogs in-game) // allow typing, and except in modal dialogs in-game)
// FIXME: 1) that's awful logic 2) that's not what the code below does, events are never blocked in menus // FIXME: 1) that's awful logic 2) that's not what the code below does,
if (getDeviceList()->getAssignMode() != NO_ASSIGN && !GUIEngine::isWithinATextBox() && // events are never blocked in menus
(!GUIEngine::ModalDialog::isADialogActive() && StateManager::get()->getGameState() == GUIEngine::GAME)) if (getDeviceList()->getAssignMode() != NO_ASSIGN &&
!GUIEngine::isWithinATextBox() &&
(!GUIEngine::ModalDialog::isADialogActive() &&
StateManager::get()->getGameState() == GUIEngine::GAME))
{ {
return EVENT_BLOCK; return EVENT_BLOCK;
} }
@ -837,9 +898,9 @@ void InputManager::setMode(InputDriverMode new_mode)
m_sensed_input_high_gamepad.clear(); m_sensed_input_high_gamepad.clear();
m_sensed_input_high_kbd.clear(); m_sensed_input_high_kbd.clear();
// The order is deliberate just in case someone starts to make // The order is deliberate just in case someone starts
// STK multithreaded: m_sensed_input must not be 0 when // to make STK multithreaded: m_sensed_input must not be
// mode == INPUT_SENSE_PREFER_{AXIS,BUTTON}. // 0 when mode == INPUT_SENSE_PREFER_{AXIS,BUTTON}.
m_mode = MENU; m_mode = MENU;
break; break;
@ -886,8 +947,8 @@ void InputManager::setMode(InputDriverMode new_mode)
// We must be in menu mode now in order to switch. // We must be in menu mode now in order to switch.
assert (m_mode == MENU); assert (m_mode == MENU);
// Reset the helper values for the relative mouse movement supresses to // Reset the helper values for the relative mouse movement
// the notification of them as an input. // supresses to the notification of them as an input.
m_mouse_val_x = m_mouse_val_y = 0; m_mouse_val_x = m_mouse_val_y = 0;
m_mode = new_mode; m_mode = new_mode;

View File

@ -31,6 +31,8 @@
#include "network/network_manager.hpp" #include "network/network_manager.hpp"
#include "utils/constants.hpp" #include "utils/constants.hpp"
#define SWAT_ANGLE 22.0f
Attachment::Attachment(Kart* kart) Attachment::Attachment(Kart* kart)
{ {
m_type = ATTACH_NOTHING; m_type = ATTACH_NOTHING;
@ -41,7 +43,7 @@ Attachment::Attachment(Kart* kart)
// If we attach a NULL mesh, we get a NULL scene node back. So we // If we attach a NULL mesh, we get a NULL scene node back. So we
// have to attach some kind of mesh, but make it invisible. // have to attach some kind of mesh, but make it invisible.
m_node = irr_driver->addAnimatedMesh( m_node = irr_driver->addAnimatedMesh(
attachment_manager->getMesh(ATTACH_BOMB)); attachment_manager->getMesh(Attachment::ATTACH_BOMB));
#ifdef DEBUG #ifdef DEBUG
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());
@ -58,11 +60,10 @@ Attachment::~Attachment()
} // ~Attachment } // ~Attachment
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void Attachment::set(attachmentType type, float time, Kart *current_kart) void Attachment::set(AttachmentType type, float time, Kart *current_kart)
{ {
clear(); clear();
m_node->setMesh(attachment_manager->getMesh(type)); m_node->setMesh(attachment_manager->getMesh(type));
if (!UserConfigParams::m_graphical_effects) if (!UserConfigParams::m_graphical_effects)
@ -74,6 +75,15 @@ void Attachment::set(attachmentType type, float time, Kart *current_kart)
m_type = type; m_type = type;
m_time_left = time; m_time_left = time;
m_previous_owner = current_kart; m_previous_owner = current_kart;
m_node->setRotation(core::vector3df(0, 0, 0));
m_count = m_type==ATTACH_SWATTER
? m_kart->getKartProperties()->getSwatterCount()
: 1;
m_animation_timer = 0.0f;
m_animation_phase = SWATTER_AIMING;
m_rot_per_sec = core::vector3df(0,0,0);
m_animation_target = NULL;
// A parachute can be attached as result of the usage of an item. In this // A parachute can be attached as result of the usage of an item. In this
// case we have to save the current kart speed so that it can be detached // case we have to save the current kart speed so that it can be detached
// by slowing down. // by slowing down.
@ -98,6 +108,7 @@ void Attachment::set(attachmentType type, float time, Kart *current_kart)
void Attachment::clear() void Attachment::clear()
{ {
m_type=ATTACH_NOTHING; m_type=ATTACH_NOTHING;
m_time_left=0.0; m_time_left=0.0;
m_node->setVisible(false); m_node->setVisible(false);
@ -108,6 +119,12 @@ void Attachment::clear()
} // clear } // clear
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
/** Randomly selects the new attachment. For a server process, the
* attachment can be passed into this function.
* \param item The item that was collected.
* \param new_attachment Optional: only used on the clients, it
* specifies the new attachment to use
*/
void Attachment::hitBanana(Item *item, int new_attachment) void Attachment::hitBanana(Item *item, int new_attachment)
{ {
float leftover_time = 0.0f; float leftover_time = 0.0f;
@ -228,6 +245,11 @@ void Attachment::update(float dt)
case ATTACH_NOTHING: // Nothing to do, but complete all cases for switch case ATTACH_NOTHING: // Nothing to do, but complete all cases for switch
case ATTACH_MAX: case ATTACH_MAX:
break; break;
case ATTACH_SWATTER:
updateSwatter(dt);
// If the swatter is used up, trigger cleaning up
if(m_count==0) m_time_left = -1.0f;
break;
case ATTACH_BOMB: case ATTACH_BOMB:
if(m_time_left <= (m_node->getEndFrame() - m_node->getStartFrame() - 1)) if(m_time_left <= (m_node->getEndFrame() - m_node->getStartFrame() - 1))
{ {
@ -249,4 +271,183 @@ void Attachment::update(float dt)
if ( m_time_left <= 0.0f) if ( m_time_left <= 0.0f)
clear(); clear();
} // update } // update
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Updates an armed swatter: it checks for any karts that are close enough
* and not invulnerable, it swats the kart.
* \param dt Time step size.
*/
void Attachment::updateSwatter(float dt)
{
core::vector3df r = m_node->getRotation();
r += m_rot_per_sec * dt;
switch(m_animation_phase)
{
case SWATTER_AIMING:
aimSwatter();
break;
case SWATTER_TO_KART:
if(fabsf(r.Z)>=90)
{
checkForHitKart(r.Z>0);
m_rot_per_sec *= -1.0f;
m_animation_phase = SWATTER_BACK_FROM_KART;
}
break;
case SWATTER_BACK_FROM_KART:
if (r.Z>0)
{
r = core::vector3df(0,0,0);
m_rot_per_sec = r;
m_animation_phase = SWATTER_AIMING;
m_animation_target = NULL;
}
break;
case SWATTER_ITEM_1: // swatter going to the left
if(r.Z>SWAT_ANGLE)
{
m_animation_phase = SWATTER_ITEM_2;
m_rot_per_sec *= -1.0f;
}
break;
case SWATTER_ITEM_2: // swatter going all the way to the right
if(r.Z<-SWAT_ANGLE)
{
m_animation_phase = SWATTER_ITEM_3;
m_rot_per_sec *= -1.0f;
}
break;
case SWATTER_ITEM_3: // swatter going back to rest position.
if(r.Z>0)
{
r = core::vector3df(0,0,0);
m_rot_per_sec = r;
m_animation_phase = SWATTER_AIMING;
}
break;
} // switch m_animation_phase
m_node->setRotation(r);
} // updateSwatter
//-----------------------------------------------------------------------------
/** Returns true if the point xyz is to the left of the kart.
* \param xyz Point to determine the direction
*/
bool Attachment::isLeftSideOfKart(const Vec3 &xyz)
{
Vec3 forw_vec = m_kart->getTrans().getBasis().getColumn(2);
const Vec3& k1 = m_kart->getXYZ();
const Vec3 k2 = k1+forw_vec;
return xyz.sideOfLine2D(k1, k2)>0;
} // isLeftSideOfKart
//-----------------------------------------------------------------------------
/** This function is called when the swatter reaches the hit angle (i.e. it
* is furthest down). Check all karts if any one is hit, i.e. is at the right
* side and at the right angle and distance.
* \param isWattingLeft True if the swatter is aiming to the left side
* of the kart.
*/
void Attachment::checkForHitKart(bool isSwattingLeft)
{
// Square of the minimum distance
const KartProperties *kp = m_kart->getKartProperties();
float min_dist2 = kp->getSwatterDistance2();
Kart *hit_kart = NULL;
Vec3 forw_vec = m_kart->getTrans().getBasis().getColumn(2);
const World *world = World::getWorld();
for(unsigned int i=0; i<world->getNumKarts(); i++)
{
Kart *kart = world->getKart(i);
if(kart->isEliminated() || kart==m_kart || kart->isSquashed())
continue;
float f = (kart->getXYZ()-m_kart->getXYZ()).length2();
// Distance is too great, ignore this kart.
if(f>min_dist2) continue;
// Check if the kart is at the right side.
const bool left = isLeftSideOfKart(kart->getXYZ());
if(left!=isSwattingLeft)
{
//printf("%s wrong side: %d %d\n",
// kart->getIdent().c_str(), left, isSwattingLeft);
continue;
}
Vec3 kart_vec = m_kart->getXYZ()-kart->getXYZ();
// cos alpha = a*b/||a||/||b||
// Since forw_vec is a unit vector, we only have to divide by
// the length of the vector to the kart.
float cos_angle = kart_vec.dot(forw_vec)/kart_vec.length();
float angle = acosf(cos_angle)*180/M_PI;
if(angle<45 || angle>135)
{
//printf("%s angle %f\n", kart->getIdent().c_str(), angle);
continue;
}
kart->setSquash(kp->getSquashDuration(),
kp->getSquashSlowdown());
// It is assumed that only one kart is within reach of the swatter,
// so we can stop testing karts here.
return;
} // for i < num_karts
} // angleToKart
//-----------------------------------------------------------------------------
/** Checks for any kart that is not already squashed that is close enough.
* If a kart is found, it changes the state of the swatter to be
* SWATTER_TARGET and starts the animation.
*/
void Attachment::aimSwatter()
{
const World *world = World::getWorld();
Kart *min_kart = NULL;
// Square of the minimum distance
float min_dist2 = m_kart->getKartProperties()->getSwatterDistance2();
for(unsigned int i=0; i<world->getNumKarts(); i++)
{
Kart *kart = world->getKart(i);
if(kart->isEliminated() || kart==m_kart || kart->isSquashed())
continue;
float f = (kart->getXYZ()-m_kart->getXYZ()).length2();
if(f<min_dist2)
{
min_dist2 = f;
min_kart = kart;
}
}
// No kart close enough, nothing to do.
if(!min_kart) return;
m_count --;
m_animation_phase = SWATTER_TO_KART;
m_animation_target = min_kart;
const KartProperties *kp = m_kart->getKartProperties();
m_animation_timer = kp->getSwatterAnimationTime();
const bool left = isLeftSideOfKart(min_kart->getXYZ());
m_rot_per_sec = core::vector3df(0, 0,
left ?90.0f:-90.0f) / m_animation_timer;
} // aimSwatter
//-----------------------------------------------------------------------------
/** Starts a (smaller) and faster swatting movement to be played
* when the kart is hit by an item.
*/
void Attachment::swatItem()
{
assert(m_type==ATTACH_SWATTER);
assert(m_animation_target==NULL);
m_animation_phase = SWATTER_ITEM_1;
m_animation_timer =
m_kart->getKartProperties()->getSwatterItemAnimationTime();
m_count--;
m_rot_per_sec = core::vector3df(0, 0, SWAT_ANGLE) / m_animation_timer;
} // swatItem

View File

@ -27,72 +27,115 @@
class Kart; class Kart;
class Item; class Item;
// Some loop in attachment.cpp depend on ATTACH_FIRST and ATTACH_MAX.
// So if new elements are added, make sure to add them in between those values.
// Also, please note that Attachment::Attachment relies on ATTACH_FIRST being 0.
enum attachmentType
{
ATTACH_FIRST = 0,
ATTACH_PARACHUTE = 0,
ATTACH_BOMB,
ATTACH_ANVIL,
ATTACH_TINYTUX,
ATTACH_MAX,
ATTACH_NOTHING
};
/** /**
* \ingroup items * \ingroup items
*/ */
class Attachment: public NoCopy class Attachment: public NoCopy
{ {
public:
// Some loop in attachment.cpp depend on ATTACH_FIRST and ATTACH_MAX.
// So if new elements are added, make sure to add them in between those values.
// Also, please note that Attachment::Attachment relies on ATTACH_FIRST being 0.
enum AttachmentType
{
ATTACH_FIRST = 0,
ATTACH_PARACHUTE = 0,
ATTACH_BOMB,
ATTACH_ANVIL,
ATTACH_SWATTER,
ATTACH_TINYTUX,
ATTACH_MAX,
ATTACH_NOTHING
};
private: private:
attachmentType m_type; //!< attachment type /** Attachment type. */
Kart *m_kart; //!< kart the attachment is attached to AttachmentType m_type;
float m_time_left; //!< time left till attachment expires
float m_initial_speed; //!< for parachutes only /** Kart the attachment is attached to. */
Kart *m_kart;
/** How often an attachment can be used (e.g. swatter). */
int m_count;
/** Time left till attachment expires. */
float m_time_left;
/** For parachutes only. */
float m_initial_speed;
/** Scene node of the attachment, which will be attached to the kart's /** Scene node of the attachment, which will be attached to the kart's
* scene node. */ * scene node. */
scene::IAnimatedMeshSceneNode scene::IAnimatedMeshSceneNode
*m_node; *m_node;
Kart *m_previous_owner; //!< used by bombs so that it's not passed
//!< back to previous owner /** Used by bombs so that it's not passed back to previous owner. */
Kart *m_previous_owner;
/** State of a swatter animation. The swatter is either aiming (looking
* for a kart and/or swatting an incoming item), moving towards or back
* from a kart, or swatting left and right to hit an item - which has
* three phases: going left to an angle a (phae 1), then going to -a
* (phase 2), then going back to 0. */
enum {SWATTER_AIMING, SWATTER_TO_KART,
SWATTER_BACK_FROM_KART,
SWATTER_ITEM_1, SWATTER_ITEM_2, SWATTER_ITEM_3,
SWATTER_BACK_FROM_ITEM} m_animation_phase;
/** Timer for swatter animation. */
float m_animation_timer;
/** The kart the swatter is aiming at. */
Kart *m_animation_target;
/** Rotation per second so that the swatter will hit the kart. */
core::vector3df m_rot_per_sec;
RandomGenerator m_random; RandomGenerator m_random;
void aimSwatter();
bool isLeftSideOfKart(const Vec3 &xyz);
void checkForHitKart(bool isSwattingToLeft);
public: public:
Attachment(Kart* _kart); Attachment(Kart* kart);
~Attachment(); ~Attachment();
void set (attachmentType _type, float time, Kart *previous_kart=NULL);
void set (attachmentType _type) { set(_type, m_time_left); }
void clear (); void clear ();
attachmentType getType () const { return m_type; }
float getTimeLeft () const { return m_time_left; }
void setTimeLeft (float t){ m_time_left = t; }
Kart* getPreviousOwner () const { return m_previous_owner; }
float weightAdjust () const {
return m_type==ATTACH_ANVIL
?stk_config->m_anvil_weight:0.0f;
}
float airResistanceAdjust () const {
return m_type==ATTACH_PARACHUTE
?stk_config->m_parachute_friction:0.0f;
}
float speedAdjust () const {
return m_type==ATTACH_ANVIL
?stk_config->m_anvil_speed_factor:1.0f;
}
/** Randomly selects the new attachment. For a server process, the
* attachment can be passed into this function.
* \param item The item that was collected.
* \param new_attachment Optional: only used on the clients, it
* specifies the new attachment to use
*/
void hitBanana(Item *item, int new_attachment=-1); void hitBanana(Item *item, int new_attachment=-1);
void update (float dt); void update (float dt);
void updateSwatter(float dt);
void moveBombFromTo(Kart *from, Kart *to); void moveBombFromTo(Kart *from, Kart *to);
}; void swatItem();
void set (AttachmentType type, float time, Kart *previous_kart=NULL);
// ------------------------------------------------------------------------
/** Sets the type of the attachment, but keeps the old time left value. */
void set (AttachmentType type) { set(type, m_time_left); }
// ------------------------------------------------------------------------
/** Returns the type of this attachment. */
AttachmentType getType() const { return m_type; }
// ------------------------------------------------------------------------
/** Returns how much time is left before this attachment is removed. */
float getTimeLeft() const { return m_time_left; }
// ------------------------------------------------------------------------
/** Sets how long this attachment will remain attached. */
void setTimeLeft(float t){ m_time_left = t; }
// ------------------------------------------------------------------------
/** Returns the previous owner of this attachment, used in bombs that
* are being passed between karts. */
Kart* getPreviousOwner() const { return m_previous_owner; }
// ------------------------------------------------------------------------
/** Returns additional weight for the kart. */
float weightAdjust() const {
return m_type==ATTACH_ANVIL ? stk_config->m_anvil_weight : 0.0f; }
// ------------------------------------------------------------------------
/** Returns if the swatter is currently aiming, i.e. can be used to
* swat an incoming projectile. */
bool isSwatterReady() const {
assert(m_type==ATTACH_SWATTER);
return m_animation_phase == SWATTER_AIMING;
} // isSwatterReady
// ------------------------------------------------------------------------
}; // Attachment
#endif #endif

View File

@ -25,7 +25,9 @@
AttachmentManager *attachment_manager = 0; AttachmentManager *attachment_manager = 0;
struct initAttachmentType {attachmentType attachment; const char *file; const char *icon_file;}; struct initAttachmentType {Attachment::AttachmentType attachment;
const char *file;
const char *icon_file;};
/* Some explanations to the attachments: /* Some explanations to the attachments:
Parachute: This will increase the air friction, reducing the maximum speed. Parachute: This will increase the air friction, reducing the maximum speed.
@ -46,17 +48,18 @@ struct initAttachmentType {attachmentType attachment; const char *file; const c
initAttachmentType iat[]= initAttachmentType iat[]=
{ {
{ATTACH_PARACHUTE, "parachute.b3d", "parachute-attach-icon.png"}, {Attachment::ATTACH_PARACHUTE, "parachute.b3d", "parachute-attach-icon.png"},
{ATTACH_BOMB, "bomb.b3d", "bomb-attach-icon.png" }, {Attachment::ATTACH_BOMB, "bomb.b3d", "bomb-attach-icon.png" },
{ATTACH_ANVIL, "anchor.b3d", "anchor-attach-icon.png" }, {Attachment::ATTACH_ANVIL, "anchor.b3d", "anchor-attach-icon.png" },
{ATTACH_TINYTUX, "reset-button.b3d","reset-attach-icon.png" }, {Attachment::ATTACH_SWATTER, "swatter.b3d", "swatter-icon.png" },
{ATTACH_MAX, "", "" }, {Attachment::ATTACH_TINYTUX, "reset-button.b3d","reset-attach-icon.png" },
{Attachment::ATTACH_MAX, "", "" },
}; };
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
AttachmentManager::~AttachmentManager() AttachmentManager::~AttachmentManager()
{ {
for(int i=0; iat[i].attachment!=ATTACH_MAX; i++) for(int i=0; iat[i].attachment!=Attachment::ATTACH_MAX; i++)
{ {
scene::IMesh *mesh = m_attachments[iat[i].attachment]; scene::IMesh *mesh = m_attachments[iat[i].attachment];
mesh->drop(); mesh->drop();
@ -73,7 +76,7 @@ AttachmentManager::~AttachmentManager()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void AttachmentManager::removeTextures() void AttachmentManager::removeTextures()
{ {
for(int i=0; iat[i].attachment!=ATTACH_MAX; i++) for(int i=0; iat[i].attachment!=Attachment::ATTACH_MAX; i++)
{ {
// FIXME: free attachment textures // FIXME: free attachment textures
} // for } // for
@ -82,7 +85,7 @@ void AttachmentManager::removeTextures()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void AttachmentManager::loadModels() void AttachmentManager::loadModels()
{ {
for(int i=0; iat[i].attachment!=ATTACH_MAX; i++) for(int i=0; iat[i].attachment!=Attachment::ATTACH_MAX; i++)
{ {
std::string full_path = file_manager->getModelFile(iat[i].file); std::string full_path = file_manager->getModelFile(iat[i].file);
m_attachments[iat[i].attachment]=irr_driver->getAnimatedMesh(full_path); m_attachments[iat[i].attachment]=irr_driver->getAnimatedMesh(full_path);

View File

@ -33,20 +33,23 @@ namespace irr
class AttachmentManager: public NoCopy class AttachmentManager: public NoCopy
{ {
private: private:
scene::IAnimatedMesh *m_attachments[ATTACH_MAX]; scene::IAnimatedMesh *m_attachments[Attachment::ATTACH_MAX];
Material *m_all_icons [ATTACH_MAX]; Material *m_all_icons [Attachment::ATTACH_MAX];
public: public:
AttachmentManager() {}; AttachmentManager() {};
~AttachmentManager(); ~AttachmentManager();
void removeTextures (); void removeTextures ();
void loadModels (); void loadModels ();
// ------------------------------------------------------------------------
/** Returns the mest for a certain attachment. /** Returns the mest for a certain attachment.
* \param type Type of the attachment needed. */ * \param type Type of the attachment needed. */
scene::IAnimatedMesh *getMesh(attachmentType type) const {return m_attachments[type]; } scene::IAnimatedMesh *getMesh(Attachment::AttachmentType type) const
{return m_attachments[type]; }
// ------------------------------------------------------------------------
/** Returns the icon to display in the race gui if a kart /** Returns the icon to display in the race gui if a kart
* has an attachment. */ * has an attachment. */
const Material* const Material* getIcon (int type) const {return m_all_icons [type]; }
getIcon (int type) const {return m_all_icons [type];} // ------------------------------------------------------------------------
}; };
extern AttachmentManager *attachment_manager; extern AttachmentManager *attachment_manager;

View File

@ -142,6 +142,10 @@ void Powerup::set(PowerupManager::PowerupType type, int n)
switch (m_type) switch (m_type)
{ {
// No sound effect when arming the glove
case PowerupManager::POWERUP_SWATTER:
break;
case PowerupManager::POWERUP_ZIPPER: case PowerupManager::POWERUP_ZIPPER:
break ; break ;
@ -193,6 +197,7 @@ void Powerup::use()
{ {
// Play custom kart sound when collectible is used // Play custom kart sound when collectible is used
if (m_type != PowerupManager::POWERUP_NOTHING && if (m_type != PowerupManager::POWERUP_NOTHING &&
m_type != PowerupManager::POWERUP_SWATTER &&
m_type != PowerupManager::POWERUP_ZIPPER) m_type != PowerupManager::POWERUP_ZIPPER)
m_owner->playCustomSFX(SFXManager::CUSTOM_SHOOT); m_owner->playCustomSFX(SFXManager::CUSTOM_SHOOT);
@ -248,6 +253,10 @@ void Powerup::use()
projectile_manager->newProjectile(m_owner, m_type); projectile_manager->newProjectile(m_owner, m_type);
break ; break ;
case PowerupManager::POWERUP_SWATTER:
m_owner->getAttachment()->set(Attachment::ATTACH_SWATTER,
m_owner->getKartProperties()->getSwatterDuration());
break;
case PowerupManager::POWERUP_BUBBLEGUM: case PowerupManager::POWERUP_BUBBLEGUM:
{ {
Vec3 hit_point; Vec3 hit_point;
@ -302,7 +311,8 @@ void Powerup::use()
if(kart == m_owner) continue; if(kart == m_owner) continue;
if(kart->getPosition() == 1) if(kart->getPosition() == 1)
{ {
kart->attach(ATTACH_ANVIL, stk_config->m_anvil_time); kart->attach(Attachment::ATTACH_ANVIL,
stk_config->m_anvil_time);
kart->updatedWeight(); kart->updatedWeight();
kart->adjustSpeed(stk_config->m_anvil_speed_factor*0.5f); kart->adjustSpeed(stk_config->m_anvil_speed_factor*0.5f);
@ -338,7 +348,8 @@ void Powerup::use()
if(kart->isEliminated() || kart== m_owner) continue; if(kart->isEliminated() || kart== m_owner) continue;
if(m_owner->getPosition() > kart->getPosition()) if(m_owner->getPosition() > kart->getPosition())
{ {
kart->attach(ATTACH_PARACHUTE, stk_config->m_parachute_time_other); kart->attach(Attachment::ATTACH_PARACHUTE,
stk_config->m_parachute_time_other);
if(kart->getController()->isPlayerController()) if(kart->getController()->isPlayerController())
player_kart = kart; player_kart = kart;

View File

@ -93,7 +93,7 @@ PowerupManager::PowerupType
static std::string powerup_names[] = { static std::string powerup_names[] = {
"", /* Nothing */ "", /* Nothing */
"bubblegum", "cake", "bowling", "zipper", "plunger", "switch", "bubblegum", "cake", "bowling", "zipper", "plunger", "switch",
"parachute", "anchor" "swatter", "parachute", "anchor"
}; };
for(unsigned int i=POWERUP_FIRST; i<=POWERUP_LAST; i++) for(unsigned int i=POWERUP_FIRST; i<=POWERUP_LAST; i++)

View File

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

View File

@ -205,7 +205,8 @@ void DefaultAIController::update(float dt)
// Special behaviour if we have a bomb attach: try to hit the kart ahead // Special behaviour if we have a bomb attach: try to hit the kart ahead
// of us. // of us.
bool commands_set = false; bool commands_set = false;
if(m_handle_bomb && m_kart->getAttachment()->getType()==ATTACH_BOMB && if(m_handle_bomb &&
m_kart->getAttachment()->getType()==Attachment::ATTACH_BOMB &&
m_kart_ahead ) m_kart_ahead )
{ {
// Use nitro if the kart is far ahead, or faster than this kart // Use nitro if the kart is far ahead, or faster than this kart
@ -559,6 +560,23 @@ void DefaultAIController::handleItems(const float dt)
} }
break; // POWERUP_ANVIL break; // POWERUP_ANVIL
case PowerupManager::POWERUP_SWATTER:
{
// Squared distance for which the swatter works
float d2 = m_kart->getKartProperties()->getSwatterDistance2();
// Fire if the closest kart ahead or to the back is not already
// squashed and close enough.
// FIXME: this can be improved on, since more than one kart might
// be hit, and a kart ahead might not be at an angle at
// which the glove can be used.
if( ( m_kart_ahead && !m_kart_ahead->isSquashed() &&
(m_kart_ahead->getXYZ()-m_kart->getXYZ()).length2()<d2 &&
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;
break;
}
default: default:
printf("Invalid or unhandled powerup '%d' in default AI.\n", printf("Invalid or unhandled powerup '%d' in default AI.\n",
m_kart->getPowerup()->getType()); m_kart->getPowerup()->getType());
@ -728,8 +746,8 @@ void DefaultAIController::handleNitroAndZipper()
// If a parachute or anvil is attached, the nitro doesn't give much // If a parachute or anvil is attached, the nitro doesn't give much
// benefit. Better wait till later. // benefit. Better wait till later.
const bool has_slowdown_attachment = const bool has_slowdown_attachment =
m_kart->getAttachment()->getType()==ATTACH_PARACHUTE || m_kart->getAttachment()->getType()==Attachment::ATTACH_PARACHUTE ||
m_kart->getAttachment()->getType()==ATTACH_ANVIL; m_kart->getAttachment()->getType()==Attachment::ATTACH_ANVIL;
if(has_slowdown_attachment) return; if(has_slowdown_attachment) return;
// If the kart is very slow (e.g. after rescue), use nitro // If the kart is very slow (e.g. after rescue), use nitro

View File

@ -148,7 +148,8 @@ void NewAIController::update(float dt)
// Special behaviour if we have a bomb attach: try to hit the kart ahead // Special behaviour if we have a bomb attach: try to hit the kart ahead
// of us. // of us.
bool commands_set = false; bool commands_set = false;
if(m_handle_bomb && m_kart->getAttachment()->getType()==ATTACH_BOMB && if(m_handle_bomb &&
m_kart->getAttachment()->getType()==Attachment::ATTACH_BOMB &&
m_kart_ahead ) m_kart_ahead )
{ {
// Use nitro if the kart is far ahead, or faster than this kart // Use nitro if the kart is far ahead, or faster than this kart
@ -442,6 +443,7 @@ void NewAIController::handleItems( const float DELTA, const int STEPS )
m_controls->m_fire = m_time_since_last_shot > 3.0f && m_controls->m_fire = m_time_since_last_shot > 3.0f &&
m_kart->getPosition()>1; m_kart->getPosition()>1;
} }
case PowerupManager::POWERUP_SWATTER: // fallthrough
default: default:
m_controls->m_fire = true; m_controls->m_fire = true;
} }
@ -600,8 +602,8 @@ void NewAIController::handleNitroAndZipper()
// If a parachute or anvil is attached, the nitro doesn't give much // If a parachute or anvil is attached, the nitro doesn't give much
// benefit. Better wait till later. // benefit. Better wait till later.
const bool has_slowdown_attachment = const bool has_slowdown_attachment =
m_kart->getAttachment()->getType()==ATTACH_PARACHUTE || m_kart->getAttachment()->getType()==Attachment::ATTACH_PARACHUTE ||
m_kart->getAttachment()->getType()==ATTACH_ANVIL; m_kart->getAttachment()->getType()==Attachment::ATTACH_ANVIL;
if(has_slowdown_attachment) return; if(has_slowdown_attachment) return;
// If the kart is very slow (e.g. after rescue), use nitro // If the kart is very slow (e.g. after rescue), use nitro

View File

@ -336,7 +336,7 @@ void PlayerController::update(float dt)
m_controls->m_rescue=false; m_controls->m_rescue=false;
} }
if (m_kart->playingEmergencyAnimation() && if (m_kart->playingEmergencyAnimation() &&
m_kart->getAttachment()->getType() != ATTACH_TINYTUX) m_kart->getAttachment()->getType() != Attachment::ATTACH_TINYTUX)
{ {
m_bzzt_sound->play(); m_bzzt_sound->play();
} }

View File

@ -107,7 +107,7 @@ void EmergencyAnimation::forceRescue(bool is_auto_rescue)
m_up_velocity = m_kart->getKartProperties()->getRescueHeight() / m_timer; m_up_velocity = m_kart->getKartProperties()->getRescueHeight() / m_timer;
m_xyz = m_kart->getXYZ(); m_xyz = m_kart->getXYZ();
m_kart->attach(ATTACH_TINYTUX, m_timer); m_kart->attach(Attachment::ATTACH_TINYTUX, m_timer);
m_curr_rotation.setPitch(m_kart->getPitch()); m_curr_rotation.setPitch(m_kart->getPitch());
m_curr_rotation.setRoll(m_kart->getRoll() ); m_curr_rotation.setRoll(m_kart->getRoll() );
@ -137,6 +137,13 @@ void EmergencyAnimation::handleExplosion(const Vec3 &pos, bool direct_hit)
if(playingEmergencyAnimation()) return; if(playingEmergencyAnimation()) return;
if(m_kart->isInvulnerable()) if(m_kart->isInvulnerable())
return; return;
Attachment *a=m_kart->getAttachment();
if(a->getType()==Attachment::ATTACH_SWATTER &&
a->isSwatterReady())
{
a->swatItem();
return;
}
m_xyz = m_kart->getXYZ(); m_xyz = m_kart->getXYZ();
// Ignore explosion that are too far away. // Ignore explosion that are too far away.

View File

@ -97,6 +97,7 @@ Kart::Kart (const std::string& ident, Track* track, int position, bool is_first_
m_wheel_toggle = 1; m_wheel_toggle = 1;
m_finish_time = 0.0f; m_finish_time = 0.0f;
m_invulnerable_time = 0.0f; m_invulnerable_time = 0.0f;
m_squash_time = 0.0f;
m_shadow_enabled = false; m_shadow_enabled = false;
m_shadow = NULL; m_shadow = NULL;
m_terrain_particles = NULL; m_terrain_particles = NULL;
@ -482,8 +483,9 @@ void Kart::reset()
m_camera->setInitialTransform(); m_camera->setInitialTransform();
} }
// Stop any animations currently being played. // Reset squashing and animations
m_kart_model->setAnimation(KartModel::AF_DEFAULT); m_kart_model->resetWheels();
// If the controller was replaced (e.g. replaced by end controller), // If the controller was replaced (e.g. replaced by end controller),
// restore the original controller. // restore the original controller.
if(m_saved_controller) if(m_saved_controller)
@ -501,6 +503,8 @@ void Kart::reset()
m_finished_race = false; m_finished_race = false;
m_finish_time = 0.0f; m_finish_time = 0.0f;
m_invulnerable_time = 0.0f; m_invulnerable_time = 0.0f;
m_squash_time = 0.0f;
m_kart_model->scaleKart(Vec3(1.0f, 1.0f, 1.0f));
m_collected_energy = 0; m_collected_energy = 0;
m_has_started = false; m_has_started = false;
m_wheel_rotation = 0; m_wheel_rotation = 0;
@ -742,6 +746,18 @@ void Kart::update(float dt)
return; return;
} }
if(m_squash_time>0)
{
m_squash_time-=dt;
// If squasing time ends, reset the model
if(m_squash_time<=0)
{
m_kart_model->scaleKart(Vec3(1.0f, 1.0f, 1.0f));
MaxSpeed::setSlowdown(MaxSpeed::MS_DECREASE_SQUASH,
/*slowdown*/1.0f, /*fade in*/0.0f);
}
}
// Update the position and other data taken from the physics // Update the position and other data taken from the physics
Moveable::update(dt); Moveable::update(dt);
@ -937,6 +953,19 @@ void Kart::update(float dt)
} }
} // update } // update
//-----------------------------------------------------------------------------
/** Squashes this kart: it will scale the kart in up direction, and causes
* a slowdown while this kart is squashed.
* \param time How long the kart will be squashed.
* \param slowdown Reduction of max speed.
*/
void Kart::setSquash(float time, float slowdown)
{
m_kart_model->scaleKart(Vec3(1.0f, 0.5f, 1.0f));
MaxSpeed::setSlowdown(MaxSpeed::MS_DECREASE_SQUASH, slowdown, 0.1f);
m_squash_time = time;
} // setSquash
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Plays any terrain specific sound effect. /** Plays any terrain specific sound effect.
*/ */
@ -1399,7 +1428,8 @@ void Kart::updatePhysics(float dt)
float engine_power = getActualWheelForce() + handleNitro(dt) float engine_power = getActualWheelForce() + handleNitro(dt)
+ m_slipstream->getSlipstreamPower(); + m_slipstream->getSlipstreamPower();
if(m_attachment->getType()==ATTACH_PARACHUTE) engine_power*=0.2f; if(m_attachment->getType()==Attachment::ATTACH_PARACHUTE)
engine_power*=0.2f;
if (m_flying) if (m_flying)
{ {

View File

@ -120,6 +120,10 @@ private:
/** Time a kart is invulnerable. */ /** Time a kart is invulnerable. */
float m_invulnerable_time; float m_invulnerable_time;
/** How long a kart is being squashed. If this is >0
* the kart is squashed. */
float m_squash_time;
// Bullet physics parameters // Bullet physics parameters
// ------------------------- // -------------------------
btRaycastVehicle::btVehicleTuning btRaycastVehicle::btVehicleTuning
@ -225,10 +229,12 @@ public:
void adjustSpeed (float f); void adjustSpeed (float f);
void capSpeed (float max_speed); void capSpeed (float max_speed);
void updatedWeight (); void updatedWeight ();
virtual void collectedItem (Item *item, int random_attachment); void collectedItem (Item *item, int random_attachment);
virtual void reset (); void reset ();
virtual void handleZipper (const Material *m=NULL, bool play_sound=false); void handleZipper (const Material *m=NULL, bool play_sound=false);
virtual void crashed (Kart *k, const Material *m=NULL); void setSquash (float time, float slowdown);
void crashed (Kart *k, const Material *m=NULL);
virtual void update (float dt); virtual void update (float dt);
virtual void finishedRace (float time); virtual void finishedRace (float time);
@ -247,8 +253,8 @@ public:
void setKartProperties(const KartProperties *kp) { m_kart_properties=kp; } void setKartProperties(const KartProperties *kp) { m_kart_properties=kp; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets the attachment and time it stays attached. */ /** Sets the attachment and time it stays attached. */
void attach(attachmentType attachment_, float time) void attach(Attachment::AttachmentType attachment, float time)
{ m_attachment->set(attachment_, time); } { m_attachment->set(attachment, time); }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets a new powerup. */ /** Sets a new powerup. */
void setPowerup (PowerupManager::PowerupType t, int n) void setPowerup (PowerupManager::PowerupType t, int n)
@ -420,7 +426,11 @@ public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets the energy the kart has collected. */ /** Sets the energy the kart has collected. */
void setEnergy(float val) { m_collected_energy = val; } void setEnergy(float val) { m_collected_energy = val; }
}; // ------------------------------------------------------------------------
/** Returns if the kart is currently being squashed. */
bool isSquashed() const { return m_squash_time >0; }
// ------------------------------------------------------------------------
}; // Kart
#endif #endif

View File

@ -376,6 +376,18 @@ void KartModel::setDefaultPhysicsPosition(const Vec3 &center_shift,
} // setDefaultPhysicsPosition } // setDefaultPhysicsPosition
// ----------------------------------------------------------------------------
/** Resets the kart model. It removes any scaling (squashing) and stops
* animation from being played.
*/
void KartModel::reset()
{
m_animated_node->setScale(core::vector3df(1.0f, 1.0f, 1.0f));
// Stop any animations currently being played.
setAnimation(KartModel::AF_DEFAULT);
} // reset
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Enables- or disables the end animation. /** Enables- or disables the end animation.
* \param type The type of animation to play. * \param type The type of animation to play.
@ -529,3 +541,10 @@ void KartModel::resetWheels()
const float suspension[4]={0,0,0,0}; const float suspension[4]={0,0,0,0};
update(0, 0.0f, suspension); update(0, 0.0f, suspension);
} // reset } // reset
// ----------------------------------------------------------------------------
/** Scales the kart model by a certain amount. */
void KartModel::scaleKart(const Vec3 &s)
{
m_animated_node->setScale(s.toIrrVector());
} // squashKart

View File

@ -141,6 +141,7 @@ public:
KartModel(bool is_master); KartModel(bool is_master);
~KartModel(); ~KartModel();
KartModel* makeCopy(); KartModel* makeCopy();
void reset();
void loadInfo(const XMLNode &node); void loadInfo(const XMLNode &node);
bool loadModels(const KartProperties &kart_properties); bool loadModels(const KartProperties &kart_properties);
scene::ISceneNode* scene::ISceneNode*
@ -180,6 +181,7 @@ public:
void update(float rotation, float steer, const float suspension[4]); void update(float rotation, float steer, const float suspension[4]);
void resetWheels(); void resetWheels();
void setDefaultPhysicsPosition(const Vec3 &center_shift, float wheel_radius); void setDefaultPhysicsPosition(const Vec3 &center_shift, float wheel_radius);
void scaleKart(const Vec3 &s);
/** Enables- or disables the end animation. */ /** Enables- or disables the end animation. */
void setAnimation(AnimationFrameType type); void setAnimation(AnimationFrameType type);

View File

@ -88,9 +88,14 @@ KartProperties::KartProperties(const std::string &filename)
m_camera_distance = m_camera_forward_up_angle = m_camera_distance = m_camera_forward_up_angle =
m_camera_backward_up_angle = m_explosion_invulnerability_time = m_camera_backward_up_angle = m_explosion_invulnerability_time =
m_rescue_time = m_rescue_height = m_explosion_time = m_rescue_time = m_rescue_height = m_explosion_time =
m_explosion_radius = m_ai_steering_variation = UNDEFINED; m_explosion_radius = m_ai_steering_variation =
m_swatter_distance2 = m_swatter_duration = m_swatter_animation_time =
m_swatter_item_animation_time = m_squash_slowdown =
m_squash_duration = UNDEFINED;
m_gravity_center_shift = Vec3(UNDEFINED); m_gravity_center_shift = Vec3(UNDEFINED);
m_has_skidmarks = true; m_has_skidmarks = true;
m_swatter_count = -1;
m_version = 0; m_version = 0;
m_color = video::SColor(255, 0, 0, 0); m_color = video::SColor(255, 0, 0, 0);
m_shape = 32; // close enough to a circle. m_shape = 32; // close enough to a circle.
@ -435,6 +440,23 @@ void KartProperties::getAllData(const XMLNode * root)
zipper_node->get("max-speed-increase", &m_zipper_max_speed_increase); zipper_node->get("max-speed-increase", &m_zipper_max_speed_increase);
} }
if(const XMLNode *swatter_node= root->getNode("swatter"))
{
swatter_node->get("duration", &m_swatter_duration );
swatter_node->get("count", &m_swatter_count );
swatter_node->get("animation-time", &m_swatter_animation_time);
swatter_node->get("item-animation-time",
&m_swatter_item_animation_time);
swatter_node->get("squash-duration", &m_squash_duration );
swatter_node->get("squash-slowdown", &m_squash_slowdown );
if(swatter_node->get("distance", &m_swatter_distance2) )
{
// Avoid squaring if distance is not defined, so that
// distance2 remains UNDEFINED (which is a negative value)
m_swatter_distance2 *= m_swatter_distance2;
}
}
if(const XMLNode *camera_node= root->getNode("camera")) if(const XMLNode *camera_node= root->getNode("camera"))
{ {
camera_node->get("distance", &m_camera_distance); camera_node->get("distance", &m_camera_distance);
@ -594,6 +616,14 @@ void KartProperties::checkAllSet(const std::string &filename)
CHECK_NEG(m_nitro_max_speed_increase, "nitro max-speed-increase" ); CHECK_NEG(m_nitro_max_speed_increase, "nitro max-speed-increase" );
CHECK_NEG(m_nitro_duration, "nitro duration" ); CHECK_NEG(m_nitro_duration, "nitro duration" );
CHECK_NEG(m_nitro_fade_out_time, "nitro fade-out-time" ); CHECK_NEG(m_nitro_fade_out_time, "nitro fade-out-time" );
CHECK_NEG(m_swatter_distance2, "swatter distance" );
CHECK_NEG(m_swatter_animation_time, "swatter animation-time" );
CHECK_NEG(m_swatter_item_animation_time,"swatter item-animation-time" );
CHECK_NEG(m_swatter_duration, "swatter duration" );
CHECK_NEG(m_swatter_count, "swatter count" );
CHECK_NEG(m_squash_duration, "swatter squash-duration" );
CHECK_NEG(m_squash_slowdown, "swatter squash-slowdown" );
CHECK_NEG(m_rescue_height, "rescue height" ); CHECK_NEG(m_rescue_height, "rescue height" );
CHECK_NEG(m_rescue_time, "rescue time" ); CHECK_NEG(m_rescue_time, "rescue time" );
CHECK_NEG(m_rescue_vert_offset, "rescue vert-offset" ); CHECK_NEG(m_rescue_vert_offset, "rescue vert-offset" );

View File

@ -193,6 +193,21 @@ private:
/** Duration during which the increased maximum speed /** Duration during which the increased maximum speed
* due to nitro fades out. */ * due to nitro fades out. */
float m_nitro_fade_out_time; float m_nitro_fade_out_time;
/** Square of the maximum distance a swatter can operate. */
float m_swatter_distance2;
/** How often the swatter can be fired. */
int m_swatter_count;
/** How long the swatter lasts. */
float m_swatter_duration;
/** How long a kart will remain squashed. */
float m_squash_duration;
/** The slowdown to apply while a kart is squashed. The new maxspeed
* is max_speed*m_squash_slowdown. */
float m_squash_slowdown;
/** Animation time for the swatter. */
float m_swatter_animation_time;
/** How long the item swatting animation will last. */
float m_swatter_item_animation_time;
/** Engine sound effect. */ /** Engine sound effect. */
std::string m_engine_sfx_type; std::string m_engine_sfx_type;
@ -295,7 +310,9 @@ public:
~KartProperties (); ~KartProperties ();
void getAllData (const XMLNode * root); void getAllData (const XMLNode * root);
void checkAllSet(const std::string &filename); void checkAllSet(const std::string &filename);
float getStartupBoost() const;
/** Returns the maximum steering angle (depending on speed). */
float getMaxSteerAngle (float speed) const; float getMaxSteerAngle (float speed) const;
/** Returns the material for the kart icons. */ /** Returns the material for the kart icons. */
@ -492,24 +509,27 @@ public:
/** Return the fade out time once a rubber band is removed. */ /** Return the fade out time once a rubber band is removed. */
float getRubberBandFadeOutTime () const {return m_rubber_band_fade_out_time;} float getRubberBandFadeOutTime () const {return m_rubber_band_fade_out_time;}
/** Returns duration of a plunger in your face. */ /** Returns duration of a plunger in your face. */
float getPlungerInFaceTime () const float getPlungerInFaceTime () const
{return m_plunger_in_face_duration[race_manager->getDifficulty()];} {return m_plunger_in_face_duration[race_manager->getDifficulty()];}
/** Returns the time a zipper is active. */ /** Returns the time a zipper is active. */
float getZipperTime () const {return m_zipper_time; } float getZipperTime () const {return m_zipper_time; }
/** Returns the time a zipper is active. */ /** Returns the time a zipper is active. */
float getZipperFadeOutTime () const {return m_zipper_fade_out_time; } float getZipperFadeOutTime () const {return m_zipper_fade_out_time; }
/** Returns the additional force added applied to the kart. */ /** Returns the additional force added applied to the kart. */
float getZipperForce () const { return m_zipper_force; } float getZipperForce () const { return m_zipper_force; }
/** Returns the initial zipper speed gain. */ /** Returns the initial zipper speed gain. */
float getZipperSpeedGain () const { return m_zipper_speed_gain; } float getZipperSpeedGain () const { return m_zipper_speed_gain; }
/** Returns the increase of the maximum speed of the kart /** Returns the increase of the maximum speed of the kart
* if a zipper is active. */ * if a zipper is active. */
float getZipperMaxSpeedIncrease () const float getZipperMaxSpeedIncrease () const
{ return m_zipper_max_speed_increase;} { return m_zipper_max_speed_increase;}
/** Returns additional rotation of 3d model when skidding. */ /** Returns additional rotation of 3d model when skidding. */
float getSkidVisual () const {return m_skid_visual; } float getSkidVisual () const {return m_skid_visual; }
@ -585,8 +605,30 @@ public:
/** Returns the full path where the files for this kart are stored. */ /** Returns the full path where the files for this kart are stored. */
const std::string& getKartDir () const {return m_root; } const std::string& getKartDir () const {return m_root; }
float getStartupBoost() const;
}; /** Returns the square of the maximum distance at which a swatter
* can hit karts. */
float getSwatterDistance2() const { return m_swatter_distance2; }
/** Returns how often a swatter can be used. */
int getSwatterCount() const { return m_swatter_count; }
/** Returns how long a swatter will stay attached/ready to be used. */
float getSwatterDuration() const { return m_swatter_duration; }
/** Returns how long a kart remains squashed. */
float getSquashDuration() const {return m_squash_duration; }
/** Returns the slowdown of a kart that is squashed. */
float getSquashSlowdown() const {return m_squash_slowdown; }
/** Returns how long it takes for the swatter to hit a target. */
float getSwatterAnimationTime() const { return m_swatter_animation_time; }
/** Returns how long it takes for the swatter to swat at an item. */
float getSwatterItemAnimationTime() const
{return m_swatter_item_animation_time; }
}; // KartProperties
#endif #endif

View File

@ -41,6 +41,7 @@ public:
enum {MS_DECREASE_MIN, enum {MS_DECREASE_MIN,
MS_DECREASE_TERRAIN = MS_DECREASE_MIN, MS_DECREASE_TERRAIN = MS_DECREASE_MIN,
MS_DECREASE_AI, MS_DECREASE_AI,
MS_DECREASE_SQUASH,
MS_DECREASE_MAX}; MS_DECREASE_MAX};
private: private:

View File

@ -974,6 +974,12 @@ int main(int argc, char *argv[] )
UserConfigParams::m_internet_status = NetworkHttp::IPERM_ALLOWED; UserConfigParams::m_internet_status = NetworkHttp::IPERM_ALLOWED;
GUIEngine::ModalDialog::dismiss(); GUIEngine::ModalDialog::dismiss();
network_http = new NetworkHttp(); network_http = new NetworkHttp();
// Note that the network thread must be started after
// the assignment to network_http (since the thread
// might use network_http, otherwise a race condition
// can be introduced resulting in a crash).
network_http->startNetworkThread();
} // onConfirm } // onConfirm
// -------------------------------------------------------- // --------------------------------------------------------
virtual void onCancel() virtual void onCancel()
@ -982,6 +988,7 @@ int main(int argc, char *argv[] )
UserConfigParams::m_internet_status = NetworkHttp::IPERM_NOT_ALLOWED; UserConfigParams::m_internet_status = NetworkHttp::IPERM_NOT_ALLOWED;
GUIEngine::ModalDialog::dismiss(); GUIEngine::ModalDialog::dismiss();
network_http = new NetworkHttp(); network_http = new NetworkHttp();
network_http->startNetworkThread();
} // onCancel } // onCancel
}; // ConfirmServer }; // ConfirmServer

View File

@ -180,10 +180,10 @@ void Physics::KartKartCollision(Kart *kartA, Kart *kartB)
Attachment *attachmentA=kartA->getAttachment(); Attachment *attachmentA=kartA->getAttachment();
Attachment *attachmentB=kartB->getAttachment(); Attachment *attachmentB=kartB->getAttachment();
if(attachmentA->getType()==ATTACH_BOMB) if(attachmentA->getType()==Attachment::ATTACH_BOMB)
{ {
// If both karts have a bomb, explode them immediately: // If both karts have a bomb, explode them immediately:
if(attachmentB->getType()==ATTACH_BOMB) if(attachmentB->getType()==Attachment::ATTACH_BOMB)
{ {
attachmentA->setTimeLeft(0.0f); attachmentA->setTimeLeft(0.0f);
attachmentB->setTimeLeft(0.0f); attachmentB->setTimeLeft(0.0f);
@ -198,7 +198,7 @@ void Physics::KartKartCollision(Kart *kartA, Kart *kartB)
} }
} }
} }
else if(attachmentB->getType()==ATTACH_BOMB && else if(attachmentB->getType()==Attachment::ATTACH_BOMB &&
attachmentB->getPreviousOwner()!=kartA) attachmentB->getPreviousOwner()!=kartA)
{ {
attachmentB->moveBombFromTo(kartB, kartA); attachmentB->moveBombFromTo(kartB, kartA);

View File

@ -556,7 +556,7 @@ void RaceGUI::drawGlobalPlayerIcons(const KartIconDisplayInfo* info)
} }
} }
//attachment //attachment
if (kart->getAttachment()->getType() != ATTACH_NOTHING) if (kart->getAttachment()->getType() != Attachment::ATTACH_NOTHING)
{ {
video::ITexture *icon_attachment = video::ITexture *icon_attachment =
attachment_manager->getIcon(kart->getAttachment()->getType()) attachment_manager->getIcon(kart->getAttachment()->getType())

View File

@ -29,8 +29,8 @@
Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, Quad::Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
bool invisible, bool ai_ignore) bool invisible, bool ai_ignore)
{ {
if(sideOfLine2D(p0, p2, p1)>0 || if(p1.sideOfLine2D(p0, p2)>0 ||
sideOfLine2D(p0, p2, p3)<0) p3.sideOfLine2D(p0, p2)<0)
{ {
printf("Warning: quad has wrong orientation: p0=%f %f %f p1=%f %f %f\n", printf("Warning: quad has wrong orientation: p0=%f %f %f p1=%f %f %f\n",
p0.getX(), p0.getY(), p0.getZ(),p1.getX(), p1.getY(), p1.getZ()); p0.getX(), p0.getY(), p0.getZ(),p1.getX(), p1.getY(), p1.getZ());
@ -85,16 +85,6 @@ void Quad::getVertices(video::S3DVertex *v, const video::SColor &color) const
v[3].Color = color; v[3].Color = color;
} // setVertices } // setVertices
// -----------------------------------------------------------------------------
/** Returns wether a point is to the left or to the right of a line.
* While all arguments are 3d, only the x and y coordinates are actually used.
*/
float Quad::sideOfLine2D(const Vec3& l1, const Vec3& l2, const Vec3& p) const
{
return (l2.getX()-l1.getX())*(p.getZ()-l1.getZ()) -
(l2.getZ()-l1.getZ())*(p.getX()-l1.getX());
} // sideOfLine
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool Quad::pointInQuad(const Vec3& p) const bool Quad::pointInQuad(const Vec3& p) const
{ {
@ -113,12 +103,12 @@ bool Quad::pointInQuad(const Vec3& p) const
// If a point is exactly on the line of two quads (e.g. between points // If a point is exactly on the line of two quads (e.g. between points
// 0,1 on one quad, and 3,2 of the previous quad), assign this point // 0,1 on one quad, and 3,2 of the previous quad), assign this point
// to be on the 'later' quad, i.e. on the line between points 0 and 1. // to be on the 'later' quad, i.e. on the line between points 0 and 1.
if(sideOfLine2D(m_p[0], m_p[2], p)<0) { if(p.sideOfLine2D(m_p[0], m_p[2])<0) {
return sideOfLine2D(m_p[0], m_p[1], p) >= 0.0 && return p.sideOfLine2D(m_p[0], m_p[1]) >= 0.0 &&
sideOfLine2D(m_p[1], m_p[2], p) >= 0.0; p.sideOfLine2D(m_p[1], m_p[2]) >= 0.0;
} else { } else {
return sideOfLine2D(m_p[2], m_p[3], p) > 0.0 && return p.sideOfLine2D(m_p[2], m_p[3]) > 0.0 &&
sideOfLine2D(m_p[3], m_p[0], p) >= 0.0; p.sideOfLine2D(m_p[3], m_p[0]) >= 0.0;
} }
} // pointInQuad } // pointInQuad

View File

@ -54,7 +54,6 @@ private:
/** Set if this quad should not be used by the AI. */ /** Set if this quad should not be used by the AI. */
bool m_ai_ignore; bool m_ai_ignore;
float sideOfLine2D(const Vec3& l1, const Vec3& l2, const Vec3& p) const;
public: public:
Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3, Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
bool invis=false, bool ai_ignore=false); bool invis=false, bool ai_ignore=false);

View File

@ -123,6 +123,19 @@ public:
void min(const Vec3& a) {if(a.getX()<m_floats[0]) m_floats[0]=a.getX(); void min(const Vec3& a) {if(a.getX()<m_floats[0]) m_floats[0]=a.getX();
if(a.getY()<m_floats[1]) m_floats[1]=a.getY(); if(a.getY()<m_floats[1]) m_floats[1]=a.getY();
if(a.getZ()<m_floats[2]) m_floats[2]=a.getZ();} if(a.getZ()<m_floats[2]) m_floats[2]=a.getZ();}
// ------------------------------------------------------------------------
/** Determines which side of a line this point is. This is using
* a 2d projection (into the X-Z plane).
* \param start The start point of the line.
* \param end The end point of the line.
* \return >0 Point is to the left side; <0 if it's on the right.
*/
float sideOfLine2D(const Vec3& start, const Vec3& end) const
{
return (end.getX()-start.getX())*(m_floats[2]-start.getZ()) -
(end.getZ()-start.getZ())*(m_floats[0]-start.getX());
} // sideOfLine2D
}; // Vec3 }; // Vec3