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"
model="anchor.b3d" />
<item name="switch" icon="swap-icon.png" />
<item name="swatter" icon="swatter-icon.png" />
<item name="parachute" icon="parachute-icon.png"
model="parachute.b3d" />
<item name="plunger" icon="plunger-icon.png"
@ -36,19 +37,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 para anvil -->
<first w="25 5 15 5 10 10 0 0"
w-multi=" 0 0 5 0 0 0 0 0" />
<top33 w="30 30 30 30 30 10 30 0"
w-multi=" 0 10 10 0 10 0 0 0" />
<mid33 w="30 30 30 30 30 10 30 0"
w-multi=" 0 20 20 20 20 0 0 0" />
<end33 w=" 0 30 30 30 30 10 30 30"
w-multi=" 0 30 30 30 30 0 0 0" />
<last w=" 0 30 30 60 60 0 60 60"
w-multi=" 0 30 30 60 60 0 0 0" />
<battle w=" 0 30 60 0 0 10 0 0"
w-multi=" 0 0 5 0 0 0 0 0" />
<!-- 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 30 0"
w-multi=" 0 10 10 0 10 0 10 0 0" />
<mid33 w="30 30 30 30 30 10 30 30 0"
w-multi=" 0 20 20 20 20 0 0 20 0" />
<end33 w=" 0 30 30 30 30 10 30 30 30"
w-multi=" 0 30 30 30 30 0 30 0 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 0 30 0"
w-multi=" 0 0 5 0 0 0 0 5 0" />
</powerup>

View File

@ -145,13 +145,13 @@
<nitro power-boost="3" consumption="1" small-container="1" big-container="3"
max-speed-increase="5" duration="1" fade-out-time="2"/>
<!-- time is the time a zipper is active. force is the additional
zipper force. speed-gain is the one time additional speed.
max-speed-increase is the additional speed allowed on top
of the kart-specific maximum kart speed. Fade-out time determines
how long it takes for a zipper to fade out (after 'time'). -->
<zipper time="3.5" force="250.0" speed-gain="4.5" max-speed-increase="15"
fade-out-time="1.0" />
<!-- time is the time a zipper is active. force is the additional
zipper force. speed-gain is the one time additional speed.
max-speed-increase is the additional speed allowed on top
of the kart-specific maximum kart speed. Fade-out time determines
how long it takes for a zipper to fade out (after 'time'). -->
<zipper time="3.5" force="250.0" speed-gain="4.5" max-speed-increase="15"
fade-out-time="1.0" />
<!-- Skidding: increase: multiplicative increase of skidding factor in each frame.
decrease: multiplicative decrease of skidding factor in each frame.
@ -176,6 +176,20 @@
add-power="3" min-speed="10"
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
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

View File

