diff --git a/data/shaders/motion_blur.frag b/data/shaders/motion_blur.frag index 837226d8e..2db9f093e 100644 --- a/data/shaders/motion_blur.frag +++ b/data/shaders/motion_blur.frag @@ -25,20 +25,17 @@ uniform float boost_amount; // The color buffer to use. uniform sampler2D color_buffer; +uniform sampler2D dtex; // Center (in texture coordinates) at which the kart is. A small circle // around this center is not blurred (see mask_radius below) uniform vec2 center; -// The direction to which the blurring aims at -uniform vec2 direction; - // Radius of mask around the character in which no blurring happens // so that the kart doesn't get blurred. uniform float mask_radius; -// Maximum height of texture used -uniform float max_tex_height; +uniform mat4 previous_viewproj; layout (std140) uniform MatrixesData { @@ -55,42 +52,44 @@ out vec4 FragColor; // Number of samples used for blurring #define NB_SAMPLES 8 +vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix); + void main() { - vec2 texcoords = gl_FragCoord.xy / screen; + vec2 texcoords = gl_FragCoord.xy / screen; - // Sample the color buffer - vec3 color = texture(color_buffer, texcoords).rgb; + // Sample the color buffer + vec3 color = texture(color_buffer, texcoords).rgb; - // Compute the blur direction. - // IMPORTANT: we don't normalize it so that it avoids a glitch around 'center', - // plus it naturally scales the motion blur in a cool way :) - vec2 blur_dir = direction - texcoords; + float z = texture(dtex, texcoords).x; + vec4 ViewPos = getPosFromUVDepth(vec3(texcoords, z), InverseProjectionMatrix); + vec4 OldScreenPos = previous_viewproj * InverseViewMatrix * ViewPos; + OldScreenPos /= OldScreenPos.w; + OldScreenPos = .5 * OldScreenPos + .5; - // Compute the blurring factor: - // - apply the mask, i.e. no blurring in a small circle around the kart - float blur_factor = max(0.0, length(texcoords - center) - mask_radius); + // Compute the blur direction. + // IMPORTANT: we don't normalize it so that it avoids a glitch around 'center', + // plus it naturally scales the motion blur in a cool way :) + vec2 blur_dir = texcoords - OldScreenPos.xy; - // - avoid blurring the top of the screen - blur_factor *= (max_tex_height - texcoords.t); + // Compute the blurring factor: + // - apply the mask, i.e. no blurring in a small circle around the kart + float blur_factor = max(0.0, length(texcoords - center) - mask_radius); - // - apply the boost amount - blur_factor *= boost_amount; + // - apply the boost amount + blur_factor *= boost_amount; - // Scale the blur direction - blur_dir *= blur_factor; + // Scale the blur direction + blur_dir *= boost_amount; - // Compute the blur - vec2 inc_vec = blur_dir / vec2(NB_SAMPLES); - vec2 blur_texcoords = texcoords + inc_vec; - for(int i=1 ; i < NB_SAMPLES ; i++) - { - color += texture(color_buffer, blur_texcoords).rgb; - blur_texcoords += inc_vec; - } - color /= vec3(NB_SAMPLES); - FragColor = vec4(color, 1.0); - - // Keep this commented line for debugging: - //FragColor = vec4(blur_factor, blur_factor, blur_factor, 0.0); + // Compute the blur + vec2 inc_vec = blur_dir / vec2(NB_SAMPLES); + vec2 blur_texcoords = texcoords - inc_vec * NB_SAMPLES / 2; + for(int i=1 ; i < NB_SAMPLES ; i++) + { + color += texture(color_buffer, blur_texcoords).rgb; + blur_texcoords += inc_vec; + } + color /= vec3(NB_SAMPLES); + FragColor = vec4(color, 1.0); } diff --git a/data/shaders/object_pass2.frag b/data/shaders/object_pass2.frag index c984c7f34..d45ca01fb 100644 --- a/data/shaders/object_pass2.frag +++ b/data/shaders/object_pass2.frag @@ -8,7 +8,8 @@ vec3 getLightFactor(float specMapValue); void main(void) { - vec4 col = texture(Albedo, uv) * color; + vec4 col = texture(Albedo, uv); + col.xyz *= pow(color.xyz, vec3(2.2)); vec3 LightFactor = getLightFactor(1.); FragColor = vec4(col.xyz * LightFactor, 1.); } diff --git a/data/stk_config.xml b/data/stk_config.xml index 84d3c09c0..57e012e6c 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -380,9 +380,6 @@ downward-impulse-factor="0" track-connection-accel="2"/> - <!-- Parameters for the upright constraint, which keeps karts upright. --> - <upright tolerance="0.2" max-force="30"/> - <!-- collision impulse-type: STK can apply an additional impulse in case of kart-track collision: diff --git a/sources.cmake b/sources.cmake index c054f1b47..317927706 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") -file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") \ No newline at end of file +file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/challenges/challenge_data.cpp b/src/challenges/challenge_data.cpp index 901f0ff6d..a65131611 100644 --- a/src/challenges/challenge_data.cpp +++ b/src/challenges/challenge_data.cpp @@ -376,8 +376,7 @@ void ChallengeData::setRace(RaceManager::Difficulty d) const else if(m_mode==CM_GRAND_PRIX) { race_manager->setMinorMode(m_minor); - const GrandPrixData *gp = grand_prix_manager->getGrandPrix(m_gp_id); - race_manager->setGrandPrix(*gp); + race_manager->setGrandPrix(grand_prix_manager->getGrandPrix(m_gp_id)); race_manager->setDifficulty(d); race_manager->setNumKarts(m_num_karts[d]); race_manager->setNumLocalPlayers(1); @@ -459,7 +458,7 @@ bool ChallengeData::isGPFulfilled() const // is no world objects to query at this stage. if (race_manager->getMajorMode() != RaceManager::MAJOR_MODE_GRAND_PRIX || race_manager->getMinorMode() != m_minor || - race_manager->getGrandPrix()->getId() != m_gp_id || + race_manager->getGrandPrix().getId() != m_gp_id || race_manager->getNumberOfKarts() < (unsigned int)m_num_karts[d] || race_manager->getNumPlayers() > 1) return false; diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index fcfe0feda..7be00a805 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -99,7 +99,7 @@ const int MIN_SUPPORTED_WIDTH = 800; * So we create a dummy device here to begin with, which is then later (once * the real device exists) changed in initDevice(). */ -IrrDriver::IrrDriver() : object_count{} +IrrDriver::IrrDriver() { m_resolution_changing = RES_CHANGE_NONE; m_phase = SOLID_NORMAL_AND_DEPTH_PASS; @@ -112,6 +112,7 @@ IrrDriver::IrrDriver() : object_count{} m_mipviz = m_wireframe = m_normals = m_ssaoviz = \ m_lightviz = m_shadowviz = m_distortviz = m_rsm = m_rh = m_gi = false; SkyboxCubeMap = m_last_light_bucket_distance = 0; + memset(object_count, 0, sizeof(object_count)); } // IrrDriver // ---------------------------------------------------------------------------- diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index 948b48589..29baf0534 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -230,7 +230,7 @@ private: core::array<video::IRenderTarget> m_mrt; /** Matrixes used in several places stored here to avoid recomputation. */ - core::matrix4 m_ViewMatrix, m_InvViewMatrix, m_ProjMatrix, m_InvProjMatrix, m_ProjViewMatrix, m_InvProjViewMatrix; + core::matrix4 m_ViewMatrix, m_InvViewMatrix, m_ProjMatrix, m_InvProjMatrix, m_ProjViewMatrix, m_previousProjViewMatrix, m_InvProjViewMatrix; std::vector<video::ITexture *> SkyboxTextures; std::vector<video::ITexture *> SphericalHarmonicsTextures; @@ -694,7 +694,8 @@ public: void setProjMatrix(core::matrix4 matrix) { m_ProjMatrix = matrix; matrix.getInverse(m_InvProjMatrix); } const core::matrix4 &getProjMatrix() const { return m_ProjMatrix; } const core::matrix4 &getInvProjMatrix() const { return m_InvProjMatrix; } - void genProjViewMatrix() { m_ProjViewMatrix = m_ProjMatrix * m_ViewMatrix; m_InvProjViewMatrix = m_ProjViewMatrix; m_InvProjViewMatrix.makeInverse(); } + void genProjViewMatrix() { m_previousProjViewMatrix = m_ProjViewMatrix; m_ProjViewMatrix = m_ProjMatrix * m_ViewMatrix; m_InvProjViewMatrix = m_ProjViewMatrix; m_InvProjViewMatrix.makeInverse(); } + const core::matrix4 & getPreviousPVMatrix() { return m_previousProjViewMatrix; } const core::matrix4 &getProjViewMatrix() const { return m_ProjViewMatrix; } const core::matrix4 &getInvProjViewMatrix() const { return m_InvProjViewMatrix; } #ifdef DEBUG diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index 9fdbece98..d3073a5df 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -607,13 +607,17 @@ void PostProcessing::renderMotionBlur(unsigned cam, FrameBuffer &in_fbo, FrameBu glUseProgram(FullScreenShader::MotionBlurShader::Program); glBindVertexArray(FullScreenShader::MotionBlurShader::vao); - setTexture(0, in_fbo.getRTT()[0], GL_NEAREST, GL_NEAREST); + setTexture(0, in_fbo.getRTT()[0], GL_LINEAR, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + setTexture(1, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST); FullScreenShader::MotionBlurShader - ::setUniforms(cb->getBoostTime(cam), cb->getCenter(cam), - cb->getDirection(cam), 0.15f, - cb->getMaxHeight(cam) * 0.7f, 0); + ::setUniforms(.1, // Todo : should be framerate dependent + // Todo : use a previousPVMatrix per cam, not global + irr_driver->getPreviousPVMatrix(), + cb->getCenter(cam), + 0.15f, + 0, 1); glDrawArrays(GL_TRIANGLES, 0, 3); } @@ -868,7 +872,7 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo { PROFILER_PUSH_CPU_MARKER("- Motion blur", 0xFF, 0x00, 0x00); ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_MOTIONBLUR)); - if (isRace && UserConfigParams::m_motionblur && m_any_boost && World::getWorld() != NULL) // motion blur + if (isRace && UserConfigParams::m_motionblur && World::getWorld() != NULL) // motion blur { renderMotionBlur(0, *in_fbo, *out_fbo); std::swap(in_fbo, out_fbo); diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp index 86a4adb2b..729d3992e 100644 --- a/src/graphics/render.cpp +++ b/src/graphics/render.cpp @@ -204,9 +204,6 @@ void IrrDriver::renderGLSL(float dt) else fbo->BlitToDefault(viewport.UpperLeftCorner.X, viewport.UpperLeftCorner.Y, viewport.LowerRightCorner.X, viewport.LowerRightCorner.Y); } - else - glDisable(GL_FRAMEBUFFER_SRGB); - PROFILER_POP_CPU_MARKER(); } // for i<world->getNumKarts() @@ -372,7 +369,10 @@ void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned po PROFILER_POP_CPU_MARKER(); } if (!UserConfigParams::m_dynamic_lights && !forceRTT) + { + glDisable(GL_FRAMEBUFFER_SRGB); return; + } // Render displacement { diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index 05daf7bb6..02d759383 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -2663,33 +2663,34 @@ namespace FullScreenShader GLuint MotionBlurShader::uniform_boost_amount; GLuint MotionBlurShader::uniform_center; GLuint MotionBlurShader::uniform_color_buffer; - GLuint MotionBlurShader::uniform_direction; + GLuint MotionBlurShader::uniform_dtex; + GLuint MotionBlurShader::uniform_previous_viewproj; GLuint MotionBlurShader::uniform_mask_radius; - GLuint MotionBlurShader::uniform_max_tex_height; GLuint MotionBlurShader::vao; void MotionBlurShader::init() { Program = LoadProgram( GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(), + GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(), GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/motion_blur.frag").c_str()); uniform_boost_amount = glGetUniformLocation(Program, "boost_amount"); uniform_center = glGetUniformLocation(Program, "center"); uniform_color_buffer = glGetUniformLocation(Program, "color_buffer"); - uniform_direction = glGetUniformLocation(Program, "direction"); uniform_mask_radius = glGetUniformLocation(Program, "mask_radius"); - uniform_max_tex_height = glGetUniformLocation(Program, "max_tex_height"); + uniform_dtex = glGetUniformLocation(Program, "dtex"); + uniform_previous_viewproj = glGetUniformLocation(Program, "previous_viewproj"); vao = createFullScreenVAO(Program); } - void MotionBlurShader::setUniforms(float boost_amount, const core::vector2df ¢er, const core::vector2df &direction, float mask_radius, float max_tex_height, unsigned TU_cb) + void MotionBlurShader::setUniforms(float boost_amount, const core::matrix4 &previousVP, const core::vector2df ¢er, float mask_radius, unsigned TU_cb, unsigned TU_dtex) { + glUniformMatrix4fv(uniform_previous_viewproj, 1, GL_FALSE, previousVP.pointer()); glUniform1f(uniform_boost_amount, boost_amount); glUniform2f(uniform_center, center.X, center.Y); - glUniform2f(uniform_direction, direction.X, direction.Y); glUniform1f(uniform_mask_radius, mask_radius); - glUniform1f(uniform_max_tex_height, max_tex_height); glUniform1i(uniform_color_buffer, TU_cb); + glUniform1i(uniform_dtex, TU_dtex); } GLuint GodFadeShader::Program; diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp index db57030df..f82277126 100644 --- a/src/graphics/shaders.hpp +++ b/src/graphics/shaders.hpp @@ -759,11 +759,11 @@ class MotionBlurShader { public: static GLuint Program; - static GLuint uniform_boost_amount, uniform_color_buffer, uniform_center, uniform_direction, uniform_mask_radius, uniform_max_tex_height; + static GLuint uniform_boost_amount, uniform_color_buffer, uniform_dtex, uniform_previous_viewproj, uniform_center, uniform_mask_radius; static GLuint vao; static void init(); - static void setUniforms(float boost_amount, const core::vector2df ¢er, const core::vector2df &direction, float mask_radius, float max_tex_height, unsigned TU_cb); + static void setUniforms(float boost_amount, const core::matrix4 &previousVP, const core::vector2df ¢er, float mask_radius, unsigned TU_cb, unsigned TU_dtex); }; class GodFadeShader diff --git a/src/guiengine/widgets/spinner_widget.cpp b/src/guiengine/widgets/spinner_widget.cpp index 31a8ad3d2..d6a53e6f8 100644 --- a/src/guiengine/widgets/spinner_widget.cpp +++ b/src/guiengine/widgets/spinner_widget.cpp @@ -160,7 +160,7 @@ void SpinnerWidget::add() { label->setText(m_labels[m_value].c_str() ); } - + } @@ -174,7 +174,7 @@ void SpinnerWidget::add() m_children[2].m_id = m_children[2].m_element->getID(); // refresh display - + setValue(m_value); } @@ -333,7 +333,7 @@ void SpinnerWidget::setValue(const int new_value) assert(new_value >= 0); assert(new_value < (int)m_labels.size()); - m_children[1].m_element->setText(m_labels[new_value].c_str() ); + m_children[1].m_element->setText(m_labels[new_value].c_str()); } else if (m_text.size() > 0 && m_children.size() > 0) { diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp index 4e93875e9..af67b278b 100644 --- a/src/karts/abstract_kart.hpp +++ b/src/karts/abstract_kart.hpp @@ -28,7 +28,6 @@ class AbstractKartAnimation; class Attachment; class btKart; class btQuaternion; -class btUprightConstraint; class Controller; class Item; class KartModel; @@ -342,9 +341,6 @@ public: /** Returns the bullet vehicle which represents this kart. */ virtual btKart* getVehicle() const = 0; // ------------------------------------------------------------------------ - /** Returns the upright constraint for this kart. */ - virtual btUprightConstraint* getUprightConstraint() const = 0; - // ------------------------------------------------------------------------ virtual btQuaternion getVisualRotation() const = 0; // ------------------------------------------------------------------------ /** Returns true if the kart is 'resting', i.e. (nearly) not moving. */ diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index fd720eb9d..23f5dba20 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -58,7 +58,6 @@ #include "network/network_manager.hpp" #include "physics/btKart.hpp" #include "physics/btKartRaycast.hpp" -#include "physics/btUprightConstraint.hpp" #include "physics/physics.hpp" #include "race/history.hpp" #include "tracks/track.hpp" @@ -269,7 +268,6 @@ Kart::~Kart() World::getWorld()->getPhysics()->removeKart(this); delete m_vehicle; delete m_vehicle_raycaster; - delete m_uprightConstraint; } for(int i=0; i<m_kart_chassis.getNumChildShapes(); i++) @@ -682,13 +680,6 @@ void Kart::createPhysics() // Obviously these allocs have to be properly managed/freed btTransform t; t.setIdentity(); - m_uprightConstraint=new btUprightConstraint(this, t); - m_uprightConstraint->setLimit(m_kart_properties->getUprightTolerance()); - m_uprightConstraint->setBounce(0.0f); - m_uprightConstraint->setMaxLimitForce(m_kart_properties->getUprightMaxForce()); - m_uprightConstraint->setErp(1.0f); - m_uprightConstraint->setLimitSoftness(1.0f); - m_uprightConstraint->setDamping(0.0f); World::getWorld()->getPhysics()->addKart(this); } // createPhysics @@ -699,8 +690,9 @@ void Kart::flyUp() { m_flying = true; Moveable::flyUp(); -} +} // flyUp +// ---------------------------------------------------------------------------- void Kart::flyDown() { if (isNearGround()) @@ -712,7 +704,7 @@ void Kart::flyDown() { Moveable::flyDown(); } -} // flyUp +} // flyDown // ---------------------------------------------------------------------------- /** Starts the engine sound effect. Called once the track intro phase is over. @@ -957,8 +949,7 @@ bool Kart::isOnGround() const //----------------------------------------------------------------------------- /** The kart is near the ground, but not necessarily on it (small jumps). This - * is used to determine when to switch off the upright constraint, so that - * explosions can be more violent, while still + * is used to determine when to stop flying. */ bool Kart::isNearGround() const { @@ -1102,21 +1093,6 @@ void Kart::update(float dt) m_slipstream->update(dt); - if (!m_flying) - { - // When really on air, free fly, when near ground, try to glide / - // adjust for landing. If zipped, be stable, so ramp+zipper can - // allow nice jumps without scripting the fly - // Also disable he upright constraint when gravity is changed by - // the terrain - if( (!isNearGround() && - m_max_speed->getSpeedIncreaseTimeLeft(MaxSpeed::MS_INCREASE_ZIPPER)<=0.0f ) || - (getMaterial() && getMaterial()->hasGravity()) ) - m_uprightConstraint->setLimit(M_PI); - else - m_uprightConstraint->setLimit(m_kart_properties->getUprightTolerance()); - } - // TODO: hiker said this probably will be moved to btKart or so when updating bullet engine. // Neutralize any yaw change if the kart leaves the ground, so the kart falls more or less // straight after jumping, but still allowing some "boat shake" (roll and pitch). @@ -2041,6 +2017,30 @@ void Kart::updatePhysics(float dt) m_max_speed->setMinSpeed(min_speed); m_max_speed->update(dt); + // If the kart is flying, keep its up-axis aligned to gravity (which in + // turn typically means the kart is parallel to the ground). This avoids + // that the kart rotates in mid-air and lands on its side. + if(m_vehicle->getNumWheelsOnGround()==0) + { + btVector3 kart_up = getTrans().getBasis().getColumn(1); // up vector + btVector3 terrain_up = m_body->getGravity(); + float g = World::getWorld()->getTrack()->getGravity(); + // Normalize the gravity, g is the length of the vector + btVector3 new_up = 0.9f * kart_up + 0.1f * terrain_up/-g; + // Get the rotation (hpr) based on current heading. + Vec3 rotation(getHeading(), new_up); + btMatrix3x3 m; + m.setEulerZYX(rotation.getX(), rotation.getY(), rotation.getZ()); + // We can't use getXYZ() for the position here, since the position is + // based on interpolation, while the actual center-of-mass-transform + // is based on the actual value every 1/60 of a second (using getXYZ() + // would result in the kart being pushed ahead a bit, making it jump + // much further, depending on fps) + btTransform new_trans(m, m_body->getCenterOfMassTransform().getOrigin()); + //setTrans(new_trans); + m_body->setCenterOfMassTransform(new_trans); + } + // To avoid tunneling (which can happen on long falls), clamp the // velocity in Y direction. Tunneling can happen if the Y velocity // is larger than the maximum suspension travel (per frame), since then @@ -2048,6 +2048,7 @@ void Kart::updatePhysics(float dt) // not sure if this is enough in all cases!). So the speed is limited // to suspensionTravel / dt with dt = 1/60 (since this is the dt // bullet is using). + // Only apply if near ground instead of purely based on speed avoiding // the "parachute on top" look. const Vec3 &v = m_body->getLinearVelocity(); @@ -2056,7 +2057,7 @@ void Kart::updatePhysics(float dt) Vec3 v_clamped = v; // clamp the speed to 99% of the maxium falling speed. v_clamped.setY(-m_kart_properties->getSuspensionTravelCM()*0.01f*60 * 0.99f); - m_body->setLinearVelocity(v_clamped); + //m_body->setLinearVelocity(v_clamped); } //at low velocity, forces on kart push it back and forth so we ignore this diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp index eaf7e835c..8ce5ea519 100644 --- a/src/karts/kart.hpp +++ b/src/karts/kart.hpp @@ -35,7 +35,6 @@ #include "utils/no_copy.hpp" class btKart; -class btUprightConstraint; class Attachment; class Controller; @@ -146,7 +145,6 @@ private: btCompoundShape m_kart_chassis; btVehicleRaycaster *m_vehicle_raycaster; btKart *m_vehicle; - btUprightConstraint *m_uprightConstraint; /** The amount of energy collected by hitting coins. Note that it * must be float, since dt is subtraced in each timestep. */ @@ -342,11 +340,7 @@ public: virtual Skidding *getSkidding() { return m_skidding; } // ------------------------------------------------------------------------ /** Returns the bullet vehicle which represents this kart. */ - virtual btKart *getVehicle () const {return m_vehicle; } - // ------------------------------------------------------------------------ - /** Returns the upright constraint for this kart. */ - virtual btUprightConstraint *getUprightConstraint() const - {return m_uprightConstraint;} + virtual btKart *getVehicle() const {return m_vehicle; } // ------------------------------------------------------------------------ /** Returns the speed of the kart in meters/second. */ virtual float getSpeed() const {return m_speed; } diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp index 940bf5c7a..68706516e 100644 --- a/src/karts/kart_properties.cpp +++ b/src/karts/kart_properties.cpp @@ -72,9 +72,8 @@ KartProperties::KartProperties(const std::string &filename) m_wheel_radius = m_chassis_linear_damping = m_max_suspension_force = m_chassis_angular_damping = m_suspension_rest = m_max_speed_reverse_ratio = m_rescue_vert_offset = - m_upright_tolerance = m_collision_terrain_impulse = - m_collision_impulse = m_restitution = m_collision_impulse_time = - m_upright_max_force = m_suspension_travel_cm = + m_collision_terrain_impulse = m_collision_impulse = m_restitution = + m_collision_impulse_time = m_suspension_travel_cm = m_track_connection_accel = m_rubber_band_max_length = m_rubber_band_force = m_rubber_band_duration = m_rubber_band_speed_increase = m_rubber_band_fade_out_time = @@ -367,12 +366,6 @@ void KartProperties::getAllData(const XMLNode * root) &m_track_connection_accel ); } - if(const XMLNode *upright_node = root->getNode("upright")) - { - upright_node->get("tolerance", &m_upright_tolerance); - upright_node->get("max-force", &m_upright_max_force); - } - if(const XMLNode *collision_node = root->getNode("collision")) { collision_node->get("impulse", &m_collision_impulse ); @@ -673,8 +666,6 @@ void KartProperties::checkAllSet(const std::string &filename) CHECK_NEG(m_bevel_factor.getX(), "collision bevel-factor" ); CHECK_NEG(m_bevel_factor.getY(), "collision bevel-factor" ); CHECK_NEG(m_bevel_factor.getZ(), "collision bevel-factor" ); - CHECK_NEG(m_upright_tolerance, "upright tolerance" ); - CHECK_NEG(m_upright_max_force, "upright max-force" ); CHECK_NEG(m_rubber_band_max_length, "plunger band-max-length" ); CHECK_NEG(m_rubber_band_force, "plunger band-force" ); CHECK_NEG(m_rubber_band_duration, "plunger band-duration" ); diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp index e9b50c6d1..d90dec561 100644 --- a/src/karts/kart_properties.hpp +++ b/src/karts/kart_properties.hpp @@ -328,9 +328,6 @@ private: /** The restitution factor to be used in collsions for this kart. */ float m_restitution; - float m_upright_tolerance; - float m_upright_max_force; - /** How far behind a kart slipstreaming is effective. */ float m_slipstream_length; /** How wide the slipstream area is at the end. */ @@ -699,15 +696,6 @@ public: float getExplosionInvulnerabilityTime() const { return m_explosion_invulnerability_time; } - // ------------------------------------------------------------------------ - /** Returns how much a kart can roll/pitch before the upright constraint - * counteracts. */ - float getUprightTolerance () const {return m_upright_tolerance; } - - // ------------------------------------------------------------------------ - /** Returns the maximum value of the upright counteracting force. */ - float getUprightMaxForce () const {return m_upright_max_force; } - // ------------------------------------------------------------------------ /** Returns the maximum length of a rubber band before it breaks. */ float getRubberBandMaxLength () const {return m_rubber_band_max_length;} diff --git a/src/main.cpp b/src/main.cpp index 3e438a95c..8bc70861a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -854,7 +854,7 @@ int handleCmdLine() Log::warn("main", "There is no GP named '%s'.", s.c_str()); return 0; } - race_manager->setGrandPrix(*gp); + race_manager->setGrandPrix(gp); } // --gp if(CommandLine::has("--numkarts", &n) ||CommandLine::has("-k", &n)) diff --git a/src/network/race_config.cpp b/src/network/race_config.cpp index bb4ac65ae..a60af9015 100644 --- a/src/network/race_config.cpp +++ b/src/network/race_config.cpp @@ -3,6 +3,8 @@ #include "race/race_manager.hpp" #include "utils/log.hpp" +#include <stdexcept> + /** \brief Gets the element with the highest count in a std::map<S,int>. * \param histogram : A pointer to the histogram. * \return The key of type S that has the highest second value. diff --git a/src/physics/btUprightConstraint.cpp b/src/physics/btUprightConstraint.cpp deleted file mode 100644 index 3b183c5e6..000000000 --- a/src/physics/btUprightConstraint.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - -Bullet Continuous Collision Detection and Physics Library -Copyright (C) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the -use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim - that you wrote the original software. If you use this software in a - product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. - -*/ - -#include "physics/btUprightConstraint.hpp" - -#include <new> -#include <stdio.h> - -#include "BulletDynamics/Dynamics/btRigidBody.h" -#include "LinearMath/btTransformUtil.h" - -#include "karts/kart.hpp" - -//! -//! -//! - -void btUprightConstraint::solveAngularLimit( - btUprightConstraintLimit *limit, - btScalar timeStep, btScalar jacDiagABInv, - btRigidBody * body0 ) -{ - - // Work out if limit is violated - if(limit->m_angle>=m_loLimit && limit->m_angle<=m_hiLimit) return; - - limit->m_currentLimitError = (limit->m_angle<m_loLimit) - ? limit->m_angle - m_loLimit - : limit->m_angle - m_hiLimit; - - btScalar targetVelocity = -m_ERP*limit->m_currentLimitError - / (3.1415f/8.0f*timeStep); - btScalar maxMotorForce = m_maxLimitForce; - - maxMotorForce *= timeStep; - - // current velocity difference - btVector3 angularVelocity = body0->getAngularVelocity(); - btScalar axisAngularVelocity = limit->m_axis.dot( angularVelocity ); - - // correction velocity - btScalar motorVelocity = m_limitSoftness*(targetVelocity - - m_damping*axisAngularVelocity); - - // correction impulse - btScalar unclippedMotorImpulse = (1+m_bounce)*motorVelocity*jacDiagABInv; - - // clip correction impulse - btScalar clippedMotorImpulse = unclippedMotorImpulse; - - //todo: should clip against accumulated impulse - - if (unclippedMotorImpulse>0.0f) - { - clippedMotorImpulse = unclippedMotorImpulse > maxMotorForce - ? maxMotorForce : unclippedMotorImpulse; - } - else - { - clippedMotorImpulse = unclippedMotorImpulse < -maxMotorForce - ? -maxMotorForce : unclippedMotorImpulse; - } - - // sort with accumulated impulses - btScalar lo = btScalar(-1e30); - btScalar hi = btScalar(1e30); - - btScalar oldaccumImpulse = limit->m_accumulatedImpulse; - - btScalar sum = oldaccumImpulse + clippedMotorImpulse; - - limit->m_accumulatedImpulse = sum > hi ? btScalar(0.) - : sum < lo ? btScalar(0.) : sum; - - clippedMotorImpulse = limit->m_accumulatedImpulse - oldaccumImpulse; - - btVector3 motorImp = clippedMotorImpulse * limit->m_axis; - body0->applyTorqueImpulse(motorImp); -} // solveAngularLimit - -//! -//! -//! - -btUprightConstraint::btUprightConstraint(const Kart* kart, - const btTransform& frameInA) - : btTypedConstraint(D6_CONSTRAINT_TYPE, *(kart->getBody())) - , m_frameInA(frameInA) - -{ - m_kart = kart; - m_ERP = 1.0f; - m_bounce = 0.0f; - m_damping = 1.0f; - m_limitSoftness = 1.0f; - m_maxLimitForce = 3000.0f; - m_disable_time = 0.0f; - m_limit[0].m_accumulatedImpulse = 0.0f; - m_limit[1].m_accumulatedImpulse = 0.0f; - m_limit[ 0 ].m_axis = btVector3( 1, 0, 0 ); - m_limit[ 1 ].m_axis = btVector3( 0, 0, 1 ); - setLimit( SIMD_PI * 0.4f ); -} // btUprightConstraint - -//! -//! -//! - -void btUprightConstraint::buildJacobian() -{ - m_limit[ 0 ].m_angle = m_kart->getPitch(); - m_limit[ 1 ].m_angle = -m_kart->getRoll(); - for ( int i = 0; i < 2; i++ ) - { - new (&m_jacAng[ i ]) btJacobianEntry( m_limit[ i ].m_axis, - m_rbA.getCenterOfMassTransform().getBasis().transpose(), - m_rbB.getCenterOfMassTransform().getBasis().transpose(), - m_rbA.getInvInertiaDiagLocal(), - m_rbB.getInvInertiaDiagLocal()); - } -} // buildJacobian - -//! -//! -//! - -void btUprightConstraint::solveConstraintObsolete(btRigidBody& /*bodyA*/,btRigidBody& /*bodyB*/,btScalar timeStep) -{ - m_timeStep = timeStep; - - // Update disable time and return if constraint is still disabled - if(m_disable_time>0.0f) - { - m_disable_time -= timeStep; - if(m_disable_time>0.0f) return; - } - - solveAngularLimit( &m_limit[ 0 ], m_timeStep, btScalar(1.) / m_jacAng[ 0 ].getDiagonal(), &m_rbA ); - solveAngularLimit( &m_limit[ 1 ], m_timeStep, btScalar(1.) / m_jacAng[ 1 ].getDiagonal(), &m_rbA ); -} // solveConstraint - -void btUprightConstraint::getInfo1(btConstraintInfo1* info) { - info->m_numConstraintRows = 0; - info->nub = 0; -} - -void btUprightConstraint::getInfo2(btConstraintInfo2* info) { -} - -btScalar btUprightConstraint::getParam(int num, int axis) const -{ - return 0; -} - -void btUprightConstraint::setParam(int num, btScalar value, int axis) -{ -} diff --git a/src/physics/btUprightConstraint.hpp b/src/physics/btUprightConstraint.hpp deleted file mode 100644 index a2711fa9f..000000000 --- a/src/physics/btUprightConstraint.hpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - -Bullet Continuous Collision Detection and Physics Library -Copyright (C) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the -use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim - that you wrote the original software. If you use this software in a - product, an acknowledgment in the product documentation would be - appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef HEADER_UPRIGHT_CONSTRAINT_HPP -#define HEADER_UPRIGHT_CONSTRAINT_HPP - -#include "LinearMath/btVector3.h" -#include "BulletDynamics/ConstraintSolver/btJacobianEntry.h" -#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" - -class btRigidBody; -class Kart; - -/** - * \ingroup physics - */ -class btUprightConstraint : public btTypedConstraint -{ -private: - class btUprightConstraintLimit - { - public: - btVector3 m_axis; - btScalar m_angle; - btScalar m_accumulatedImpulse; - btScalar m_currentLimitError; - }; - - //! relative_frames - - //!@{ - btTransform m_frameInA;//!< the constraint space w.r.t body A - //!@} - - //! Jacobians - //!@{ - btJacobianEntry m_jacAng[ 2 ];//!< angular constraint - //!@} - - const Kart *m_kart; -protected: - - //! temporal variables - //!@{ - btScalar m_timeStep; - btScalar m_ERP; - btScalar m_bounce; - btScalar m_damping; - btScalar m_maxLimitForce; - btScalar m_limitSoftness; - btScalar m_hiLimit; - btScalar m_loLimit; - btScalar m_disable_time; - - btUprightConstraintLimit m_limit[ 2 ]; - - //!@} - - btUprightConstraint& operator=(btUprightConstraint& other) - { - btAssert(0); - (void) other; - return *this; - } - - void buildAngularJacobian(btJacobianEntry & jacAngular, - const btVector3 & jointAxisW); - - void solveAngularLimit(btUprightConstraintLimit *limit, - btScalar timeStep, btScalar jacDiagABInv, - btRigidBody * body0 ); - -public: - - btUprightConstraint(const Kart* kart, const btTransform& frameInA); - - // -PI,+PI is the full range - // 0,0 is no rotation around x or z - // -PI*0.2,+PI*0.2 is a nice bit of tilt - void setLimit( btScalar range ) { m_loLimit = -range; - m_hiLimit = +range; } - // Error correction scaling - // 0 - 1 - void setErp( btScalar erp ) { m_ERP = erp; } - void setBounce( btScalar bounce ) { m_bounce = bounce; } - void setMaxLimitForce( btScalar force ) { m_maxLimitForce = force; } - void setLimitSoftness( btScalar softness ) { m_limitSoftness = softness; } - void setDamping( btScalar damping ) { m_damping = damping; } - void setDisableTime( btScalar t ) { m_disable_time = t; } - virtual void buildJacobian(); - virtual void solveConstraintObsolete(btRigidBody& /*bodyA*/, - btRigidBody& /*bodyB*/, btScalar - timeStep); - virtual void getInfo1 (btConstraintInfo1* info); - virtual void getInfo2 (btConstraintInfo2* info); - virtual void setParam(int num, btScalar value, int axis = -1); - virtual btScalar getParam(int num, int axis) const; - -}; - - - -#endif //UPRIGHT_CONSTRAINT_H - diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 1a845ca4c..1d2995a5c 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -33,7 +33,6 @@ #include "modes/world.hpp" #include "karts/explosion_animation.hpp" #include "physics/btKart.hpp" -#include "physics/btUprightConstraint.hpp" #include "physics/irr_debug_drawer.hpp" #include "physics/physical_object.hpp" #include "physics/stk_dynamics_world.hpp" @@ -86,8 +85,8 @@ Physics::~Physics() // ---------------------------------------------------------------------------- /** Adds a kart to the physics engine. - * This adds the rigid body, the vehicle, and the upright constraint, but only - * if the kart is not already in the physics world. + * This adds the rigid body and the vehicle but only if the kart is not + * already in the physics world. * \param kart The kart to add. * \param vehicle The raycast vehicle object. */ @@ -102,7 +101,6 @@ void Physics::addKart(const AbstractKart *kart) } m_dynamics_world->addRigidBody(kart->getBody()); m_dynamics_world->addVehicle(kart->getVehicle()); - m_dynamics_world->addConstraint(kart->getUprightConstraint()); } // addKart //----------------------------------------------------------------------------- @@ -130,7 +128,6 @@ void Physics::removeKart(const AbstractKart *kart) { m_dynamics_world->removeRigidBody(kart->getBody()); m_dynamics_world->removeVehicle(kart->getVehicle()); - m_dynamics_world->removeConstraint(kart->getUprightConstraint()); } } // removeKart diff --git a/src/race/grand_prix_data.cpp b/src/race/grand_prix_data.cpp index e1c293a8d..f717edc0c 100644 --- a/src/race/grand_prix_data.cpp +++ b/src/race/grand_prix_data.cpp @@ -24,13 +24,15 @@ #include "config/player_manager.hpp" #include "io/file_manager.hpp" #include "io/utf_writer.hpp" +#include "states_screens/dialogs/random_gp_dialog.hpp" #include "tracks/track_manager.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" -#include <iostream> -#include <memory> #include <algorithm> +#include <cassert> +#include <cstdlib> +#include <memory> #include <stdexcept> @@ -44,6 +46,94 @@ GrandPrixData::GrandPrixData(const std::string& filename) reload(); } +// ---------------------------------------------------------------------------- +GrandPrixData::GrandPrixData(const unsigned int number_of_tracks, + const std::string& track_group, + const RandomGPInfoDialog::REVERSED use_reverse) +{ + m_filename = "Random GP - Not loaded from a file!"; + m_id = "random"; + m_name = "Random Grand Prix"; + m_editable = false; + + m_tracks.reserve(number_of_tracks); + m_laps.reserve(number_of_tracks); + m_reversed.reserve(number_of_tracks); + + changeTrackNumber(number_of_tracks, track_group); + changeReverse(use_reverse); +} + +// ---------------------------------------------------------------------------- +void GrandPrixData::changeTrackNumber(const unsigned int number_of_tracks, + const std::string& track_group) +{ + // The problem with the track groups is that "all" isn't a track group + // TODO: Add "all" to the track groups and rewrite this more elegant + std::vector<int> track_indices; + size_t available_tracks; + if (track_group == "all") + { + available_tracks = track_manager->getNumberOfTracks(); + } + else + { + track_indices = track_manager->getTracksInGroup(track_group); + available_tracks = track_indices.size(); + } + assert(number_of_tracks <= available_tracks); + + // add or remove the right number of tracks + if (m_tracks.size() < number_of_tracks) + { + while (m_tracks.size() < number_of_tracks) + { + int index = (track_group == "all") ? + rand() % available_tracks : + track_indices[rand() % available_tracks]; + + std::string id = track_manager->getTrack(index)->getIdent(); + // Avoid duplicate tracks + if (std::find(m_tracks.begin(), m_tracks.end(), id) != m_tracks.end()) + continue; + + m_tracks.push_back(id); + m_laps.push_back(3); // TODO: Take the default number from the track + m_reversed.push_back(false); // This will be changed later + } + } + else if (m_tracks.size() > number_of_tracks) + { + while (m_tracks.size() > number_of_tracks) + { + m_tracks.pop_back(); + m_laps.pop_back(); + m_reversed.pop_back(); + } + } + + assert(m_tracks.size() == m_laps.size() ); + assert(m_laps.size() == m_reversed.size()); +} + +// ---------------------------------------------------------------------------- + +void GrandPrixData::changeReverse(const RandomGPInfoDialog::REVERSED use_reverse) +{ + for (unsigned int i = 0; i < m_tracks.size(); i++) + { + if (use_reverse == RandomGPInfoDialog::NO_REVERSE) + m_reversed[i] = false; + else if (use_reverse == RandomGPInfoDialog::MIXED) + if (track_manager->getTrack(m_tracks[i])->reverseAvailable()) + m_reversed[i] = (rand() % 2!=0); + else + m_reversed[i] = false; + else // all reversed + m_reversed[i] = true; + } +} + // ---------------------------------------------------------------------------- void GrandPrixData::setId(const std::string& id) { diff --git a/src/race/grand_prix_data.hpp b/src/race/grand_prix_data.hpp index c2013d75c..742ec15f7 100644 --- a/src/race/grand_prix_data.hpp +++ b/src/race/grand_prix_data.hpp @@ -20,14 +20,15 @@ #ifndef HEADER_GRAND_PRIX_DATA_HPP #define HEADER_GRAND_PRIX_DATA_HPP +#include <irrString.h> #include <string> #include <vector> -#include <cassert> -#include <irrString.h> -#include <stdexcept> +#include "states_screens/dialogs/random_gp_dialog.hpp" #include "utils/translation.hpp" +using irr::core::stringw; + class Track; /** Simple class that hold the data relevant to a 'grand_prix', aka. a number @@ -76,6 +77,13 @@ public: GrandPrixData(const std::string& filename); /** Needed for simple creation of an instance of GrandPrixData */ GrandPrixData() {}; + /** Creates a new random GP */ + GrandPrixData(const unsigned int number_of_tracks, + const std::string& track_group, + const RandomGPInfoDialog::REVERSED use_reverse); + void changeTrackNumber(const unsigned int number_of_tracks, + const std::string& track_group); + void changeReverse(const RandomGPInfoDialog::REVERSED use_reverse); // Methods for the GP editor void setId(const std::string& id); @@ -117,7 +125,6 @@ public: // ------------------------------------------------------------------------ /** Returns the filename of the grand prix xml file. */ const std::string& getFilename() const { return m_filename; } - }; // GrandPrixData #endif diff --git a/src/race/grand_prix_manager.cpp b/src/race/grand_prix_manager.cpp index 48b02046e..9270bac06 100644 --- a/src/race/grand_prix_manager.cpp +++ b/src/race/grand_prix_manager.cpp @@ -31,6 +31,23 @@ GrandPrixManager *grand_prix_manager = NULL; const char* GrandPrixManager::SUFFIX = ".grandprix"; +// ---------------------------------------------------------------------------- +GrandPrixManager::GrandPrixManager() +{ + m_random_gp = NULL; // better do it explicitly and avoid weird stuff + loadFiles(); +} // GrandPrixManager + +// ---------------------------------------------------------------------------- +GrandPrixManager::~GrandPrixManager() +{ + for(unsigned int i=0; i<m_gp_data.size(); i++) + { + delete m_gp_data[i]; + } + delete m_random_gp; +} // ~GrandPrixManager + // ---------------------------------------------------------------------------- void GrandPrixManager::loadFiles() { @@ -48,7 +65,7 @@ void GrandPrixManager::loadFiles() if (!dir.empty() && dir[dir.size() - 1] == '/') loadDir(dir); } -} +} // loadFiles // ---------------------------------------------------------------------------- void GrandPrixManager::loadDir(const std::string& dir) @@ -64,7 +81,7 @@ void GrandPrixManager::loadDir(const std::string& dir) i != result.end(); i++) if (StringUtils::hasSuffix(*i, SUFFIX)) load(dir + *i); -} +} // loadDir // ---------------------------------------------------------------------------- void GrandPrixManager::load(const std::string& filename) @@ -92,7 +109,7 @@ void GrandPrixManager::reload() m_gp_data.clear(); loadFiles(); -} +} // reload // ---------------------------------------------------------------------------- std::string GrandPrixManager::generateId() @@ -118,7 +135,7 @@ std::string GrandPrixManager::generateId() } return s.str(); -} +} // generateId // ---------------------------------------------------------------------------- bool GrandPrixManager::existsName(const irr::core::stringw& name) const @@ -128,32 +145,20 @@ bool GrandPrixManager::existsName(const irr::core::stringw& name) const return true; return false; -} - -// ---------------------------------------------------------------------------- -GrandPrixManager::GrandPrixManager() -{ - loadFiles(); -} - -// ---------------------------------------------------------------------------- -GrandPrixManager::~GrandPrixManager() -{ - for(unsigned int i=0; i<m_gp_data.size(); i++) - { - delete m_gp_data[i]; - } -} +} // existsName // ---------------------------------------------------------------------------- GrandPrixData* GrandPrixManager::getGrandPrix(const std::string& s) const { return editGrandPrix(s); -} +} // getGrandPrix // ---------------------------------------------------------------------------- GrandPrixData* GrandPrixManager::editGrandPrix(const std::string& s) const { + if (s == "random") + return m_random_gp; + for(unsigned int i=0; i<m_gp_data.size(); i++) { if(m_gp_data[i]->getId() == s) @@ -161,7 +166,7 @@ GrandPrixData* GrandPrixManager::editGrandPrix(const std::string& s) const } // for i in m_gp_data return NULL; -} +} // editGrandPrix // ---------------------------------------------------------------------------- void GrandPrixManager::checkConsistency() @@ -194,7 +199,7 @@ GrandPrixData* GrandPrixManager::createNewGP(const irr::core::stringw& newName) m_gp_data.push_back(gp); return gp; -} +} // createNewGP // ---------------------------------------------------------------------------- GrandPrixData* GrandPrixManager::copy(const std::string& id, @@ -214,7 +219,7 @@ GrandPrixData* GrandPrixManager::copy(const std::string& id, m_gp_data.push_back(gp); return gp; -} +} // copy // ---------------------------------------------------------------------------- void GrandPrixManager::remove(const std::string& id) @@ -232,4 +237,4 @@ void GrandPrixManager::remove(const std::string& id) Log::warn("GrandPrixManager", "Grand Prix '%s' can not be removed", gp->getId().c_str()); } -} +} // remove diff --git a/src/race/grand_prix_manager.hpp b/src/race/grand_prix_manager.hpp index c8510bf3a..314ddcc59 100644 --- a/src/race/grand_prix_manager.hpp +++ b/src/race/grand_prix_manager.hpp @@ -47,6 +47,10 @@ private: bool existsName(const irr::core::stringw& name) const; public: + /** saved here by a random GP dialog to avoid dangling pinters or + * memory leaks */ + GrandPrixData* m_random_gp; + GrandPrixManager(); ~GrandPrixManager(); void reload(); diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index 98460295a..2626530b4 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -771,9 +771,10 @@ void RaceManager::startGP(const GrandPrixData* gp, bool from_overworld, bool continue_saved_gp) { assert(gp != NULL); + //std::cout << gp->getId(); StateManager::get()->enterGameState(); - setGrandPrix(*gp); + setGrandPrix(gp); setCoinTarget( 0 ); // Might still be set from a previous challenge race_manager->setupPlayerKartInfo(); m_continue_saved_gp = continue_saved_gp; diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp index 12834fac8..379fd3ed0 100644 --- a/src/race/race_manager.hpp +++ b/src/race/race_manager.hpp @@ -331,7 +331,7 @@ private: int m_coin_target; bool m_has_time_target; float m_time_target; - int m_goal_target; + int m_goal_target; void startNextRace(); // start a next race @@ -344,7 +344,7 @@ private: bool m_have_kart_last_position_on_overworld; Vec3 m_kart_last_position_on_overworld; - + /** Determines if saved GP should be continued or not*/ bool m_continue_saved_gp; @@ -410,9 +410,9 @@ public: void setDifficulty(Difficulty diff); // ------------------------------------------------------------------------ - void setGrandPrix(const GrandPrixData &gp) + void setGrandPrix(const GrandPrixData *gp) { - m_grand_prix = gp; + m_grand_prix = *gp; m_coin_target = 0; } // ------------------------------------------------------------------------ @@ -525,7 +525,7 @@ public: // ------------------------------------------------------------------------ const std::string& getTrackName() const { return m_tracks[m_track_number];} // ------------------------------------------------------------------------ - const GrandPrixData *getGrandPrix() const { return &m_grand_prix; } + const GrandPrixData& getGrandPrix() const { return m_grand_prix; } // ------------------------------------------------------------------------ unsigned int getFinishedKarts() const { return m_num_finished_karts; } // ------------------------------------------------------------------------ diff --git a/src/states_screens/dialogs/enter_gp_name_dialog.cpp b/src/states_screens/dialogs/enter_gp_name_dialog.cpp index 6434a747d..e75c64abc 100644 --- a/src/states_screens/dialogs/enter_gp_name_dialog.cpp +++ b/src/states_screens/dialogs/enter_gp_name_dialog.cpp @@ -81,7 +81,7 @@ void EnterGPNameDialog::onEnterPressedInternal() TextBoxWidget* textCtrl = getWidget<TextBoxWidget>("textfield"); assert(textCtrl != NULL); stringw name = textCtrl->getText().trim(); - if (name.size() > 0) + if (name.size() > 0 && name != "Random Grand Prix") { // check for duplicate names for (unsigned int i = 0; i < grand_prix_manager->getNumberOfGrandPrix(); i++) diff --git a/src/states_screens/dialogs/gp_info_dialog.cpp b/src/states_screens/dialogs/gp_info_dialog.cpp index 9f8ff35ff..36fc61646 100644 --- a/src/states_screens/dialogs/gp_info_dialog.cpp +++ b/src/states_screens/dialogs/gp_info_dialog.cpp @@ -28,6 +28,7 @@ #include "guiengine/widgets/label_widget.hpp" #include "io/file_manager.hpp" #include "race/grand_prix_manager.hpp" +#include "race/grand_prix_data.hpp" #include "race/race_manager.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/tracks_screen.hpp" @@ -35,143 +36,190 @@ #include "tracks/track_manager.hpp" #include "utils/translation.hpp" -#include <iostream> #include <IGUIEnvironment.h> #include <IGUIStaticText.h> -using namespace irr::gui; -using namespace irr::video; -using namespace irr::core; -using namespace GUIEngine; +using irr::gui::IGUIStaticText; +using GUIEngine::PROP_ID; -// ------------------------------------------------------------------------------------------------------ +typedef GUIEngine::LabelWidget Label; -GPInfoDialog::GPInfoDialog(const std::string& gpIdent, const float w, const float h) : ModalDialog(w, h) +const float GPInfoDialog::PERCENT_WIDTH = 0.8f; +const float GPInfoDialog::PERCENT_HEIGHT = 0.7f; + +GPInfoDialog::GPInfoDialog(const std::string& gp_ident) + : ModalDialog(PERCENT_WIDTH, PERCENT_HEIGHT) { doInit(); m_curr_time = 0.0f; - const int y1 = m_area.getHeight()/7; - const int y2 = m_area.getHeight()*6/7; + m_gp = grand_prix_manager->getGrandPrix(gp_ident); + m_gp->checkConsistency(); - m_gp_ident = gpIdent; + m_under_title = m_area.getHeight()/7; + m_over_body = m_area.getHeight()/7; + m_lower_bound = m_area.getHeight()*6/7; - const GrandPrixData* gp = grand_prix_manager->getGrandPrix(gpIdent); - assert (gp != NULL); + addTitle(); + addTracks(); + addScreenshot(); + addButtons(); +} - // ---- GP Name - core::rect< s32 > area_top(0, 0, m_area.getWidth(), y1); - IGUIStaticText* title = GUIEngine::getGUIEnv()->addStaticText( translations->fribidize(gp->getName()), - area_top, false, true, // border, word wrap - m_irrlicht_window); +// ---------------------------------------------------------------------------- + +GPInfoDialog::~GPInfoDialog() +{ + GUIEngine::Screen* curr_screen = GUIEngine::getCurrentScreen(); + if (curr_screen->getName() == "tracks.stkgui") + static_cast<TracksScreen*>(curr_screen)->setFocusOnGP(m_gp->getId()); +} + +// ---------------------------------------------------------------------------- + +void GPInfoDialog::addTitle() +{ + core::rect< s32 > area_top(0, 0, m_area.getWidth(), m_under_title); + IGUIStaticText* title = GUIEngine::getGUIEnv()->addStaticText( + translations->fribidize(m_gp->getName()), + area_top, false, true, // border, word wrap + m_irrlicht_window); title->setTabStop(false); - title->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); + title->setTextAlignment(irr::gui::EGUIA_CENTER, irr::gui::EGUIA_CENTER); +} +// ---------------------------------------------------------------------------- - // ---- Track listings - const std::vector<std::string> tracks = gp->getTrackNames(); - const int trackAmount = tracks.size(); +void GPInfoDialog::addTracks() +{ + const std::vector<std::string> tracks = m_gp->getTrackNames(); + const unsigned int track_amount = tracks.size(); - int height_of_one_line = (y2 - y1)/(trackAmount+1); - const int textHeight = GUIEngine::getFontHeight(); - if (height_of_one_line > (int)(textHeight*1.5f)) height_of_one_line = (int)(textHeight*1.5f); + int height_of_one_line = std::min((m_lower_bound - m_over_body)/(track_amount+1), + (unsigned int)(GUIEngine::getFontHeight()*1.5f)); - bool gp_ok = true; - - for (int t=0; t<trackAmount; t++) + // Count the number of label already existing labels representing a track + unsigned int existing = 0; + for (unsigned int i = 0; i < m_widgets.size(); i++) { - const int from_y = y1 + height_of_one_line*(t+1); - - Track* track = track_manager->getTrack(tracks[t]); - stringw lineText; - if (track == NULL) - { - lineText = L"MISSING : "; - lineText.append( stringw(tracks[t].c_str()) ); - gp_ok = false; - } - else - { - lineText = track->getName(); - } - - LabelWidget* widget = new LabelWidget(); - widget->setText(translations->fribidize(lineText), false); - widget->m_x = 20; - widget->m_y = from_y; - widget->m_w = m_area.getWidth()/2 - 20; - widget->m_h = height_of_one_line; - widget->setParent(m_irrlicht_window); - - m_widgets.push_back(widget); - widget->add(); - - // IGUIStaticText* line = GUIEngine::getGUIEnv()->addStaticText( lineText.c_str(), - // entry_area, false , true , // border, word wrap - // m_irrlicht_window); + if (m_widgets.get(i)->m_properties[PROP_ID] == "Track label") + existing++; } - // ---- Track screenshot + unsigned int reuse = std::min(existing, track_amount); + // m_widgets has the type PtrVector<Widget, HOLD> + unsigned int widgets_iter = 0; + for (unsigned int i = 0; i < reuse; i++) + { + Track* track = track_manager->getTrack(tracks[i]); - m_screenshot_widget = new IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO, - false /* tab stop */, false /* focusable */, - IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE /* Track gives us absolute paths */); + // Find the next widget that is a track label + while (m_widgets.get(widgets_iter)->m_properties[PROP_ID] != "Track label") + widgets_iter++; + + Label* widget = dynamic_cast<Label*>(m_widgets.get(widgets_iter)); + widget->setText(translations->fribidize(track->getName()), false); + widget->move(20, m_over_body + height_of_one_line*(i+1), + m_area.getWidth()/2 - 20, height_of_one_line); + + widgets_iter++; + } + + if (existing < track_amount) + { + // There are not enough labels for all the track names, so we have to + // add some more + for (unsigned int i = reuse; i < track_amount; i++) + { + Track* track = track_manager->getTrack(tracks[i]); + assert(track != NULL); + + Label* widget = new Label(); + widget->m_properties[PROP_ID] = "Track label"; + widget->setText(translations->fribidize(track->getName()), false); + widget->setParent(m_irrlicht_window); + m_widgets.push_back(widget); + widget->add(); + + widget->move(20, m_over_body + height_of_one_line*(i+1), + m_area.getWidth()/2 - 20, height_of_one_line); + } + } + else if (existing > track_amount) + { + // There are label which are not necessary anymore so they're deleted + for (unsigned int i = widgets_iter; i < m_widgets.size(); i++) + { + if (m_widgets.get(i)->m_properties[PROP_ID] == "Track label") + { + m_irrlicht_window->removeChild(m_widgets.get(i)->getIrrlichtElement()); + m_widgets.remove(i); + i--; + } + } + } +} + +// ---------------------------------------------------------------------------- + +void GPInfoDialog::addScreenshot() +{ + m_screenshot_widget = new GUIEngine::IconButtonWidget( + GUIEngine::IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO, + false, false, GUIEngine::IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); // images are saved squared, but must be stretched to 4:3 m_screenshot_widget->setCustomAspectRatio(4.0f / 3.0f); - m_screenshot_widget->m_x = m_area.getWidth()/2; - m_screenshot_widget->m_y = y1; - m_screenshot_widget->m_w = m_area.getWidth()/2; - m_screenshot_widget->m_h = y2 - y1 - 10; + m_screenshot_widget->m_x = m_area.getWidth()/2-20; + m_screenshot_widget->m_y = m_over_body + 10; - Track* track = (tracks.size() == 0 ? NULL : track_manager->getTrack(tracks[0])); + // Scale the picture to the biggest possible size without an overflow + if (m_lower_bound - m_over_body - 20 < m_area.getWidth()/2*3/4) + { + m_screenshot_widget->m_w = (m_lower_bound - m_over_body - 30)*4/3; + m_screenshot_widget->m_h = m_lower_bound - m_over_body - 30; + } + else + { + m_screenshot_widget->m_w = m_area.getWidth()/2; + m_screenshot_widget->m_h = m_area.getWidth()*3/8; // *(3/4)*(1/2) + } - m_screenshot_widget->m_properties[PROP_ICON] = (track != NULL ? - track->getScreenshotFile().c_str() : - file_manager->getAsset(FileManager::GUI,"main_help.png")); + Track* track = track_manager->getTrack(m_gp->getTrackNames()[0]); + m_screenshot_widget->m_properties[GUIEngine::PROP_ICON] = (track->getScreenshotFile().c_str()); m_screenshot_widget->setParent(m_irrlicht_window); m_screenshot_widget->add(); m_widgets.push_back(m_screenshot_widget); +} +// ---------------------------------------------------------------------------- +void GPInfoDialog::addButtons() +{ // ---- Start button - ButtonWidget* okBtn = new ButtonWidget(); - ButtonWidget* continueBtn = new ButtonWidget(); + GUIEngine::ButtonWidget* okBtn = new GUIEngine::ButtonWidget(); + GUIEngine::ButtonWidget* continueBtn = new GUIEngine::ButtonWidget(); SavedGrandPrix* saved_gp = SavedGrandPrix::getSavedGP( StateManager::get() ->getActivePlayerProfile(0) ->getUniqueID(), - gpIdent, + m_gp->getId(), race_manager->getDifficulty(), race_manager->getNumberOfKarts(), race_manager->getNumLocalPlayers()); - if (tracks.size() == 0) - { - okBtn->m_properties[PROP_ID] = "cannot_start"; - okBtn->setText(_("Sorry, no tracks available")); - } - else if (gp_ok) - { - okBtn->m_properties[PROP_ID] = "start"; - okBtn->setText(_("Start Grand Prix")); + okBtn->m_properties[PROP_ID] = "start"; + okBtn->setText(_("Start Grand Prix")); - continueBtn->m_properties[PROP_ID] = "continue"; - continueBtn->setText(_("Continue")); - } - else - { - okBtn->m_properties[PROP_ID] = "cannot_start"; - okBtn->setText(_("This Grand Prix is broken!")); - okBtn->setBadge(BAD_BADGE); - } + continueBtn->m_properties[PROP_ID] = "continue"; + continueBtn->setText(_("Continue")); - if (saved_gp && gp_ok) + if (saved_gp) { continueBtn->m_x = m_area.getWidth()/2 + 110; - continueBtn->m_y = y2; + continueBtn->m_y = m_lower_bound; continueBtn->m_w = 200; - continueBtn->m_h = m_area.getHeight() - y2 - 15; + continueBtn->m_h = m_area.getHeight() - m_lower_bound - 15; continueBtn->setParent(m_irrlicht_window); m_widgets.push_back(continueBtn); continueBtn->add(); @@ -185,9 +233,9 @@ GPInfoDialog::GPInfoDialog(const std::string& gpIdent, const float w, const floa okBtn->m_x = m_area.getWidth()/2 - 200; } - okBtn->m_y = y2; + okBtn->m_y = m_lower_bound; okBtn->m_w = 400; - okBtn->m_h = m_area.getHeight() - y2 - 15; + okBtn->m_h = m_area.getHeight() - m_lower_bound - 15; okBtn->setParent(m_irrlicht_window); m_widgets.push_back(okBtn); okBtn->add(); @@ -195,87 +243,57 @@ GPInfoDialog::GPInfoDialog(const std::string& gpIdent, const float w, const floa okBtn->getIrrlichtElement()->setTabGroup(false); okBtn->setFocusForPlayer( PLAYER_ID_GAME_MASTER ); - } -// ------------------------------------------------------------------------------------------------------ - -GPInfoDialog::~GPInfoDialog() -{ - // Place focus back on selected GP, in case the dialog was cancelled and we're back to - // the track selection screen after - Screen* curr_screen = GUIEngine::getCurrentScreen(); - if (curr_screen->getName() == "tracks.stkgui") - { - ((TracksScreen*)curr_screen)->setFocusOnGP(m_gp_ident); - } - -} - -// ------------------------------------------------------------------------------------------------------ +// ---------------------------------------------------------------------------- void GPInfoDialog::onEnterPressedInternal() { - // Save the gp identifier, since dismiss will delete this object. - std::string gp_id = m_gp_ident; + // Save the GP id because dismiss() will destroy this instance + std::string gp_id = m_gp->getId(); ModalDialog::dismiss(); // Disable accidentally unlocking of a challenge PlayerManager::getCurrentPlayer()->setCurrentChallenge(""); race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, false); } -// ------------------------------------------------------------------------------------------------------ +// ---------------------------------------------------------------------------- GUIEngine::EventPropagation GPInfoDialog::processEvent(const std::string& eventSource) { - if (eventSource == "start") + if (eventSource == "start" || eventSource == "continue") { // Save GP identifier, since dismiss will delete this object. - std::string gp_id = m_gp_ident; + std::string gp_id = m_gp->getId(); ModalDialog::dismiss(); - race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, false); + race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, + (eventSource == "continue")); return GUIEngine::EVENT_BLOCK; } - if (eventSource == "continue") - { - // Save GP identifier, since dismiss will delete this object. - std::string gp_id = m_gp_ident; - ModalDialog::dismiss(); - race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, true); - return GUIEngine::EVENT_BLOCK; - } - else if (eventSource == "cannot_start") - { - sfx_manager->quickSound( "anvil" ); - } return GUIEngine::EVENT_LET; } -// ------------------------------------------------------------------------------------------------------ +// ---------------------------------------------------------------------------- void GPInfoDialog::onUpdate(float dt) { - const int frameBefore = (int)(m_curr_time / 1.5f); + if (dt == 0) + return; // if nothing changed, return right now + m_curr_time += dt; int frameAfter = (int)(m_curr_time / 1.5f); - if (frameAfter == frameBefore) return; // if nothing changed, return right now - - const GrandPrixData* gp = grand_prix_manager->getGrandPrix(m_gp_ident); - assert(gp != NULL); - const std::vector<std::string> tracks = gp->getTrackNames(); + const std::vector<std::string> tracks = m_gp->getTrackNames(); if (frameAfter >= (int)tracks.size()) { frameAfter = 0; m_curr_time = 0; } - Track* track = (tracks.size() == 0 ? NULL : - track_manager->getTrack(tracks[frameAfter])); - std::string fn = track ? track->getScreenshotFile() - : file_manager->getAsset(FileManager::GUI, "main_help.png"); - m_screenshot_widget->setImage(fn.c_str(), IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); + Track* track = track_manager->getTrack(tracks[frameAfter]); + std::string file = track->getScreenshotFile(); + typedef GUIEngine::IconButtonWidget Icon; + m_screenshot_widget->setImage(file.c_str(), Icon::ICON_PATH_TYPE_ABSOLUTE); } -// ------------------------------------------------------------------------------------------------------ diff --git a/src/states_screens/dialogs/gp_info_dialog.hpp b/src/states_screens/dialogs/gp_info_dialog.hpp index b8959bf2d..d221d44d9 100644 --- a/src/states_screens/dialogs/gp_info_dialog.hpp +++ b/src/states_screens/dialogs/gp_info_dialog.hpp @@ -20,6 +20,10 @@ #define HEADER_GP_INFO_DIALOG_HPP #include "guiengine/modaldialog.hpp" +// Don't include grand_prix_data.hpp here or the compilation will fail + + +class GrandPrixData; namespace GUIEngine { @@ -32,23 +36,46 @@ namespace GUIEngine */ class GPInfoDialog : public GUIEngine::ModalDialog { - std::string m_gp_ident; +protected: // Necessary for RandomGPInfoDialog GUIEngine::IconButtonWidget* m_screenshot_widget; - float m_curr_time; - + GrandPrixData* m_gp; + + /** height of the separator over the body */ + int m_over_body; + /** height of the separator under the titlebar, which is equal to + * m_over_body in a normal GPInfoDialo and lower in RandomGPInfoDialog. */ + int m_under_title; + /** height of the seperator over the buttons */ + int m_lower_bound; + + void addTitle(); + /** \brief display all the tracks according to the current gp + * For a normal gp info dialog, it just creates a label for every track. + * But with a random gp info dialog, it tries to reuse as many + * labels as possible by just changing their text. */ + void addTracks(); + void addScreenshot(); + /** display a ok-button and eventually a continue-button */ + void addButtons(); + + /** only used for track_screen.cpp */ + GPInfoDialog() : ModalDialog(PERCENT_WIDTH, PERCENT_HEIGHT) {} + +private: + static const float PERCENT_WIDTH; + static const float PERCENT_HEIGHT; + public: - /** - * Creates a modal dialog with given percentage of screen width and height - */ - GPInfoDialog(const std::string& gpIdent, const float percentWidth, const float percentHeight); + GPInfoDialog(const std::string& gpIdent); + /** Places the focus back on the selected GP, in the case that the dialog + * was cancelled and we're returning to the track selection screen */ virtual ~GPInfoDialog(); - + void onEnterPressedInternal(); GUIEngine::EventPropagation processEvent(const std::string& eventSource); - - virtual void onUpdate(float dt); + virtual void onUpdate(float dt); }; #endif diff --git a/src/states_screens/dialogs/random_gp_dialog.cpp b/src/states_screens/dialogs/random_gp_dialog.cpp new file mode 100644 index 000000000..5a7406e5e --- /dev/null +++ b/src/states_screens/dialogs/random_gp_dialog.cpp @@ -0,0 +1,195 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 konstin +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "guiengine/engine.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" +#include "guiengine/widgets/spinner_widget.hpp" +#include "race/grand_prix_manager.hpp" +#include "race/race_manager.hpp" +#include "states_screens/dialogs/random_gp_dialog.hpp" +#include "tracks/track_manager.hpp" + +#include <IGUIEnvironment.h> +#include <IGUIStaticText.h> + +using irr::core::stringc; +using irr::core::stringw; +using irr::gui::IGUIStaticText; + +typedef GUIEngine::SpinnerWidget Spinner; + +RandomGPInfoDialog::RandomGPInfoDialog() +{ + // Defaults - loading selection from last time frrom a file would be better + m_number_of_tracks = 2; // We can assume that there are at least 2 standard tracks + m_trackgroup = "standard"; + m_use_reverse = NO_REVERSE; + + doInit(); + m_curr_time = 0.0f; + + m_under_title = m_area.getHeight()/7; + m_over_body = m_area.getHeight()/7 + SPINNER_HEIGHT + 10; // 10px space + m_lower_bound = m_area.getHeight()*6/7; + + // The GP manager is be used to make the GP live longer than this dialog + if (grand_prix_manager->m_random_gp) + { + delete grand_prix_manager->m_random_gp; + grand_prix_manager->m_random_gp = NULL; + } + m_gp = new GrandPrixData(m_number_of_tracks, m_trackgroup, m_use_reverse); + grand_prix_manager->m_random_gp = m_gp; + + addTitle(); + addSpinners(); + addTracks(); + addScreenshot(); + addButtons(); + addRestartButton(); +} + +// ---------------------------------------------------------------------------- + +void RandomGPInfoDialog::addSpinners() +{ + const int trackgroup_width = 200, laps_with = 150, reverse_width = 200; + const int left = (m_area.getWidth() - trackgroup_width - 150 - 250)/2; + + // Trackgroup chooser + Spinner* spinner = new Spinner(false); + spinner->m_properties[GUIEngine::PROP_ID] = "Trackgroup"; + spinner->m_properties[GUIEngine::PROP_WRAP_AROUND] = "true"; + spinner->setParent(m_irrlicht_window); + m_widgets.push_back(spinner); + spinner->add(); + spinner->move(left, m_under_title, trackgroup_width, SPINNER_HEIGHT); + // Fill it with all the track group names + spinner->addLabel("all"); + int index_standard; + const std::vector<std::string>& groups = track_manager->getAllTrackGroups(); + for (unsigned int i = 0; i < groups.size(); i++) + { + // FIXME: The NULL check is necessary until #1348 on github is fixed + if (groups[i].c_str() != NULL) + { + spinner->addLabel(stringw(groups[i].c_str())); + if(groups[i] == "standard") + index_standard = i+1; + } + } + // The value can only be set here because SpinnerWidget resets the value + // every time a label is added + spinner->setValue(index_standard); + + // Number of laps chooser + spinner = new Spinner(false); + spinner->setValue(m_number_of_tracks); + spinner->setMin(1); + spinner->setMax(track_manager->getTracksInGroup("standard").size()); + spinner->setParent(m_irrlicht_window); + spinner->m_properties[GUIEngine::PROP_ID] = "Number of tracks"; + spinner->m_properties[GUIEngine::PROP_WRAP_AROUND] = "true"; + m_widgets.push_back(spinner); + spinner->add(); + spinner->move(left + trackgroup_width + 10, m_under_title, laps_with, SPINNER_HEIGHT); + + // reverse choose + spinner = new Spinner(false); + spinner->setParent(m_irrlicht_window); + spinner->m_properties[GUIEngine::PROP_ID] = "reverse"; + spinner->m_properties[GUIEngine::PROP_WRAP_AROUND] = "true"; + m_widgets.push_back(spinner); + spinner->add(); + spinner->move(left + trackgroup_width + laps_with + 10, m_under_title, reverse_width, SPINNER_HEIGHT); + spinner->addLabel("no reverse"); + spinner->addLabel("all reverse"); + spinner->addLabel("mixed"); +} + +// ---------------------------------------------------------------------------- + +void RandomGPInfoDialog::addRestartButton() +{ + GUIEngine::IconButtonWidget* button = new GUIEngine::IconButtonWidget(); + button->setImage("gui/restart.png"); + button->setParent(m_irrlicht_window); + button->m_properties[GUIEngine::PROP_ID] = "reload"; + m_widgets.push_back(button); + button->add(); + button->move(m_area.getWidth() - 20 - 32, 20, 32, 32); +} + +// ---------------------------------------------------------------------------- + +GUIEngine::EventPropagation RandomGPInfoDialog::processEvent( + const std::string& eventSource) +{ + if (eventSource == "start") + { + ModalDialog::dismiss(); + race_manager->startGP(grand_prix_manager->m_random_gp, false, false); + return GUIEngine::EVENT_BLOCK; + } + else if (eventSource == "Number of tracks") + { + // The old gp can be reused because there's only track deletion/adding + m_number_of_tracks = getWidget<Spinner>("Number of tracks")->getValue(); + m_gp->changeTrackNumber(m_number_of_tracks, m_trackgroup); + addTracks(); + } + else if (eventSource == "Trackgroup") + { + Spinner* t = getWidget<Spinner>("Trackgroup"); + Spinner* s = getWidget<Spinner>("Number of tracks"); + + m_trackgroup = stringc(t->getStringValue()).c_str(); + + // Update the maximum for the number of tracks since it's depending on + // the current track. The current value in the Number-of-tracks-spinner + // has to be updated, since otherwise the displayed (and used) value + // can be bigger than the maximum. (Might be a TODO to fix this) + unsigned int max = (m_trackgroup == "all") ? + track_manager->getNumberOfTracks() : + track_manager->getTracksInGroup(m_trackgroup).size(); + m_number_of_tracks = std::min(max, m_number_of_tracks); + s->setMax(max); + if (s->getValue() > (signed)max) + s->setValue(max); + + delete m_gp; + m_gp = new GrandPrixData(m_number_of_tracks, m_trackgroup, m_use_reverse); + grand_prix_manager->m_random_gp = m_gp; + addTracks(); + } + else if (eventSource == "reverse") + { + Spinner* r = getWidget<Spinner>("reverse"); + m_use_reverse = static_cast<REVERSED>(r->getValue()); + m_gp->changeReverse(m_use_reverse); + } + else if (eventSource == "reload") + { + delete m_gp; + m_gp = new GrandPrixData(m_number_of_tracks, m_trackgroup, m_use_reverse); + grand_prix_manager->m_random_gp = m_gp; + addTracks(); + } + + return GUIEngine::EVENT_LET; +} + diff --git a/src/states_screens/dialogs/random_gp_dialog.hpp b/src/states_screens/dialogs/random_gp_dialog.hpp new file mode 100644 index 000000000..698217eec --- /dev/null +++ b/src/states_screens/dialogs/random_gp_dialog.hpp @@ -0,0 +1,53 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2014 konstin +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_RANDOM_GP_INFO_DIALOG_HPP +#define HEADER_RANDOM_GP_INFO_DIALOG_HPP + +#include "states_screens/dialogs/gp_info_dialog.hpp" + +#include <string> + +class RandomGPInfoDialog : public GPInfoDialog +{ +public: + enum REVERSED + { + NO_REVERSE = 0, + ALL_REVERSE = 1, + MIXED = 2 + }; +private: + unsigned int m_number_of_tracks; + std::string m_trackgroup; + REVERSED m_use_reverse; + +public: + static const int SPINNER_HEIGHT = 40; + + RandomGPInfoDialog(); + + /** Adds a SpinnerWidgets to choose the track groups, one to choose the + * number of tracks and one to choose if the tracks should be raced in + * reverse. The Spinners are centered. */ + void addSpinners(); + void addRestartButton(); + + GUIEngine::EventPropagation processEvent(const std::string& eventSource); +}; + +#endif diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index 2e84fd276..93695466e 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -1602,6 +1602,11 @@ void KartSelectionScreen::updateKartWidgetModel(uint8_t widget_id, kart_model.getWheelGraphicsPosition(2) ); w3->addModel( kart_model.getWheelModel(3), kart_model.getWheelGraphicsPosition(3) ); + for (size_t i = 0; i < kart_model.getSpeedWeightedObjectsCount(); i++) + { + const SpeedWeightedObject& obj = kart_model.getSpeedWeightedObject(i); + w3->addModel(obj.m_model, obj.m_position); + } w3->update(0); m_kart_widgets[widget_id].m_kart_name diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index b8a6e88da..9b5803421 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -92,7 +92,7 @@ void RaceResultGUI::init() // Calculate screenshot scrolling parameters const std::vector<std::string> tracks = - race_manager->getGrandPrix()->getTrackNames(); + race_manager->getGrandPrix().getTrackNames(); int n_tracks = tracks.size(); int currentTrack = race_manager->getTrackNumber(); m_start_track = currentTrack; @@ -208,7 +208,7 @@ void RaceResultGUI::enableAllButtons() void RaceResultGUI::eventCallback(GUIEngine::Widget* widget, const std::string& name, const int playerID) { - int n_tracks = race_manager->getGrandPrix()->getNumberOfTracks(); + int n_tracks = race_manager->getGrandPrix().getNumberOfTracks(); if (name == "up_button" && n_tracks > m_max_tracks && m_start_track > 0) { m_start_track--; @@ -1121,7 +1121,7 @@ void RaceResultGUI::enableGPProgress() status_label->m_h = font_height; status_label->add(); status_label->setText(_("Track %i/%i", currentTrack + 1, - race_manager->getGrandPrix()->getNumberOfTracks()), true); + race_manager->getGrandPrix().getNumberOfTracks()), true); addGPProgressWidget(status_label); y = (status_label->m_y + status_label->m_h + 5); @@ -1316,7 +1316,7 @@ void RaceResultGUI::displayHighScores() void RaceResultGUI::displayScreenShots() { const std::vector<std::string> tracks = - race_manager->getGrandPrix()->getTrackNames(); + race_manager->getGrandPrix().getTrackNames(); int currentTrack = race_manager->getTrackNumber(); int n_sshot = 1; diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp index b00cd47b4..94f715553 100644 --- a/src/states_screens/tracks_screen.cpp +++ b/src/states_screens/tracks_screen.cpp @@ -29,6 +29,7 @@ #include "race/grand_prix_manager.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/dialogs/gp_info_dialog.hpp" +#include "states_screens/dialogs/random_gp_dialog.hpp" #include "states_screens/dialogs/track_info_dialog.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" @@ -121,9 +122,16 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name, gps_widget->getSelectionIDString(PLAYER_ID_GAME_MASTER); if (selection == "locked") + { unlock_manager->playLockSound(); + } else - new GPInfoDialog(selection, 0.8f, 0.7f); + { + if (selection == "Random Grand Prix") + new RandomGPInfoDialog(); + else + new GPInfoDialog(selection); + } } else if (name == "trackgroups") { @@ -214,12 +222,13 @@ void TracksScreen::init() } } - /*// Random GP - not finished yet + // Random GP std::vector<std::string> screenshots; - screenshots.push_back("gui/main_help.png"); - gps_widget->addAnimatedItem(translations->fribidize("Random"), "Random", + screenshots.push_back(file_manager->getAsset(FileManager::GUI, "main_help.png")); + gps_widget->addAnimatedItem(translations->fribidize("Random Grand Prix"), + "Random Grand Prix", screenshots, 1.5f, 0, - IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);*/ + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); gps_widget->updateItemDisplay(); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 9f81172e2..d7d9cbd7d 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -523,10 +523,10 @@ void Track::loadTrackInfo() delete root; std::string dir = StringUtils::getPath(m_filename); - std::string easter_name = dir+"/easter_eggs.xml"; + std::string easter_name = dir + "/easter_eggs.xml"; XMLNode *easter = file_manager->createXMLTree(easter_name); - + if(easter) { for(unsigned int i=0; i<easter->getNumNodes(); i++) @@ -934,7 +934,7 @@ bool Track::loadMainTrack(const XMLNode &root) { mesh = irr_driver->getMesh(full_path); } - + if(!mesh) { Log::fatal("track", @@ -1969,7 +1969,7 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, ModelDefin libroot = library_nodes[name]; create_lod_definitions = false; // LOD definitions are already created, don't create them again } - + scene::ISceneNode* parent = irr_driver->getSceneManager()->addEmptySceneNode(); parent->setPosition(xyz); parent->setRotation(hpr);