@ -115,7 +115,8 @@ void InputManager::handleStaticAction(int key, int value)
break;
}
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->setPowerup(PowerupManager::POWERUP_BUBBLEGUM, 10000);
@ -127,42 +128,48 @@ void InputManager::handleStaticAction(int key, int value)
}
break;
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->setPowerup(PowerupManager::POWERUP_PLUNGER, 10000);
}
break;
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->setPowerup(PowerupManager::POWERUP_CAKE, 10000);
}
break;
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->setPowerup(PowerupManager::POWERUP_SWITCH, 10000);
}
break;
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->setPowerup(PowerupManager::POWERUP_BOWLING, 10000);
}
break;
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->setPowerup(PowerupManager::POWERUP_PARACHUTE, 10000);
}
break;
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->setPowerup(PowerupManager::POWERUP_ZIPPER, 10000);
@ -180,7 +187,8 @@ void InputManager::handleStaticAction(int key, int value)
const int count = World::getWorld()->getNumKarts();
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
@ -191,8 +199,17 @@ void InputManager::handleStaticAction(int key, int value)
}
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:
if (UserConfigParams::m_artist_debug_mode && value && control_is_pressed)
if (UserConfigParams::m_artist_debug_mode && value &&
control_is_pressed)
{
if (world != NULL)
{
@ -203,17 +220,9 @@ void InputManager::handleStaticAction(int key, int value)
case KEY_F12:
if(value)
UserConfigParams::m_display_fps = !UserConfigParams::m_display_fps;
UserConfigParams::m_display_fps =
!UserConfigParams::m_display_fps;
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:
if(world) history->Save();
break;
@ -227,17 +236,20 @@ void InputManager::handleStaticAction(int key, int value)
/**
* Handles input when an input sensing mode (when configuring input)
*/
void InputManager::inputSensing(Input::InputType type, int deviceID, int button,
Input::AxisDirection axisDirection, int value)
void InputManager::inputSensing(Input::InputType type, int deviceID,
int button, Input::AxisDirection axisDirection,
int value)
{
#if INPUT_MODE_DEBUG
std::cout << "INPUT SENSING... ";
#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)
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;
#if INPUT_MODE_DEBUG
@ -282,7 +294,8 @@ void InputManager::inputSensing(Input::InputType type, int deviceID, int button,
case Input::IT_STICKMOTION:
{
std::cout << "%% storing new axis binding, value=" << value <<
" deviceID=" << deviceID << " button=" << button << " axisDirection=" <<
" deviceID=" << deviceID << " button=" << button <<
" axisDirection=" <<
(axisDirection == Input::AD_NEGATIVE ? "-" : "+") << "\n";
// 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
@ -343,8 +356,10 @@ void InputManager::inputSensing(Input::InputType type, int deviceID, int button,
//-----------------------------------------------------------------------------
int InputManager::getPlayerKeyboardID() const
{
// In no-assign mode, just return the GUI player ID (devices not assigned yet)
if (m_device_manager->getAssignMode() == NO_ASSIGN) return PLAYER_ID_GAME_MASTER;
// In no-assign mode, just return the GUI player ID (devices not
// 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
// 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.
*
*/
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)
{
// Act different in input sensing mode.
@ -386,21 +402,28 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
StateManager::ActivePlayer* player = NULL;
PlayerAction action;
bool action_found = m_device_manager->translateInput( type, deviceID, button, axisDirection, value,
m_mode, &player, &action);
bool action_found = m_device_manager->translateInput(type, deviceID,
button, axisDirection,
value, m_mode,
&player, &action);
// if didn't find a _menu_ action, try finding a corresponding game action as fallback
// (the GUI can handle them too)
// if didn't find a _menu_ action, try finding a corresponding game action
// as fallback (the GUI can handle them too)
if (!action_found && m_mode == MENU)
{
action_found = m_device_manager->translateInput(type, deviceID, button, axisDirection, value,
INGAME, &player, &action);
action_found = m_device_manager->translateInput(type, deviceID,
button, axisDirection,
value, INGAME, &player,
&action);
}
// in menus, some keyboard keys are standard (before each player selected his device)
// So if a key could not be mapped to any known binding, 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)
// in menus, some keyboard keys are standard (before each player selected
// his device). So if a key could not be mapped to any known binding,
// 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)
{
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)
{
// 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
if (KartSelectionScreen::getInstance()->playerQuit( player ))
@ -457,25 +481,30 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
InputDevice *device = NULL;
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);
}
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);
}
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
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
Kart* pk;
@ -490,7 +519,8 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
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;
}
@ -501,29 +531,31 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
{
// 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 = 0;
}
// When in master-only mode, we can safely assume that players are set up, contrarly to
// early menus where we accept every input because players are not set-up yet
// When in master-only mode, we can safely assume that players
// 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 (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 &&
abs(value)>gp->m_deadzone)
{
//I18N: message shown when an input device is used but 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()));
//I18N: message shown when an input device is used but
// 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()) );
}
}
return;
@ -538,27 +570,36 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
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());
// If only the master player can act, and this player is not the master, ignore his input
if (m_device_manager->getAssignMode() == ASSIGN && m_master_player_only &&
// If only the master player can act, and this player is not
// the master, ignore his input
if (m_device_manager->getAssignMode() == ASSIGN &&
m_master_player_only &&
playerID != PLAYER_ID_GAME_MASTER)
{
//I18N: message shown when a player that isn't game master tries to modify options that
//I18N: only the game master is allowed to
GUIEngine::showMessage(_("Only the Game Master may act at this point!"));
//I18N: message shown when a player that isn't game master
//I18N: tries to modify options that only the game master
//I18N: is allowed to
GUIEngine::showMessage(
_("Only the Game Master may act at this point!"));
return;
}
// all is good, pass the translated input event on to the event handler
GUIEngine::EventHandler::get()->processGUIAction(action, deviceID, abs(value), type, playerID);
// all is good, pass the translated input event on to the
// event handler
GUIEngine::EventHandler::get()
->processGUIAction(action, deviceID, abs(value), type,
playerID);
}
}
}
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 );
}
} // input
@ -568,14 +609,16 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, int button
void InputManager::setMasterPlayerOnly(bool enabled)
{
#if INPUT_MODE_DEBUG
std::cout << "====== InputManager::setMasterPlayerOnly(" << enabled << ") ======\n";
std::cout <<
"====== InputManager::setMasterPlayerOnly(" << enabled << ") ======\n";
#endif
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
{
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)
{
// Axes - FIXME, instead of checking all of them, ask the bindings which ones to poll
for (int axis_id=0; axis_id<SEvent::SJoystickEvent::NUMBER_OF_AXES ; axis_id++)
// Axes - FIXME, instead of checking all of them, ask the bindings
// 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];
@ -609,27 +654,32 @@ EventPropagation InputManager::input(const SEvent& event)
event.JoystickEvent.Joystick, axis_id, value);
}
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, axis_id,
Input::AD_NEUTRAL, value);
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
axis_id, Input::AD_NEUTRAL, value);
}
if (event.JoystickEvent.POV == 65535)
{
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_H_ID, Input::AD_NEUTRAL,
0);
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_V_ID, Input::AD_NEUTRAL,
0);
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
Input::HAT_H_ID, Input::AD_NEUTRAL, 0);
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
Input::HAT_V_ID, Input::AD_NEUTRAL, 0);
}
else
{
// *0.017453925f is to convert degrees to radians
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick, Input::HAT_H_ID, Input::AD_NEUTRAL,
(int)(cos(event.JoystickEvent.POV*0.017453925f/100.0f)*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));
dispatchInput(Input::IT_STICKMOTION, event.JoystickEvent.Joystick,
Input::HAT_H_ID, Input::AD_NEUTRAL,
(int)(cos(event.JoystickEvent.POV*0.017453925f/100.0f)
*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)
{
@ -642,14 +692,17 @@ EventPropagation InputManager::input(const SEvent& event)
const bool isButtonPressed = event.JoystickEvent.IsButtonPressed(i);
// 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)
{
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);
}
gp->setButtonPressed(i, isButtonPressed);
@ -676,9 +729,10 @@ EventPropagation InputManager::input(const SEvent& event)
StateManager::get()->escapePressed();
return EVENT_BLOCK;
}
// 'backspace' in a text control must never be mapped, since user can be in a text
// area trying to erase text (and if it's mapped to rescue that would dismiss the
// dialog instead of erasing a single letter). Same for spacebar. Same for letters.
// 'backspace' in a text control must never be mapped, since user
// can be in a text area trying to erase text (and if it's mapped
// to rescue that would dismiss the dialog instead of erasing a
// single letter). Same for spacebar. Same for letters.
if (GUIEngine::isWithinATextBox())
{
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,
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())
{
return EVENT_BLOCK;
@ -705,9 +760,10 @@ EventPropagation InputManager::input(const SEvent& event)
}
else
{
// 'backspace' in a text control must never be mapped, since user can be in a text
// area trying to erase text (and if it's mapped to rescue that would dismiss the
// dialog instead of erasing a single letter). Same for spacebar. Same for letters.
// 'backspace' in a text control must never be mapped, since user
// can be in a text area trying to erase text (and if it's mapped
// to rescue that would dismiss the dialog instead of erasing a
// single letter). Same for spacebar. Same for letters.
if (GUIEngine::isWithinATextBox())
{
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_MMOUSE_LEFT_UP Middle mouse button was left up.
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
// block events in all modes but initial menus (except in text boxes to 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
if (getDeviceList()->getAssignMode() != NO_ASSIGN && !GUIEngine::isWithinATextBox() &&
(!GUIEngine::ModalDialog::isADialogActive() && StateManager::get()->getGameState() == GUIEngine::GAME))
// block events in all modes but initial menus (except in text boxes to
// 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
if (getDeviceList()->getAssignMode() != NO_ASSIGN &&
!GUIEngine::isWithinATextBox() &&
(!GUIEngine::ModalDialog::isADialogActive() &&
StateManager::get()->getGameState() == GUIEngine::GAME))
{
return EVENT_BLOCK;
}
@ -837,9 +898,9 @@ void InputManager::setMode(InputDriverMode new_mode)
m_sensed_input_high_gamepad.clear();
m_sensed_input_high_kbd.clear();
// The order is deliberate just in case someone starts to make
// STK multithreaded: m_sensed_input must not be 0 when
// mode == INPUT_SENSE_PREFER_{AXIS,BUTTON}.
// The order is deliberate just in case someone starts
// to make STK multithreaded: m_sensed_input must not be
// 0 when mode == INPUT_SENSE_PREFER_{AXIS,BUTTON}.
m_mode = MENU;
break;
@ -886,8 +947,8 @@ void InputManager::setMode(InputDriverMode new_mode)
// We must be in menu mode now in order to switch.
assert (m_mode == MENU);
// Reset the helper values for the relative mouse movement supresses to
// the notification of them as an input.
// Reset the helper values for the relative mouse movement
// supresses to the notification of them as an input.
m_mouse_val_x = m_mouse_val_y = 0;
m_mode = new_mode;

View File

@ -31,6 +31,8 @@
#include "network/network_manager.hpp"
#include "utils/constants.hpp"
#define SWAT_ANGLE 22.0f
Attachment::Attachment(Kart* kart)
{
m_type = ATTACH_NOTHING;
@ -40,8 +42,8 @@ Attachment::Attachment(Kart* kart)
// 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.
m_node = irr_driver->addAnimatedMesh(
attachment_manager->getMesh(ATTACH_BOMB));
m_node = irr_driver->addAnimatedMesh(
attachment_manager->getMesh(Attachment::ATTACH_BOMB));
#ifdef DEBUG
std::string debug_name = kart->getIdent()+" (attachment)";
m_node->setName(debug_name.c_str());
@ -58,11 +60,10 @@ Attachment::~Attachment()
} // ~Attachment
//-----------------------------------------------------------------------------
void Attachment::set(attachmentType type, float time, Kart *current_kart)
void Attachment::set(AttachmentType type, float time, Kart *current_kart)
{
clear();
m_node->setMesh(attachment_manager->getMesh(type));
if (!UserConfigParams::m_graphical_effects)
@ -71,9 +72,18 @@ void Attachment::set(attachmentType type, float time, Kart *current_kart)
m_node->setCurrentFrame(0);
}
m_type = type;
m_time_left = time;
m_previous_owner = current_kart;
m_type = type;
m_time_left = time;
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
// case we have to save the current kart speed so that it can be detached
// by slowing down.
@ -98,6 +108,7 @@ void Attachment::set(attachmentType type, float time, Kart *current_kart)
void Attachment::clear()
{
m_type=ATTACH_NOTHING;
m_time_left=0.0;
m_node->setVisible(false);
@ -108,6 +119,12 @@ void Attachment::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)
{
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_MAX:
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:
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)
clear();
} // 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 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
*/
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:
attachmentType m_type; //!< attachment type
Kart *m_kart; //!< kart the attachment is attached to
float m_time_left; //!< time left till attachment expires
float m_initial_speed; //!< for parachutes only
/** Attachment type. */
AttachmentType m_type;
/** 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. */
scene::IAnimatedMeshSceneNode
*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;
void aimSwatter();
bool isLeftSideOfKart(const Vec3 &xyz);
void checkForHitKart(bool isSwattingToLeft);
public:
Attachment(Kart* _kart);
~Attachment();
void set (attachmentType _type, float time, Kart *previous_kart=NULL);
void set (attachmentType _type) { set(_type, m_time_left); }
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
*/
Attachment(Kart* kart);
~Attachment();
void clear ();
void hitBanana(Item *item, int new_attachment=-1);
void update (float dt);
void updateSwatter(float dt);
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

View File

@ -25,7 +25,9 @@
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:
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[]=
{
{ATTACH_PARACHUTE, "parachute.b3d", "parachute-attach-icon.png"},
{ATTACH_BOMB, "bomb.b3d", "bomb-attach-icon.png" },
{ATTACH_ANVIL, "anchor.b3d", "anchor-attach-icon.png" },
{ATTACH_TINYTUX, "reset-button.b3d","reset-attach-icon.png" },
{ATTACH_MAX, "", "" },
{Attachment::ATTACH_PARACHUTE, "parachute.b3d", "parachute-attach-icon.png"},
{Attachment::ATTACH_BOMB, "bomb.b3d", "bomb-attach-icon.png" },
{Attachment::ATTACH_ANVIL, "anchor.b3d", "anchor-attach-icon.png" },
{Attachment::ATTACH_SWATTER, "swatter.b3d", "swatter-icon.png" },
{Attachment::ATTACH_TINYTUX, "reset-button.b3d","reset-attach-icon.png" },
{Attachment::ATTACH_MAX, "", "" },
};
//-----------------------------------------------------------------------------
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];
mesh->drop();
@ -73,7 +76,7 @@ AttachmentManager::~AttachmentManager()
//-----------------------------------------------------------------------------
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
} // for
@ -82,7 +85,7 @@ void AttachmentManager::removeTextures()
//-----------------------------------------------------------------------------
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);
m_attachments[iat[i].attachment]=irr_driver->getAnimatedMesh(full_path);

View File

@ -33,20 +33,23 @@ namespace irr
class AttachmentManager: public NoCopy
{
private:
scene::IAnimatedMesh *m_attachments[ATTACH_MAX];
Material *m_all_icons [ATTACH_MAX];
scene::IAnimatedMesh *m_attachments[Attachment::ATTACH_MAX];
Material *m_all_icons [Attachment::ATTACH_MAX];
public:
AttachmentManager() {};
~AttachmentManager();
void removeTextures ();
void loadModels ();
// ------------------------------------------------------------------------
/** Returns the mest for a certain attachment.
* \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
* has an attachment. */
const Material*
getIcon (int type) const {return m_all_icons [type];}
const Material* getIcon (int type) const {return m_all_icons [type]; }
// ------------------------------------------------------------------------
};
extern AttachmentManager *attachment_manager;

View File

@ -142,6 +142,10 @@ void Powerup::set(PowerupManager::PowerupType type, int n)
switch (m_type)
{
// No sound effect when arming the glove
case PowerupManager::POWERUP_SWATTER:
break;
case PowerupManager::POWERUP_ZIPPER:
break ;
@ -193,6 +197,7 @@ void Powerup::use()
{
// Play custom kart sound when collectible is used
if (m_type != PowerupManager::POWERUP_NOTHING &&
m_type != PowerupManager::POWERUP_SWATTER &&
m_type != PowerupManager::POWERUP_ZIPPER)
m_owner->playCustomSFX(SFXManager::CUSTOM_SHOOT);
@ -247,7 +252,11 @@ void Powerup::use()
m_sound_use->play();
projectile_manager->newProjectile(m_owner, m_type);
break ;
case PowerupManager::POWERUP_SWATTER:
m_owner->getAttachment()->set(Attachment::ATTACH_SWATTER,
m_owner->getKartProperties()->getSwatterDuration());
break;
case PowerupManager::POWERUP_BUBBLEGUM:
{
Vec3 hit_point;
@ -302,7 +311,8 @@ void Powerup::use()
if(kart == m_owner) continue;
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->adjustSpeed(stk_config->m_anvil_speed_factor*0.5f);
@ -338,7 +348,8 @@ void Powerup::use()
if(kart->isEliminated() || kart== m_owner) continue;
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())
player_kart = kart;

View File

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

View File

@ -79,7 +79,7 @@ public:
POWERUP_BUBBLEGUM = POWERUP_FIRST,
POWERUP_CAKE,
POWERUP_BOWLING, POWERUP_ZIPPER, POWERUP_PLUNGER,
POWERUP_SWITCH,
POWERUP_SWITCH, POWERUP_SWATTER,
POWERUP_PARACHUTE,
POWERUP_ANVIL, //powerup.cpp assumes these two come last
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
// of us.
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 )
{
// 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
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:
printf("Invalid or unhandled powerup '%d' in default AI.\n",
m_kart->getPowerup()->getType());
@ -728,8 +746,8 @@ void DefaultAIController::handleNitroAndZipper()
// If a parachute or anvil is attached, the nitro doesn't give much
// benefit. Better wait till later.
const bool has_slowdown_attachment =
m_kart->getAttachment()->getType()==ATTACH_PARACHUTE ||
m_kart->getAttachment()->getType()==ATTACH_ANVIL;
m_kart->getAttachment()->getType()==Attachment::ATTACH_PARACHUTE ||
m_kart->getAttachment()->getType()==Attachment::ATTACH_ANVIL;
if(has_slowdown_attachment) return;
// 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
// of us.
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 )
{
// 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_kart->getPosition()>1;
}
case PowerupManager::POWERUP_SWATTER: // fallthrough
default:
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
// benefit. Better wait till later.
const bool has_slowdown_attachment =
m_kart->getAttachment()->getType()==ATTACH_PARACHUTE ||
m_kart->getAttachment()->getType()==ATTACH_ANVIL;
m_kart->getAttachment()->getType()==Attachment::ATTACH_PARACHUTE ||
m_kart->getAttachment()->getType()==Attachment::ATTACH_ANVIL;
if(has_slowdown_attachment) return;
// 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;
}
if (m_kart->playingEmergencyAnimation() &&
m_kart->getAttachment()->getType() != ATTACH_TINYTUX)
m_kart->getAttachment()->getType() != Attachment::ATTACH_TINYTUX)
{
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_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.setRoll(m_kart->getRoll() );
@ -137,6 +137,13 @@ void EmergencyAnimation::handleExplosion(const Vec3 &pos, bool direct_hit)
if(playingEmergencyAnimation()) return;
if(m_kart->isInvulnerable())
return;
Attachment *a=m_kart->getAttachment();
if(a->getType()==Attachment::ATTACH_SWATTER &&
a->isSwatterReady())
{
a->swatItem();
return;
}
m_xyz = m_kart->getXYZ();
// 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_finish_time = 0.0f;
m_invulnerable_time = 0.0f;
m_squash_time = 0.0f;
m_shadow_enabled = false;
m_shadow = NULL;
m_terrain_particles = NULL;
@ -481,11 +482,12 @@ void Kart::reset()
m_camera->reset();
m_camera->setInitialTransform();
}
// Stop any animations currently being played.
m_kart_model->setAnimation(KartModel::AF_DEFAULT);
// Reset squashing and animations
m_kart_model->resetWheels();
// If the controller was replaced (e.g. replaced by end controller),
// restore the original controller.
// restore the original controller.
if(m_saved_controller)
{
m_controller = m_saved_controller;
@ -501,6 +503,8 @@ void Kart::reset()
m_finished_race = false;
m_finish_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_has_started = false;
m_wheel_rotation = 0;
@ -742,6 +746,18 @@ void Kart::update(float dt)
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
Moveable::update(dt);
@ -937,6 +953,19 @@ void Kart::update(float dt)
}
} // 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.
*/
@ -1399,7 +1428,8 @@ void Kart::updatePhysics(float dt)
float engine_power = getActualWheelForce() + handleNitro(dt)
+ 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)
{

View File

@ -120,6 +120,10 @@ private:
/** Time a kart is invulnerable. */
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
// -------------------------
btRaycastVehicle::btVehicleTuning
@ -225,10 +229,12 @@ public:
void adjustSpeed (float f);
void capSpeed (float max_speed);
void updatedWeight ();
virtual void collectedItem (Item *item, int random_attachment);
virtual void reset ();
virtual void handleZipper (const Material *m=NULL, bool play_sound=false);
virtual void crashed (Kart *k, const Material *m=NULL);
void collectedItem (Item *item, int random_attachment);
void reset ();
void handleZipper (const Material *m=NULL, bool play_sound=false);
void setSquash (float time, float slowdown);
void crashed (Kart *k, const Material *m=NULL);
virtual void update (float dt);
virtual void finishedRace (float time);
@ -247,8 +253,8 @@ public:
void setKartProperties(const KartProperties *kp) { m_kart_properties=kp; }
// ------------------------------------------------------------------------
/** Sets the attachment and time it stays attached. */
void attach(attachmentType attachment_, float time)
{ m_attachment->set(attachment_, time); }
void attach(Attachment::AttachmentType attachment, float time)
{ m_attachment->set(attachment, time); }
// ------------------------------------------------------------------------
/** Sets a new powerup. */
void setPowerup (PowerupManager::PowerupType t, int n)
@ -420,7 +426,11 @@ public:
// ------------------------------------------------------------------------
/** Sets the energy the kart has collected. */
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

View File

@ -376,6 +376,18 @@ void KartModel::setDefaultPhysicsPosition(const Vec3 &center_shift,
} // 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.
* \param type The type of animation to play.
@ -529,3 +541,10 @@ void KartModel::resetWheels()
const float suspension[4]={0,0,0,0};
update(0, 0.0f, suspension);
} // 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();
KartModel* makeCopy();
void reset();
void loadInfo(const XMLNode &node);
bool loadModels(const KartProperties &kart_properties);
scene::ISceneNode*
@ -180,6 +181,7 @@ public:
void update(float rotation, float steer, const float suspension[4]);
void resetWheels();
void setDefaultPhysicsPosition(const Vec3 &center_shift, float wheel_radius);
void scaleKart(const Vec3 &s);
/** Enables- or disables the end animation. */
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_backward_up_angle = m_explosion_invulnerability_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_has_skidmarks = true;
m_swatter_count = -1;
m_version = 0;
m_color = video::SColor(255, 0, 0, 0);
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);
}
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"))
{
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_duration, "nitro duration" );
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_time, "rescue time" );
CHECK_NEG(m_rescue_vert_offset, "rescue vert-offset" );

View File

@ -193,6 +193,21 @@ private:
/** Duration during which the increased maximum speed
* due to nitro fades out. */
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. */
std::string m_engine_sfx_type;
@ -295,7 +310,9 @@ public:
~KartProperties ();
void getAllData (const XMLNode * root);
void checkAllSet(const std::string &filename);
float getStartupBoost() const;
/** Returns the maximum steering angle (depending on speed). */
float getMaxSteerAngle (float speed) const;
/** Returns the material for the kart icons. */
@ -492,24 +509,27 @@ public:
/** Return the fade out time once a rubber band is removed. */
float getRubberBandFadeOutTime () const {return m_rubber_band_fade_out_time;}
/** Returns duration of a plunger in your face. */
float getPlungerInFaceTime () const
{return m_plunger_in_face_duration[race_manager->getDifficulty()];}
/** Returns the time a zipper is active. */
float getZipperTime () const {return m_zipper_time; }
/** Returns the time a zipper is active. */
float getZipperFadeOutTime () const {return m_zipper_fade_out_time; }
/** Returns the additional force added applied to the kart. */
float getZipperForce () const { return m_zipper_force; }
/** Returns the initial zipper speed gain. */
float getZipperSpeedGain () const { return m_zipper_speed_gain; }
/** Returns the increase of the maximum speed of the kart
* if a zipper is active. */
float getZipperMaxSpeedIncrease () const
{ return m_zipper_max_speed_increase;}
/** Returns additional rotation of 3d model when skidding. */
float getSkidVisual () const {return m_skid_visual; }
@ -585,8 +605,30 @@ public:
/** Returns the full path where the files for this kart are stored. */
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

View File

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

View File

@ -974,6 +974,12 @@ int main(int argc, char *argv[] )
UserConfigParams::m_internet_status = NetworkHttp::IPERM_ALLOWED;
GUIEngine::ModalDialog::dismiss();
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
// --------------------------------------------------------
virtual void onCancel()
@ -982,6 +988,7 @@ int main(int argc, char *argv[] )
UserConfigParams::m_internet_status = NetworkHttp::IPERM_NOT_ALLOWED;
GUIEngine::ModalDialog::dismiss();
network_http = new NetworkHttp();
network_http->startNetworkThread();
} // onCancel
}; // ConfirmServer

View File

@ -180,10 +180,10 @@ void Physics::KartKartCollision(Kart *kartA, Kart *kartB)
Attachment *attachmentA=kartA->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(attachmentB->getType()==ATTACH_BOMB)
if(attachmentB->getType()==Attachment::ATTACH_BOMB)
{
attachmentA->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->moveBombFromTo(kartB, kartA);

View File

@ -556,7 +556,7 @@ void RaceGUI::drawGlobalPlayerIcons(const KartIconDisplayInfo* info)
}
}
//attachment
if (kart->getAttachment()->getType() != ATTACH_NOTHING)
if (kart->getAttachment()->getType() != Attachment::ATTACH_NOTHING)
{
video::ITexture *icon_attachment =
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,
bool invisible, bool ai_ignore)
{
if(sideOfLine2D(p0, p2, p1)>0 ||
sideOfLine2D(p0, p2, p3)<0)
if(p1.sideOfLine2D(p0, p2)>0 ||
p3.sideOfLine2D(p0, p2)<0)
{
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());
@ -85,16 +85,6 @@ void Quad::getVertices(video::S3DVertex *v, const video::SColor &color) const
v[3].Color = color;
} // 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
{
@ -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
// 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.
if(sideOfLine2D(m_p[0], m_p[2], p)<0) {
return sideOfLine2D(m_p[0], m_p[1], p) >= 0.0 &&
sideOfLine2D(m_p[1], m_p[2], p) >= 0.0;
if(p.sideOfLine2D(m_p[0], m_p[2])<0) {
return p.sideOfLine2D(m_p[0], m_p[1]) >= 0.0 &&
p.sideOfLine2D(m_p[1], m_p[2]) >= 0.0;
} else {
return sideOfLine2D(m_p[2], m_p[3], p) > 0.0 &&
sideOfLine2D(m_p[3], m_p[0], p) >= 0.0;
return p.sideOfLine2D(m_p[2], m_p[3]) > 0.0 &&
p.sideOfLine2D(m_p[3], m_p[0]) >= 0.0;
}
} // pointInQuad

View File

@ -54,7 +54,6 @@ private:
/** Set if this quad should not be used by the AI. */
bool m_ai_ignore;
float sideOfLine2D(const Vec3& l1, const Vec3& l2, const Vec3& p) const;
public:
Quad(const Vec3 &p0, const Vec3 &p1, const Vec3 &p2, const Vec3 &p3,
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();
if(a.getY()<m_floats[1]) m_floats[1]=a.getY();
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