Merged with master and solved conflict with the new graph structure

This commit is contained in:
Elderme 2016-01-07 14:56:09 +01:00
commit c14ce23e66
163 changed files with 9972 additions and 3776 deletions

1
.gitignore vendored
View File

@ -9,6 +9,7 @@ stk-editor/
.config/
supertuxkart-64
make*.bat
__pycache__
data/editor
data/karts

View File

@ -1,6 +1,6 @@
# root CMakeLists for the SuperTuxKart project
project(SuperTuxKart)
set(PROJECT_VERSION "0.8.1")
set(PROJECT_VERSION "0.9.1")
cmake_minimum_required(VERSION 2.8.4)
if(NOT (CMAKE_MAJOR_VERSION VERSION_LESS 3))
@ -15,7 +15,6 @@ if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "STKRelease")
endif()
option(USE_WIIUSE "Support for wiimote input devices" ON)
option(USE_FRIBIDI "Support for right-to-left languages" ON)
option(CHECK_ASSETS "Check if assets are installed in ../stk-assets" ON)
option(USE_SYSTEM_ANGELSCRIPT "Use system angelscript instead of built-in angelscript. If you enable this option, make sure to use a compatible version." OFF)
@ -32,7 +31,9 @@ else()
endif()
if(MINGW OR CYGWIN)
set(USE_WIIUSE OFF)
option(USE_WIIUSE "Support for wiimote input devices" OFF)
else()
option(USE_WIIUSE "Support for wiimote input devices" ON)
endif()
if(UNIX AND NOT APPLE)

Binary file not shown.

View File

@ -4,6 +4,8 @@
<card is="Intel(R) HD Graphics 3000" os="windows" disable="AdvancedPipeline"/>
<card is="Intel(R) HD Graphics 3000" os="windows" disable="FramebufferSRGBWorking"/>
<card contains="Intel" os="osx" disable="GI"/>
<card contains="Intel" os="linux" version="<12.0" disable="ComputeShader"/>
<card contains="Intel" os="linux" version="<12.0" disable="FramebufferSRGBCapable"/>
<card contains="Intel" disable="TextureCompressionS3TC"/>
<card contains="Intel" os="windows" disable="HighDefinitionTextures"/>
<card contains="NVIDIA" os="windows" version="<344.65" disable="BufferStorage"/>

View File

@ -40,9 +40,12 @@
<label id="highscore3" proportion="1" text="(Empty)"/>
</div>
<spacer width="1" height="10%"/>
<spacer width="1" height="2%"/>
<label id="author" width="100%" text_align="center" word_wrap="true"/>
<spacer width="1" height="10%"/>
<label id="max-arena-players" width="100%" text_align="center" word_wrap="true"/>
</div>
</box>

View File

@ -0,0 +1,404 @@
<?xml version="1.0"?>
<!-- Format
It is possible to give relative values (factors and more) for each property.
The format as a regex is: ([+-*/](%d+|x))*
An empty value means no change, a value without sign or with a '-' replaces
the previous value. If an operator prefixes the string, e.g. *1.5 (- doesn't
work because it's a sign). x will get replaced by the previous value which
means *1.5 is equal to x*1.5. It's also possible to combine operations:
+1.5*20 means "add 1.5 to the previous value and then multiply by 20".
Note that it's computed sequentially from left to right and there is no
operator precedence. Also whitespaces aren't allowed because they are used
to split arrays.
If you want to return the square of something use x*x or only *x.
ATTENTION: '-' is a special case if it is the first character of a number-
string. It means that the number is negative and NOT that the following
float will be substracted from the base value. So if x = 10 and the string
"-5" is processed, the result will be -5 and not the same as "x-5", which
would result in 10 - 5 = 5.
-->
<characteristics>
<!-- The default values that can be modified by the difficulty, karts, etc.
The values here should also be the values of the maximum difficulty. -->
<characteristic name="base">
<!-- ********** Physics ********** -->
<!-- Suspension
stiffness: kart's suspension stiffness.
rest: Length of suspension when at rest.
travel-cm: maximum movement of suspension - in cm!!
exp-string-response: dampen the suspension spring reaction
exponentially. See
http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=7369\&p=25236&hilit=vehicle#p25236
for details.
max-force: Maximum suspension force -->
<suspension stiffness="140" rest="0.3" travel="0.29"
exp-spring-response="false" max-force="12000" />
<!-- Values related to stability of the chassis: damping, and reduced
impact of roll.
downward-impulse-factor: A speed proportional impulse applied each
frame that pushes the vehicle onto the ground.
track-connection-accel: An artificial force that pulls a wheel to
the ground if its off ground. Reduces the affect if a kart loses
contact with the ground (i.e. it then can't steer or accelerate
anymore).
smooth-flying-impulse: apply a torque impulse to flying kart to keep
them parallel to the ground. -->
<stability roll-influence="0.3"
chassis-linear-damping="0.2"
chassis-angular-damping="0"
downward-impulse-factor="5"
track-connection-accel="2"
smooth-flying-impulse="250" />
<!-- Turning
radius: The turn radius of the kart at
a given speed. The actual steering angle is dependent on the
wheel base of the kart: radius = wheel_base/sin(steering_angle).
The values below define that at speed 0 the turn radius is 2, at
speed 10 the radius is 7.5 etc.
The actual turn radius is piece-wise linearly interpolated. This
allows for tighter turning at lower speeds, and also avoids that
the kart becomes too hard to control at high speed (speeds of
higher than 23 can only be reached with powerups).
time-full-steer: This is the amount of change in steering depending
on current steering. So if the steering is between 0 and 0.5,
the time-for-steering-change is 0.15. If the current steering is
between 0.5 and 1.0, the time-for-steering-change is 0.25.
The speed is used as dt/time-for-steering-change.
In short: steering at less than halfway is somewhat faster,
which should avoid oversteering (by pressing the key for too long),
but slower when you want to steer more. Overwall with the current
settings the expected time-to-full-steer is:
0.5 * 0.25 + 0.5 * 0.15 = 0.2 ... which is overall the same
time we had previously. -->
<turn radius="0:2.0 10:7.5 25:15 45:30"
time-full-steer="0:0.17 0.5:0.17 0.5:0.28 1.0:0.28"
time-reset-steer="0.1" />
<!-- Speed and acceleration
power: The power of the kart
max-speed: The base maximum speed of the kart in m/s
brake-factor: Value used when braking.
brake-time-increase: The brake force is multiplied by
(1 + brake_time) * brake_time_increase - i.e. the longer the
brake was pressed, the harder the kart will brake.
max-speed-reverse-ratio is the percentage of max speed for reverse
gear.
-->
<engine power="875" max-speed="25" brake-factor="15"
brake-time-increase="6" max-speed-reverse-ratio="0.65" />
<!-- Simulated gears
switch-ratio defines at what ratio of the maximum
speed what gear is selected, e.g. 0.25 means that if the speed is
bigger or equal to 0.25 * maxSpeed then use gear 1, 0.5 means if
the speed is bigger or equal to 0.5 x maxSpeed then gear 2.
gear-power-increase contains the increase in max power (to simulate
different gears), e.g. 2.5 as first entry means: 2.5 * maxPower in gear 1
| first | second | third | . -->
<gear switch-ratio="0.25 0.7 1.0" power-increase="2.2 1.7 1.3" />
<!-- Mass of a kart -->
<mass value="350" />
<!-- Wheels
damping-relaxation/compression: for bullet
damping parameters
Radius and width of wheel
front-right, front-left, rear-right and rear-left give the
position of the physics raycast wheels relative to the center of
gravity. Default is to use the corners of the chassis to attach
the wheels to. -->
<wheels damping-relaxation="35" damping-compression="5">
<front-right position="0.38 0 0.6" />
<front-left position="-0.38 0 0.6" />
<rear-right position="0.38 0 -0.6" />
<rear-left position="-0.38 0 -0.6" />
</wheels>
<!-- ********** Visuals ********** -->
<!-- Skidding
increase: multiplicative increase of skidding factor in each frame.
decrease: multiplicative decrease of skidding factor in each frame.
max: maximum skidding factor = maximum increase of steering angle.
time-till-max: Time till maximum skidding is reached.
visual: Additional graphical rotation of kart. The graphical rotation
of the kart also determines the direction the kart is driving to
when skidding is stopped.
visual-time: How long it takes for the visual skid to reach maximum.
revert-visual-time: how long it takes when stopping a skid to revert
the visual skid and bring visuals and physics in sync again.
angular-velocity: Angular velocity to be used for the kart when skidding.
min-speed: Minimum speed a kart must have before it can skid. Must be
>0, otherwise the kart can skid at the start of the race.
time-till-bonus: How long a kart needs to skid in order to get a bonus.
bonus-force: A speedup applied to the kart whick skidded for a while.
bonus-time: How long the bonus-force is applied.
bonus-force: Additional engine force (this is used to offset the fact
that turning after skidding (e.g. to correct direction) often uses
up the skid bonus).
post-skid-rotate-factor: a factor to be used to determine how much
the chassis of a kart should rotate to match the graphical view.
A factor of 1 is identical, a smaller factor will rotate the kart
less (which might feel better).
physical-jump-time: Time for a physical jump at the beginning of a skid.
graphical-jump-time: Time for a graphics-only jump at the beginning
of a skid.
reduce-turn-min/max: The steering done by the controller (which is in
[-1,1]) is mapped to [reduce-turn-min, reduce-turn-max] when skidding
is active (for left turn, right turn will use [-max, -min]). The
effect is that while you skid (say left) you can adjust the direction
of the turn the kart is doing somewhat by steering to the left and right,
but you will always keep on doing a left turn, just more or less. -->
<skid increase="1.05" decrease="0.95" max="2.5" time-till-max="0.5"
visual="1.25" visual-time="0.7" revert-visual-time="0.7"
min-speed="10" time-till-bonus="1.0 3.0"
bonus-speed="4.5 6.5" bonus-time="3.0 4.0"
bonus-force="250 350"
physical-jump-time="0" graphical-jump-time="0.4"
post-skid-rotate-factor="1"
reduce-turn-min="0.2" reduce-turn-max="0.8" enabled="true" />
<!-- Camera
Distance between kart and camera.
forward-up-angle: Angle between camera and plane of kart (pitch)
when the camera is pointing forward
backward-up-angle: Angle between camera and plane of kart (pitch)
when the camera is pointing backwards. This is usually
larger than the forward-up-angle, since the kart itself
otherwise obstricts too much of the view. -->
<camera distance="1.0" forward-up-angle="15"
backward-up-angle="5" />
<!-- Jump animation
animation-time: only if the estimated time for a jump is larger
than this value will the jump animation being
shown. -->
<jump animation-time="0.5" />
<!-- Leaning related parameters, i.e. slightly leaning the karts when
driving a fast curve.
max: maximum leaning (i.e. when steering as much as possible at highest
speed), in degrees.
speed: Speed with which the leaning changes (in degree/second). -->
<lean max="8.6" speed="5.0" />
<!-- ********** Items ********** -->
<!-- Anvil
weight: The additional weight an anvil adds to a kart.
speed-factor: The additional slowdown caused by the anvil.
duration: The time an anvil is active. -->
<anvil duration="2.0" weight="150" speed-factor="0.2" />
<!-- Parachute
friction: The friction increase when a parachute is attached.
duration: The time an attached parachute is active
duration-other: The time a parachute attached from other kart works
lbound-fraction: The lower bound fraction of speed when lost will
detach parachute. E.g. at nearly 0 speed, only 5% of speed
need to be lost.
ubound-fraction: The upper bound fraction of speed when lost will
detach parachute. E.g. at max-speed 30% of speed must be lost.
max-speed: A factor that decides the impact of rate of speed
(distance between bounds) -->
<parachute friction="2.0" duration="4.0" duration-other="8.0"
lbound-fraction="0.95" ubound-fraction="0.7" max-speed="23" />
<!-- Bubblegum
duration: How long the bubblegum lasts.
speed-fraction: To what fraction of top-speed the speed is reduced.
torque: To rotate the kart somewhat.
fade-in-time: How quick the slowdown takes effect.
shield-duration: How long the bubblegum shield lasts -->
<bubblegum duration="1" speed-fraction="0.3" torque="500" fade-in-time="0.01"
shield-duration="10.0" />
<!-- Zipper
duration: Time a zipper is active.
force: Additional zipper force.
speed-gain: One time additional speed.
max-speed-increase: 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 duration="3.5" force="250.0" speed-gain="4.5" max-speed-increase="15"
fade-out-time="1.0" />
<!-- Swatter
duration: How long can the swatter be active.
distance: How close a kart or an item must be before it can be hit.
This is the square of the real distance to accelerate the
compution.
squash-duration: How long a kart will remain squashed.
squash-slowdown: Percentage of max speed that a kart is
restricted to. -->
<swatter duration="10" distance="9" squash-duration="5"
squash-slowdown="0.5" />
<!-- Plunger and rubber band handling
band-max-length: The maximum length of rubber band before it snaps.
band-force: The force a plunger/rubber band applies to the kart(s).
band-duration: The duration a rubber band acts.
in-face-time determines how long it takes before a plunger
in your face is removed. -->
<plunger band-max-length="50" band-force="1500" band-duration="1"
band-speed-increase="7" band-fade-out-time="3"
in-face-time="4.5" />
<!-- ********** Miscellaneous ********** -->
<!-- Startup
If a kart starts within the specified time after 'go',
it receives the corresponding bonus from 'boost'. Those
fields must have the same size, and must be sorted by
increasing times. -->
<startup time="0.3 0.5"
boost="6 3" />
<!-- Rescue
duration: How long it takes the kart to be raised.
height: how height the kart will be raised before it is
dropped back onto the track.
vert rescue offset: used to raise karts a bit higher before
releasing them on the ground after a rescue. Used to avoid
resetting karts into the track. Not sure if this is still
necessary. -->
<rescue vert-offset="0.0" duration="1.2" height="2" />
<!-- Explosion
duration: how long it takes before the kart can drive again (this
determines how height the kart is being thrown).
invulnerability-time: how long a kart will be invulnerable
after being hit by an explosion.
radius: Kart closer to this value will be affected by
an explosion as well. -->
<explosion duration="2" radius="5"
invulnerability-time="6" />
<!-- Nitro
engine-force: additional engine power
consumption: nitro consumption - heavier characters can be set
to need more nitro than lighter character.
small-container: how much energy a small container gives.
big-container: how much energy a big container gives.
max-speed-increase: How much the speed of a kart might exceed
its maximum speed (in m/s).
duration: How long the increased speed will be valid after
the kart stops using nitro (and the fade-out-time starts).
fade-out-time: Duration during which the increased maximum
speed due to nitro fades out.
max: How much nitro a kart can store. -->
<nitro engine-force="600" consumption="1" small-container="1" big-container="3"
max-speed-increase="5" duration="1" fade-out-time="2" max="20" />
<!-- Slipstream
length: How far behind a kart slipstream works
width: how wide slipstream works furthest away from the kart.
collect-time: How many seconds of sstream give maximum benefit
use-time: How long the benefit will last.
add-power: Additional power due to sstreaming. 1 = +100%
min-speed: Minimum speed necessary for slipstream to take effect.
max-speed-increase: How much the speed of the kart might exceed
its normal maximum speed.
duration: How long the higher speed lasts after slipstream stopped
working.
fade-out-time: How long the slip stream speed increase will
gradually be reduced. -->
<slipstream length="10" width="2" collect-time="2" use-time="5"
add-power="3" min-speed="10" max-speed-increase="5"
duration="1" fade-out-time="2" />
</characteristic>
<!-- The different difficulties (like easy, medium, hard) -->
<difficulties>
<characteristic name="easy">
<engine power="*0.66" max-speed="*0.6" />
<plunger in-face-time="3" />
</characteristic>
<characteristic name="medium">
<engine power="*0.77" max-speed="*0.8" />
<plunger in-face-time="4" />
</characteristic>
<characteristic name="hard">
<engine power="*0.89" max-speed="*0.92" />
</characteristic>
<!-- This doesn't need to be changed because the most fast/heavy/extreme
values should also be the default ones. -->
<characteristic name="best" />
</difficulties>
<!-- The different kart types, that can be specified in the kart.xml file -->
<kart-types>
<characteristic name="light">
<engine power="*0.46" max-speed="*0.92" brake-factor="*1"
max-speed-reverse-ratio="*0.77" />
<gear switch-ratio="0.20 0.55 1" power-increase="5 4 3" />
<mass value="*0.56" />
<startup time="0.3 0.5"
boost="8.5 4.5" />
<explosion time="2.1" radius="5.5"
invulnerability-time="7" />
<nitro engine-force="350" max-speed-increase="4.5" duration="1.5"
fade-out-time="2.5" />
<slipstream length="11" collect-time="1.5" use-time="2.5" add-power="3.2"
min-speed="9" max-speed-increase="4" duration="1.2"
fade-out-time="2.3" />
</characteristic>
<characteristic name="medium">
<engine power="*0.63" max-speed="*1" brake-factor="*0.73"
max-speed-reverse-ratio="*0.62" />
<gear switch-ratio="0.30 0.7 1.0" power-increase="2.2 2.2 2.5" />
<mass value="*0.71" />
<startup time="0.3 0.5"
boost="4.2 2.6" />
<explosion time="1.8" radius="5"
invulnerability-time="6" />
<nitro engine-force="425" consumption="1.4" duration="1" />
<slipstream use-time="3.3" add-power="2.8" duration="0.9"
fade-out-time="1.6" />
</characteristic>
<characteristic name="heavy">
<engine power="*1" max-speed="*1" brake-factor="*0.66"
max-speed-reverse-ratio="*1" />
<gear switch-ratio="0.45 0.70 1" power-increase="1.5 1.7 2.5" />
<mass value="*1" />
<swatter duration="10" distance="3" squash-duration="5"
squash-slowdown="0.5" />
<startup time="0.3 0.5"
boost="3.8 2" />
<explosion time="1.5" radius="4"
invulnerability-time="6" />
<nitro engine-force="600" consumption="2" max-speed-increase="8"
duration="0.7" fade-out-time="1.3" />
<slipstream length="8.5" use-time="4" add-power="2.7" min-speed="10.5"
max-speed-increase="8" duration="0.7" fade-out-time="1" />
</characteristic>
</kart-types>
<!-- Per-player settings/handicaps (or boosts) -->
<player-characteristics>
<characteristic name="normal" />
<characteristic name="handicap">
<engine brake-factor="*0.8" brake-time-increase="*0.85" max-speed-reverse-ratio="*0.8" />
<bubblegum duration="*1.5" speed-fraction="*1.5" torque="*1.5" />
<zipper duration="*0.8" force="*0.8" speed-gain="*0.8" max-speed-increase="*0.8" />
<swatter duration="*0.8" squash-duration="*1.5" squash-slowdown="*1.8" />
<plunger band-max-length="*0.8" band-speed-increase="*0.8" in-face-time="*1.3" />
<startup time="*0.8 0.8" boost="*0.8 0.8" />
<rescue duration="*1.5" />
<explosion duration="*1.3" invulnerability-time="*0.7" />
<nitro engine-force="*0.8" consumption="*1.1" max-speed-increase="*0.9" max="*0.8" />
<slipstream length="*0.8" width="*0.8" collect-time="*1.5" use-time="*0.8"
add-power="*0.8" min-speed="*0.8" max-speed-increase="*0.9" duration="*0.8" />
</characteristic>
</player-characteristics>
</characteristics>

View File

@ -85,24 +85,6 @@
<!-- How long the music credits are shown. -->
<credits music="10"/>
<!-- weight is the additional weight an anvil adds to a kart.
speed-factor is the additional slowdown caused by the anvil.
time is the time an anvil is active. -->
<anvil time="2.0" weight="150" speed-factor="0.2"/>
<!-- friction is the friction increase when a parachute is attached.
time is the time an attached parachute is active
time-other is the time a parachute attached from other kart works
lbound-fraction is the lower bound fraction of speed when lost will
detach parachute. E.g. at nearly 0 speed, only 5% of speed
need to be lost.
ubound-fraction is the upper bound fraction of speed when lost will
detach parachute. E.g. at max-speed 30% of speed must be lost.
max-speed is a factor that decides the impact of rate of speed
(distance between bounds) -->
<parachute friction="2.0" time="4.0" time-other="8.0"
lbound-fraction="0.95" ubound-fraction="0.7" max-speed="23"/>
<!-- time is the time till a bomb explodes. time-increase is the time added
to timer when bomb is passed on. -->
<bomb time="30.0" time-increase="-5.0"/>
@ -150,22 +132,6 @@
============================ -->
<general-kart-defaults>
<!-- Camera: Distance between kart and camera.
forward-up-angle: Angle between camera and plane of kart (pitch)
when the camera is pointing forward
backward-up-angle: Angle between camera and plane of kart (pitch)
when the camera is pointing backwards. This is usually
larger than the forward-up-angle, since the kart itself
otherwise obstricts too much of the view. -->
<camera distance="1.0" forward-up-angle="15"
backward-up-angle="5"/>
<!-- Jump animation related values:
animation-time: only if the estimated time for a jump is larger
than this value will the jump animation being
shown. -->
<jump animation-time="0.5" />
<!-- Skidding: increase: multiplicative increase of skidding factor in each frame.
decrease: multiplicative decrease of skidding factor in each frame.
max: maximum skidding factor = maximum increase of steering angle.
@ -339,69 +305,6 @@
/>
</ai>
<!-- The per-player difficulties in multiplayer games.
These values are multiplied with the current values of a car
so 1 means that nothing changes.
The meaning of the different values is explained below this tag. -->
<difficulties>
<normal>
<mass value="1.0"/>
<engine brake-factor="1.0" brake-time-increase="1.0" max-speed-reverse-ratio="1.0" power="1.0" max-speed="1.0"/>
<nitro consumption="1.0" max-speed-increase="1.0" duration="1.0" fade-out-time="1.0"/>
<bubblegum time="1.0" speed-fraction="1.0" torque="1.0" fade-in-time="1.0"/>
<rescue time="1.0"/>
<explosion time="1.0" radius="1.0" invulnerability-time="1.0"/>
<slipstream length="1.0" width="1.0" collect-time="1.0" use-time="1.0"
add-power="1.0" min-speed="1.0" max-speed-increase="1.0" duration="1.0"
fade-out-time="1.0"/>
<plunger band-max-length="1.0" band-force="1.0" band-duration="1.0"
band-speed-increase="1.0" band-fade-out-time="1.0" in-face-time="1.0"/>
<zipper time="1.0" force="1.0" speed-gain="1.0" max-speed-increase="1.0"
fade-out-time="1.0"/>
<swatter duration="1.0" squash-duration="1.0" squash-slowdown="1.0"/>
<startup time="1.0 1.0" boost="1.0 1.0"/>
</normal>
<handicap>
<mass value="1.0"/>
<engine brake-factor="0.8" brake-time-increase="0.85" max-speed-reverse-ratio="0.8" power="0.8" max-speed="0.8"/>
<nitro consumption="1.1" max-speed-increase="0.9" duration="1.0" fade-out-time="1.0"/>
<bubblegum time="1.5" speed-fraction="1.5" torque="1.5" fade-in-time="1.0"/>
<rescue time="1.5"/>
<explosion time="1.3" invulnerability-time="0.7"/>
<slipstream length="0.8" width="0.8" collect-time="1.5" use-time="0.8"
add-power="0.8" min-speed="0.8" max-speed-increase="0.9" duration="0.8"
fade-out-time="1.0"/>
<plunger band-max-length="0.8" band-force="1.0" band-duration="1.0"
band-speed-increase="0.8" band-fade-out-time="1.0" in-face-time="1.3"/>
<zipper time="0.8" force="0.8" speed-gain="0.8" max-speed-increase="0.8"
fade-out-time="1.0"/>
<swatter duration="0.8" squash-duration="1.5" squash-slowdown="1.8"/>
<startup time="0.8 0.8" boost="0.8 0.8"/>
</handicap>
</difficulties>
<!-- Suspension related values. stiffness: kart's suspension stiffness.
rest: Length of suspension when at rest.
travel: maximum movement of suspension!!
exp-string-response: dampen the suspension spring reaction
exponentially.
max-force: Maximum suspension force -->
<suspension stiffness="140" rest="0.3" travel="0.29"
exp-spring-response="false" max-force="12000"/>
<!-- Wheel related parameters: damping-relaxation/compression: for
bullet, damping parameters.
front-right, front-left, rear-right and rear-left give the
position of the physics raycast wheels relative to the center of
gravity. Default is to use the corners of the chassis to attach
the wheels to. -->
<wheels damping-relaxation="35" damping-compression="5">
<front-right position="0.38 0 0.6" />
<front-left position="-0.38 0 0.6" />
<rear-right position="0.38 0 -0.6" />
<rear-left position="-0.38 0 -0.6"/>
</wheels>
<!-- Parameters for the speed-weighted objects:
a bigger value for strength-factor leads to the speed of the kart more quickly affecting
the strength of the animation (up to a maximum value that corresponds to the original animation) -->
@ -411,23 +314,6 @@
(like 10000000) disables bullet skidding. -->
<friction slip="10000000"/>
<!-- Values related to stability of the chassis: damping, and reduced
impact of roll.
downward-impulse-factor: A speed proportional impulse applied each
frame that pushes the vehicle onto the ground.
track-connection-accel: An artificial force that pulls a wheel to
the ground if its off ground. Reduces the affect if a kart loses
contact with the ground (i.e. it then can't steer or accelerate
anymore).
smooth-flying-impulse: apply a torque impulse to flying kart to keep
them parallel to the ground.-->
<stability roll-influence="0.3"
chassis-linear-damping="0.2"
chassis-angular-damping="0"
downward-impulse-factor="5"
track-connection-accel="2"
smooth-flying-impulse="250"/>
<!-- collision
impulse-type: STK can apply an additional impulse in case of
kart-track collision:
@ -468,58 +354,6 @@
impulse="3000" impulse-time="0.1" terrain-impulse="1600"
restitution="1.0" bevel-factor="0.5 0.0 0.7"
physical-wheel-position="-1" />
<!-- If a kart starts within the specified time after 'go',
it receives the corresponding bonus from 'boost'. Those
fields must have the same size, and must be sorted by
increasing times. -->
<startup time = "0.3 0.5"
boost = "6 3" />
<!-- Rescue: time: How long it takes the kart to be raised.
height: how height the kart will be raised before it is
dropped back onto the track.
vert rescue offset: used to raise karts a bit higher before
releasing them on the ground after a rescue. Used to avoid
resetting karts into the track. Not sure if this is still
necessary. -->
<rescue vert-offset="0.0" time="1.2" height="2"/>
<!-- Nitro: engine-force: additional engine power
consumption: nitro consumption - heavier characters can be set
to need more nitro than lighter character.
small-container: how much energy a small container gives.
big-container: how much energy a big container gives.
max-speed-increase: How much the speed of a kart might exceed
its maximum speed (in m/s).
duration: How long the increased speed will be valid after
the kart stops using nitro (and the fade-out-time starts).
fade-out-time: Duration during which the increased maximum
speed due to nitro fades out.
max: How much nitro a kart can store.
-->
<nitro engine-force="500" consumption="1" small-container="1" big-container="3"
max-speed-increase="5" duration="1" fade-out-time="2" max="20"/>
<!-- Bubble gum data:
time: How long the bubblegum lasts.
speed-fraction: To what fraction of top-speed the speed is reduced.
torque: To rotate the kart somewhat.
fade-in-time: How quick the slowdown takes effect.
-->
<bubblegum time="1" speed-fraction="0.3" torque="500" fade-in-time="0.01"/>
<!-- time: Time a zipper is active.
force: Additional zipper force.
speed-gain: One time additional speed.
max-speed-increase: 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.
@ -562,34 +396,6 @@
post-skid-rotate-factor="1"
reduce-turn-min="0.2" reduce-turn-max="0.8"/>
<!-- Slipstream: length: How far behind a kart slipstream works
width: how wide slipstream works furthest away from the kart.
collect-time: How many seconds of sstream give maximum benefit
use-time: How long the benefit will last.
add-power: Additional power due to sstreaming. 1 = +100%
min-speed: Minimum speed necessary for slipstream to take effect.
max-speed-increase: How much the speed of the kart might exceed
its normal maximum speed.
duration: How long the higher speed lasts after slipstream stopped
working.
fade-out-time: How long the slip stream speed increase will
gradually be reduced. -->
<slipstream length="10" width="2" collect-time="2" use-time="5"
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 active.
distance: How close a kart or an item must be before it can be hit.
squash-duration: How long a kart will remain squashed.
squash-slowdown: percentage of max speed that a kart is
restricted to. -->
<swatter duration="10" distance="3" squash-duration="5"
squash-slowdown="0.5"/>
<!-- Leaning related parameters, i.e. slightly leaning the karts when
driving a fast curve.
max: maximum leaning (i.e. when steering as much as possible at highest
@ -597,156 +403,10 @@
sped: Speed with which the leaning changes (in degree/second). -->
<lean max="8.6" speed="5.0" />
<!-- turn-radius defines the turn radius of the kart at
a given speed. The actual steering angle is dependent on the
wheel base of the kart: radius = wheel_base/sin(steering_angle).
The values below define that at speed 0 the turn radius is 2, at
speed 10 the radius is 7.5 etc.
The actual turn radius is piece-wise linearly interpolated. This
allows for tighter turning at lower speeds, and also avoids that
the kart becomes too hard to control at high speed (speeds of higher
than 23 can only be reached with powerups).
time-full-steer: This is the amount of change in steering depending
on current steering. So if the steering is between 0 and 0.5,
the time-for-steering-change is 0.15. If the current steering is
between 0.5 and 1.0, the time-for-steering-change is 0.25.
The speed is used as dt/time-for-steering-change.
In short: steering at less than halfway is somewhat faster,
which should avoid oversteering (by pressing the key for too long),
but slower when you want to steer more. Overwall with the current
settings the expected time-to-full-steer is:
0.5 * 0.25 + 0.5 * 0.15 = 0.2 ... which is overall the same
time we had previously.
-->
<turn turn-radius="0:2.0 10:7.5 25:15 45:30"
time-full-steer ="0:0.15 0.5:0.15 0.5:0.25 1.0:0.25"
time-reset-steer="0.1" />
<!-- Speed and acceleration related values: power and max-speed (in m/s)
have 4 values, one for low, medium, hard, and supertux.
brake-factor: Value used when braking.
brake-time-increase: The brake force is multiplied by
(1+brake_time*brake_time_increase - i.e. the longer the brake was
pressed, the harder the kart will brake.
max-speed-reverse-ratio is the percentage of max speed for reverse gear.
-->
<engine power="450 475 500 510" max-speed="17 21 23 25" brake-factor="11.0"
brake-time-increase="6"
max-speed-reverse-ratio="0.3"/>
<!-- Simulated gears: switch-ratio defines at what ratio of the maximum
speed what gear is selected, e.g. 0.25 means that if the speed is
bigger or equal to 0.25 x maxSpeed then use gear 1, 0.5 means if
the speed is bigger or equal to 0.5 x maxSpeed then gear 2.
gear-power-increase contains the increase in max power (to simulate
different gears), e.g. 2.5 as first entry means: 2.5*maxPower in gear 1
| first | second | third | . -->
<gear switch-ratio="0.25 0.7 1.0" power-increase="2.2 1.7 1.3"/>
<!-- mass -->
<mass value="225"/>
<!-- Kart-specific plunger and rubber band handling: max-length is
the maximum length of rubber band before it snaps. force is
the force a plunger/rubber band applies to the kart(s).
duration is the duration a rubber band acts.
in-face-time determines how long it takes before a plunger
in your face is removed. -->
<plunger band-max-length="50" band-force="1500" band-duration="1"
band-speed-increase="7" band-fade-out-time="3"
in-face-time="3 4 4.5 4.5"/>
<!-- Kart-specific explosion parameters.
Time: how long it takes before the kart can drive again (this
determines how height the kart is being thrown).
Invulnerability-time: how long a kart will be invulnerable
after being hit by an explosion.
radius: Kart closer to this value will be affected by
an explosion as well. -->
<explosion time="2" radius="5"
invulnerability-time="6" />
<kart-type>
<light>
<startup time = "0.3 0.5"
boost = "8.5 4.5" />
<nitro engine-force="350" consumption="1" small-container="1" big-container="3"
max-speed-increase="4.5" duration="1.5" fade-out-time="2.5" max="20"/>
<slipstream length="11" width="2" collect-time="1.5" use-time="2.5"
add-power="3.2" min-speed="9"
max-speed-increase="4" duration="1.2" fade-out-time="2.3"/>
<turn turn-radius="0:3.0 10:10.0 25:20.0 45:40.0"
time-full-steer ="0:0.15 0.5:0.15 0.5:0.25 1.0:0.25"
time-reset-steer="0.1"/>
<engine power="250 300 350 400" max-speed="13 18 21 23.0" brake-factor="15.0"
max-speed-reverse-ratio="0.5"/>
<gear switch-ratio="0.20 0.55 1" power-increase="5 4 3"/>
<mass value="195"/>
<explosion time="2.1" radius="5.5"
invulnerability-time="7" />
</light>
<medium>
<startup time = "0.3 0.5"
boost = "4.2 2.6" />
<nitro engine-force="425" consumption="1.4" small-container="1" big-container="3"
max-speed-increase="5" duration="1.2" fade-out-time="2" max="20"/>
<slipstream length="10" width="2" collect-time="2" use-time="3.3"
add-power="2.8" min-speed="10"
max-speed-increase="5" duration="0.9" fade-out-time="1.6"/>
<turn turn-radius="0:4.5 10:16.0 25:30.0 45:60.0"
time-full-steer ="0:0.17 0.5:0.17 0.5:0.28 1.0:0.28"
time-reset-steer="0.1"/>
<engine power="375 450 525 550" max-speed="14 19 22.0 25" brake-factor="11.0"
max-speed-reverse-ratio="0.4"/>
<gear switch-ratio="0.30 0.7 1.0" power-increase="2.2 2.2 2.5"/>
<mass value="250"/>
<explosion time="1.8" radius="5"
invulnerability-time="6" />
</medium>
<heavy>
<startup time = "0.3 0.5"
boost = "3.8 2" />
<nitro engine-force="600" consumption="2" small-container="1" big-container="3"
max-speed-increase="8" duration="0.7" fade-out-time="1.3" max="20"/>
<slipstream length="8.5" width="2" collect-time="2" use-time="4"
add-power="2.7" min-speed="10.5"
max-speed-increase="8" duration="0.7" fade-out-time="1"/>
<swatter duration="10" distance="3" squash-duration="5"
squash-slowdown="0.5"/>
<turn turn-radius="0:4.0 10:18.5 25:43.0 45:72.5"
time-full-steer ="0:0.23 0.5:0.23 0.5:0.41 1.0:0.41"
time-reset-steer="0.1"/>
<engine power="575 675 775 875" max-speed="15 20 23 25" brake-factor="10"
max-speed-reverse-ratio="0.65"/>
<gear switch-ratio="0.45 0.70 1" power-increase="1.5 1.7 2.5"/>
<mass value="350"/>
<explosion time="1.5" radius="4"
invulnerability-time="6" />
</heavy>
<light />
<medium />
<heavy />
</kart-type>
</general-kart-defaults>

View File

@ -510,5 +510,6 @@ endif()
add_library(stkirrlicht STATIC ${IRRLICHT_SOURCES})
target_link_libraries(stkirrlicht ${PNG_LIBRARY} ${JPEG_LIBRARY} ${ZLIB_LIBRARY})
if(WIN32)
target_link_libraries(stkirrlicht imm32)
endif()

View File

@ -49,6 +49,14 @@ namespace irr
user receiver then no text will be sent to the console. */
EET_LOG_TEXT_EVENT,
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
//! A input method event
/** Input method events are created by the input method message and passed to IrrlichtDevice::postEventFromUser.
Windows: Implemented.
Linux / Other: Not yet implemented. */
EET_IMPUT_METHOD_EVENT,
#endif
//! A user event with user data.
/** This is not used by Irrlicht and can be used to send user
specific data though the system. The Irrlicht 'window handle'
@ -142,6 +150,20 @@ namespace irr
EMBSM_FORCE_32_BIT = 0x7fffffff
};
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
//! Enumeration for all input method events
enum EINPUT_METHOD_EVENT
{
//! a character from input method.
EIME_CHAR_INPUT = 0,
//! change position of composition window
EIME_CHANGE_POS,
EIME_FORCE_32_BIT = 0x7fffffff
};
#endif
namespace gui
{
@ -411,6 +433,20 @@ struct SEvent
s32 UserData2;
};
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
struct SInputMethodEvent
{
//! Parent window handle for IMM functions (Windows only)
void* Handle;
//! Character from Input Method
wchar_t Char;
//! Type of input method event
EINPUT_METHOD_EVENT Event;
};
#endif
EEVENT_TYPE EventType;
union
{
@ -420,6 +456,9 @@ struct SEvent
struct SJoystickEvent JoystickEvent;
struct SLogEvent LogEvent;
struct SUserEvent UserEvent;
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
struct SInputMethodEvent InputMethodEvent;
#endif
};
};

View File

@ -26,11 +26,19 @@ public:
}
//! Copies text to the clipboard
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
virtual void copyToClipboard(const wchar_t* text) const = 0;
#else
virtual void copyToClipboard(const c8* text) const = 0;
#endif
//! Get text from the clipboard
/** \return Returns 0 if no string is in there. */
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
virtual const wchar_t* getTextFromClipboard() const = 0;
#else
virtual const c8* getTextFromClipboard() const = 0;
#endif
//! Get the processor speed in megahertz
/** \param MHz The integer variable to store the speed in.

View File

@ -99,8 +99,10 @@
#if !defined(_IRR_WINDOWS_API_) && !defined(_IRR_OSX_PLATFORM_)
#ifndef _IRR_SOLARIS_PLATFORM_
#if !defined(__linux__) && !defined(__FreeBSD__)
#define _IRR_LINUX_PLATFORM_
#endif
#endif
#define _IRR_POSIX_API_
#define _IRR_COMPILE_WITH_X11_DEVICE_
#endif

View File

@ -267,6 +267,36 @@ public:
*this = &tmpbuf[idx];
}
//! Constructs a string from an unsigned long long
explicit string(unsigned long long number)
: array(0), allocated(0), used(0)
{
// temporary buffer for 32 numbers
c8 tmpbuf[32]={0};
u32 idx = 31;
// special case '0'
if (!number)
{
tmpbuf[30] = '0';
*this = &tmpbuf[30];
return;
}
// add numbers
while(number && idx)
{
--idx;
tmpbuf[idx] = (c8)('0' + (number % 10));
number /= 10;
}
*this = &tmpbuf[idx];
}
//! Constructor for copying a string from a pointer with a given length

View File

@ -2028,7 +2028,7 @@ public:
{
u32 tmp;
sscanf(text, "0x%x", &tmp);
Value = (void *) tmp;
Value = reinterpret_cast<void *>(tmp);
}
virtual E_ATTRIBUTE_TYPE getType() const

View File

@ -286,7 +286,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
core::stringw s;
#else
core::stringc s;
#endif
s = Text.subString(realmbgn, realmend - realmbgn).c_str();
Operator->copyToClipboard(s.c_str());
}
@ -299,7 +303,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
// copy
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
core::stringw sc;
#else
core::stringc sc;
#endif
sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
Operator->copyToClipboard(sc.c_str());
@ -329,7 +337,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
// add new character
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
const wchar_t* p = Operator->getTextFromClipboard();
#else
const c8* p = Operator->getTextFromClipboard();
#endif
if (p)
{
if (MarkBegin == MarkEnd)

View File

@ -599,6 +599,18 @@ bool CGUIEnvironment::postEventFromUser(const SEvent& event)
}
break;
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
case EET_IMPUT_METHOD_EVENT:
{
// todo : if CGUIEdit has not focus, close input method. Use WM_NOTIFY message.
if (Focus)
{
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return Focus->OnEvent(event);
}
}
break;
#endif
default:
break;
} // end switch

View File

@ -14,6 +14,7 @@ extern bool GLContextDebugBit;
#include <unistd.h>
#include <sys/utsname.h>
#include <time.h>
#include <locale.h>
#include "IEventReceiver.h"
#include "ISceneManager.h"
#include "IGUIEnvironment.h"
@ -38,7 +39,7 @@ extern bool GLContextDebugBit;
#ifdef __FreeBSD__
#include <sys/joystick.h>
#else
#elif defined(__linux__)
// linux/joystick.h includes linux/input.h, which #defines values for various KEY_FOO keys.
// These override the irr::KEY_FOO equivalents, which stops key handling from working.
@ -83,6 +84,7 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param)
: CIrrDeviceStub(param),
#ifdef _IRR_COMPILE_WITH_X11_
display(0), visual(0), screennr(0), window(0), StdHints(0), SoftwareImage(0),
XInputMethod(0), XInputContext(0),
#ifdef _IRR_COMPILE_WITH_OPENGL_
glxWin(0),
Context(0),
@ -134,6 +136,10 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param)
if (!VideoDriver)
return;
#ifdef _IRR_COMPILE_WITH_X11_
createInputContext();
#endif
createGUIAndScene();
}
@ -168,6 +174,8 @@ CIrrDeviceLinux::~CIrrDeviceLinux()
VideoDriver = NULL;
}
destroyInputContext();
if (display)
{
#ifdef _IRR_COMPILE_WITH_OPENGL_
@ -645,11 +653,11 @@ bool CIrrDeviceLinux::createWindow()
GLX_SAMPLE_BUFFERS_SGIS, 1,
GLX_SAMPLES_SGIS, CreationParams.AntiAlias, // 18,19
#endif
//#ifdef GL_ARB_framebuffer_sRGB
// GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, CreationParams.HandleSRGB,
//#elif defined(GL_EXT_framebuffer_sRGB)
// GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, CreationParams.HandleSRGB,
//#endif
#ifdef GLX_ARB_framebuffer_sRGB
GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, CreationParams.HandleSRGB,
#elif defined(GLX_EXT_framebuffer_sRGB)
GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, CreationParams.HandleSRGB,
#endif
GLX_STEREO, CreationParams.Stereobuffer?True:False,
None
};
@ -796,11 +804,11 @@ bool CIrrDeviceLinux::createWindow()
// GLX_USE_GL, which is silently ignored by glXChooseVisual
CreationParams.Doublebuffer?GLX_DOUBLEBUFFER:GLX_USE_GL, // 14
CreationParams.Stereobuffer?GLX_STEREO:GLX_USE_GL, // 15
//#ifdef GL_ARB_framebuffer_sRGB
// CreationParams.HandleSRGB?GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB:GLX_USE_GL,
//#elif defined(GL_EXT_framebuffer_sRGB)
// CreationParams.HandleSRGB?GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT:GLX_USE_GL,
//#endif
#ifdef GLX_ARB_framebuffer_sRGB
CreationParams.HandleSRGB?GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB:GLX_USE_GL,
#elif defined(GLX_EXT_framebuffer_sRGB)
CreationParams.HandleSRGB?GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT:GLX_USE_GL,
#endif
None
};
@ -1154,6 +1162,123 @@ void CIrrDeviceLinux::createDriver()
}
}
#ifdef _IRR_COMPILE_WITH_X11_
bool CIrrDeviceLinux::createInputContext()
{
// One one side it would be nicer to let users do that - on the other hand
// not setting the environment locale will not work when using i18n X11 functions.
// So users would have to call it always or their input is broken badly.
// We can restore immediately - so won't mess with anything in users apps.
core::stringc oldLocale(setlocale(LC_CTYPE, NULL));
setlocale(LC_CTYPE, ""); // use environment locale
if ( !XSupportsLocale() )
{
os::Printer::log("Locale not supported. Falling back to non-i18n input.", ELL_WARNING);
setlocale(LC_CTYPE, oldLocale.c_str());
return false;
}
XInputMethod = XOpenIM(display, NULL, NULL, NULL);
if ( !XInputMethod )
{
setlocale(LC_CTYPE, oldLocale.c_str());
os::Printer::log("XOpenIM failed to create an input method. Falling back to non-i18n input.", ELL_WARNING);
return false;
}
XIMStyles *im_supported_styles;
XGetIMValues(XInputMethod, XNQueryInputStyle, &im_supported_styles, (char*)NULL);
XIMStyle bestStyle = 0;
// TODO: If we want to support languages like chinese or japanese as well we probably have to work with callbacks here.
XIMStyle supportedStyle = XIMPreeditNone | XIMStatusNone;
for (int i=0; i < im_supported_styles->count_styles; ++i)
{
XIMStyle style = im_supported_styles->supported_styles[i];
if ((style & supportedStyle) == style) /* if we can handle it */
{
bestStyle = style;
break;
}
}
XFree(im_supported_styles);
if ( !bestStyle )
{
XDestroyIC(XInputContext);
XInputContext = 0;
os::Printer::log("XInputMethod has no input style we can use. Falling back to non-i18n input.", ELL_WARNING);
setlocale(LC_CTYPE, oldLocale.c_str());
return false;
}
XInputContext = XCreateIC(XInputMethod,
XNInputStyle, bestStyle,
XNClientWindow, window,
(char*)NULL);
if (!XInputContext )
{
os::Printer::log("XInputContext failed to create an input context. Falling back to non-i18n input.", ELL_WARNING);
setlocale(LC_CTYPE, oldLocale.c_str());
return false;
}
XSetICFocus(XInputContext);
setlocale(LC_CTYPE, oldLocale.c_str());
return true;
}
void CIrrDeviceLinux::destroyInputContext()
{
if ( XInputContext )
{
XUnsetICFocus(XInputContext);
XDestroyIC(XInputContext);
XInputContext = 0;
}
if ( XInputMethod )
{
XCloseIM(XInputMethod);
XInputMethod = 0;
}
}
EKEY_CODE CIrrDeviceLinux::getKeyCode(XEvent &event)
{
EKEY_CODE keyCode = (EKEY_CODE)0;
SKeyMap mp;
mp.X11Key = XkbKeycodeToKeysym(display, event.xkey.keycode, 0, 0);
// mp.X11Key = XKeycodeToKeysym(XDisplay, event.xkey.keycode, 0); // deprecated, if we still find platforms which need that we have to use some define
const s32 idx = KeyMap.binary_search(mp);
if (idx != -1)
{
keyCode = (EKEY_CODE)KeyMap[idx].Win32Key;
}
if (keyCode == 0)
{
// Any value is better than none, that allows at least using the keys.
// Worst case is that some keys will be identical, still better than _all_
// unknown keys being identical.
if ( !mp.X11Key )
{
keyCode = (EKEY_CODE)event.xkey.keycode;
os::Printer::log("No such X11Key, using event keycode", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION);
}
else if (idx == -1)
{
keyCode = (EKEY_CODE)mp.X11Key;
os::Printer::log("EKEY_CODE not found, using orig. X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION);
}
else
{
keyCode = (EKEY_CODE)mp.X11Key;
os::Printer::log("EKEY_CODE is 0, using orig. X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION);
}
}
return keyCode;
}
#endif
//! runs the device. Returns false if device wants to be deleted
bool CIrrDeviceLinux::run()
@ -1330,52 +1455,60 @@ bool CIrrDeviceLinux::run()
(next_event.xkey.keycode == event.xkey.keycode) &&
(next_event.xkey.time - event.xkey.time) < 2) // usually same time, but on some systems a difference of 1 is possible
{
/* Ignore the key release event */
// Ignore the key release event
break;
}
}
// fall-through in case the release should be handled
irrevent.EventType = irr::EET_KEY_INPUT_EVENT;
irrevent.KeyInput.PressedDown = false;
irrevent.KeyInput.Char = 0; // on release that's undefined
irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0;
irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0;
irrevent.KeyInput.Key = getKeyCode(event);
postEventFromUser(irrevent);
break;
case KeyPress:
{
SKeyMap mp;
char buf[8]={0};
XLookupString(&event.xkey, buf, sizeof(buf), &mp.X11Key, NULL);
irrevent.EventType = irr::EET_KEY_INPUT_EVENT;
irrevent.KeyInput.PressedDown = (event.type == KeyPress);
// mbtowc(&irrevent.KeyInput.Char, buf, sizeof(buf));
irrevent.KeyInput.Char = ((wchar_t*)(buf))[0];
irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0;
irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0;
event.xkey.state &= ~(ControlMask|ShiftMask); // ignore shift-ctrl states for figuring out the key
XLookupString(&event.xkey, buf, sizeof(buf), &mp.X11Key, NULL);
const s32 idx = KeyMap.binary_search(mp);
if (idx != -1)
if ( XInputContext )
{
irrevent.KeyInput.Key = (EKEY_CODE)KeyMap[idx].Win32Key;
}
else
{
irrevent.KeyInput.Key = (EKEY_CODE)0;
}
if (irrevent.KeyInput.Key == 0)
{
// 1:1 mapping to windows-keys would require testing for keyboard type (us, ger, ...)
// So unless we do that we will have some unknown keys here.
if (idx == -1)
wchar_t buf[8]={0};
Status status;
int strLen = XwcLookupString(XInputContext, &event.xkey, buf, sizeof(buf), &mp.X11Key, &status);
if ( status == XBufferOverflow )
{
os::Printer::log("Could not find EKEY_CODE, using orig. X11 keycode instead", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION);
os::Printer::log("XwcLookupString needs a larger buffer", ELL_INFORMATION);
}
if ( strLen > 0 && (status == XLookupChars || status == XLookupBoth) )
{
if ( strLen > 1 )
os::Printer::log("Additional returned characters dropped", ELL_INFORMATION);
irrevent.KeyInput.Char = buf[0];
}
else
{
os::Printer::log("EKEY_CODE is 0, using orig. X11 keycode instead", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION);
irrevent.KeyInput.Char = 0;
}
// Any value is better than none, that allows at least using the keys.
// Worst case is that some keys will be identical, still better than _all_
// unknown keys being identical.
irrevent.KeyInput.Key = (EKEY_CODE)event.xkey.keycode;
}
else // Old version without InputContext. Does not support i18n, but good to have as fallback.
{
union
{
char buf[8];
wchar_t wbuf[2];
} tmp = {0};
XLookupString(&event.xkey, tmp.buf, sizeof(tmp.buf), &mp.X11Key, NULL);
irrevent.KeyInput.Char = tmp.wbuf[0];
}
irrevent.EventType = irr::EET_KEY_INPUT_EVENT;
irrevent.KeyInput.PressedDown = true;
irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0;
irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0;
irrevent.KeyInput.Key = getKeyCode(event);
postEventFromUser(irrevent);
}
@ -2052,7 +2185,7 @@ bool CIrrDeviceLinux::activateJoysticks(core::array<SJoystickInfo> & joystickInf
#ifdef __FreeBSD__
info.axes=2;
info.buttons=2;
#else
#elif defined(__linux__)
ioctl( info.fd, JSIOCGAXES, &(info.axes) );
ioctl( info.fd, JSIOCGBUTTONS, &(info.buttons) );
fcntl( info.fd, F_SETFL, O_NONBLOCK );
@ -2074,7 +2207,7 @@ bool CIrrDeviceLinux::activateJoysticks(core::array<SJoystickInfo> & joystickInf
returnInfo.Axes = info.axes;
returnInfo.Buttons = info.buttons;
#ifndef __FreeBSD__
#if !defined(__FreeBSD__) && !defined(__OpenBSD__)
char name[80];
ioctl( info.fd, JSIOCGNAME(80), name);
returnInfo.Name = name;
@ -2119,7 +2252,7 @@ void CIrrDeviceLinux::pollJoysticks()
info.persistentData.JoystickEvent.Axis[0] = js.x; /* X axis */
info.persistentData.JoystickEvent.Axis[1] = js.y; /* Y axis */
}
#else
#elif defined(__linux__)
struct js_event event;
while (sizeof(event) == read(info.fd, &event, sizeof(event)))
{

View File

@ -153,6 +153,12 @@ namespace irr
bool restoreResolution();
bool changeResolution();
#ifdef _IRR_COMPILE_WITH_X11_
bool createInputContext();
void destroyInputContext();
EKEY_CODE getKeyCode(XEvent &event);
#endif
//! Implementation of the linux cursor control
class CCursorControl : public gui::ICursorControl
{
@ -390,6 +396,8 @@ namespace irr
XSetWindowAttributes attributes;
XSizeHints* StdHints;
XImage* SoftwareImage;
XIM XInputMethod;
XIC XInputContext;
mutable core::stringc Clipboard;
#ifdef _IRR_LINUX_X11_VIDMODE_
XF86VidModeModeInfo oldVideoMode;

View File

@ -478,7 +478,7 @@ bool CIrrDeviceSDL::run()
joyevent.JoystickEvent.ButtonStates |= (SDL_JoystickGetButton(joystick, j)<<j);
// query all axes, already in correct range
const int numAxes = core::min_(SDL_JoystickNumAxes(joystick), SEvent::SJoystickEvent::NUMBER_OF_AXES);
const int numAxes = core::min_(SDL_JoystickNumAxes(joystick), static_cast<int>(SEvent::SJoystickEvent::NUMBER_OF_AXES));
joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X]=0;
joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y]=0;
joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z]=0;

View File

@ -34,6 +34,9 @@
#endif
#endif
#include <imm.h>
#pragma comment(lib, "imm32.lib")
namespace irr
{
namespace video
@ -692,6 +695,28 @@ irr::CIrrDeviceWin32* getDeviceFromHWnd(HWND hWnd)
}
namespace irr
{
void updateICPos(void* hWnd, s32 x, s32 y, s32 height)
{
COMPOSITIONFORM cf;
HWND hwnd = (HWND)hWnd;
HIMC hIMC = ImmGetContext(hwnd);
if(hIMC)
{
cf.dwStyle = CFS_POINT;
cf.ptCurrentPos.x = x;
cf.ptCurrentPos.y = y - height;
ImmSetCompositionWindow(hIMC, &cf);
ImmReleaseContext(hwnd, hIMC);
}
}
} // end of namespace irr
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
#ifndef WM_MOUSEWHEEL
@ -802,6 +827,16 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
}
}
}
if (m->irrMessage == irr::EMIE_LMOUSE_PRESSED_DOWN || m->irrMessage == irr::EMIE_LMOUSE_LEFT_UP)
{
event.EventType = irr::EET_IMPUT_METHOD_EVENT;
event.InputMethodEvent.Event = irr::EIME_CHANGE_POS;
event.InputMethodEvent.Handle = hWnd;
if (dev)
dev->postEventFromUser(event);
}
return 0;
}
@ -818,6 +853,30 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_ERASEBKGND:
return 0;
case WM_IME_CHAR:
{
event.EventType = irr::EET_IMPUT_METHOD_EVENT;
event.InputMethodEvent.Event = irr::EIME_CHAR_INPUT;
event.InputMethodEvent.Handle = hWnd;
event.KeyInput.Char = 0;
event.KeyInput.Key = irr::KEY_OEM_CLEAR;
event.KeyInput.Shift = 0;
event.KeyInput.Control = 0;
char bits[2] = { (char)((wParam & 0xff00) >> 8), (char)(wParam & 0xff) };
if (bits[0] == 0)
event.InputMethodEvent.Char = (wchar_t)wParam;
else
MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, bits, 2, &event.InputMethodEvent.Char, 1);
dev = getDeviceFromHWnd(hWnd);
if (dev)
dev->postEventFromUser(event);
return 0;
}
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
@ -879,6 +938,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
event.KeyInput.Control = 0;
dev = getDeviceFromHWnd(hWnd);
if (dev)
dev->postEventFromUser(event);
event.EventType = irr::EET_IMPUT_METHOD_EVENT;
event.InputMethodEvent.Event = irr::EIME_CHANGE_POS;
event.InputMethodEvent.Handle = hWnd;
if (dev)
dev->postEventFromUser(event);
@ -932,6 +998,12 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
dev->switchToFullScreen();
}
}
event.EventType = irr::EET_IMPUT_METHOD_EVENT;
event.InputMethodEvent.Event = irr::EIME_CHANGE_POS;
event.InputMethodEvent.Handle = hWnd;
if (dev)
dev->postEventFromUser(event);
break;
case WM_USER:
@ -1115,6 +1187,9 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters& params)
// inform driver about the window size etc.
resizeIfNecessary();
// for reset the position of composition window
updateICPos(HWnd, 0, 0, 0);
}
@ -1870,6 +1945,8 @@ void CIrrDeviceWin32::handleSystemMessages()
// No message translation because we don't use WM_CHAR and it would conflict with our
// deadkey handling.
TranslateMessage(&msg);
if (ExternalWindow && msg.hwnd == HWnd)
WndProc(HWnd, msg.message, msg.wParam, msg.lParam);
else

View File

@ -12,6 +12,7 @@
#include <string.h>
#include <unistd.h>
#if !defined(_IRR_SOLARIS_PLATFORM_) && !defined(__CYGWIN__)
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
@ -52,6 +53,37 @@ const core::stringc& COSOperator::getOperatingSystemVersion() const
//! copies text to the clipboard
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
void COSOperator::copyToClipboard(const wchar_t* text) const
{
if (wcslen(text)==0)
return;
// Windows version
#if defined(_IRR_XBOX_PLATFORM_)
#elif defined(_IRR_WINDOWS_API_)
if (!OpenClipboard(NULL) || text == 0)
return;
EmptyClipboard();
HGLOBAL clipbuffer;
wchar_t * buffer;
clipbuffer = GlobalAlloc(GMEM_DDESHARE, wcslen(text)*sizeof(wchar_t) + sizeof(wchar_t));
buffer = (wchar_t*)GlobalLock(clipbuffer);
wcscpy(buffer, text);
GlobalUnlock(clipbuffer);
SetClipboardData(CF_UNICODETEXT, clipbuffer); //Windwos converts between CF_UNICODETEXT and CF_TEXT automatically.
CloseClipboard();
#else
#endif
}
#else
void COSOperator::copyToClipboard(const c8* text) const
{
if (strlen(text)==0)
@ -89,10 +121,34 @@ void COSOperator::copyToClipboard(const c8* text) const
#endif
}
#endif
//! gets text from the clipboard
//! \return Returns 0 if no string is in there.
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
const wchar_t* COSOperator::getTextFromClipboard() const
{
#if defined(_IRR_XBOX_PLATFORM_)
return 0;
#elif defined(_IRR_WINDOWS_API_)
if (!OpenClipboard(NULL))
return 0;
wchar_t * buffer = 0;
HANDLE hData = GetClipboardData( CF_UNICODETEXT ); //Windwos converts between CF_UNICODETEXT and CF_TEXT automatically.
buffer = (wchar_t*)GlobalLock( hData );
GlobalUnlock( hData );
CloseClipboard();
return buffer;
#else
return 0;
#endif
}
#else
const c8* COSOperator::getTextFromClipboard() const
{
#if defined(_IRR_XBOX_PLATFORM_)
@ -122,6 +178,7 @@ const c8* COSOperator::getTextFromClipboard() const
return 0;
#endif
}
#endif
bool COSOperator::getProcessorSpeedMHz(u32* MHz) const

View File

@ -27,11 +27,19 @@ public:
virtual const core::stringc& getOperatingSystemVersion() const;
//! copies text to the clipboard
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
virtual void copyToClipboard(const wchar_t* text) const;
#else
virtual void copyToClipboard(const c8* text) const;
#endif
//! gets text from the clipboard
//! \return Returns 0 if no string is in there.
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
virtual const wchar_t* getTextFromClipboard() const;
#else
virtual const c8* getTextFromClipboard() const;
#endif
//! gets the processor speed in megahertz
//! \param Mhz:

View File

@ -805,13 +805,15 @@ bool COpenGLDriver::genericDriverInit()
Params.HandleSRGB &= ((FeatureAvailable[IRR_ARB_framebuffer_sRGB] || FeatureAvailable[IRR_EXT_framebuffer_sRGB]) &&
FeatureAvailable[IRR_EXT_texture_sRGB]);
#if defined(GL_ARB_framebuffer_sRGB)
if (Params.HandleSRGB)
glEnable(GL_FRAMEBUFFER_SRGB);
#elif defined(GL_EXT_framebuffer_sRGB)
if (Params.HandleSRGB)
glEnable(GL_FRAMEBUFFER_SRGB_EXT);
#endif
glDisable(GL_FRAMEBUFFER_SRGB);
//#if defined(GL_ARB_framebuffer_sRGB)
// if (Params.HandleSRGB)
// glEnable(GL_FRAMEBUFFER_SRGB);
//#elif defined(GL_EXT_framebuffer_sRGB)
// if (Params.HandleSRGB)
// glEnable(GL_FRAMEBUFFER_SRGB_EXT);
//#endif
// This is a fast replacement for NORMALIZE_NORMALS
// if ((Version>101) || FeatureAvailable[IRR_EXT_rescale_normal])

View File

@ -274,15 +274,15 @@ GLint COpenGLTexture::getOpenGLFormatAndParametersFromColorFormat(ECOLOR_FORMAT
internalformat = GL_RGBA8;
}
}
#if defined(GL_ARB_framebuffer_sRGB) || defined(GL_EXT_framebuffer_sRGB)
if (Driver->Params.HandleSRGB)
{
if (internalformat==GL_RGBA)
internalformat=GL_SRGB_ALPHA_EXT;
else if (internalformat==GL_RGB)
internalformat=GL_SRGB_EXT;
}
#endif
//#if defined(GL_ARB_framebuffer_sRGB) || defined(GL_EXT_framebuffer_sRGB)
// if (Driver->Params.HandleSRGB)
// {
// if (internalformat==GL_RGBA)
// internalformat=GL_SRGB_ALPHA_EXT;
// else if (internalformat==GL_RGB)
// internalformat=GL_SRGB_EXT;
// }
//#endif
return internalformat;
}

View File

@ -22,10 +22,14 @@
#include <libkern/OSByteOrder.h>
#define bswap_16(X) OSReadSwapInt16(&X,0)
#define bswap_32(X) OSReadSwapInt32(&X,0)
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#elif defined(__FreeBSD__)
#include <sys/endian.h>
#define bswap_16(X) bswap16(X)
#define bswap_32(X) bswap32(X)
#elif defined(__OpenBSD__)
#include <endian.h>
#define bswap_16(X) swap16(X)
#define bswap_32(X) swap32(X)
#elif !defined(_IRR_SOLARIS_PLATFORM_) && !defined(__PPC__) && !defined(_IRR_WINDOWS_API_)
#include <byteswap.h>
#else

View File

@ -42,18 +42,6 @@
#include <hidsdi.h>
#include <setupapi.h>
#ifdef __MINGW32__
/* this prototype is missing from the mingw headers so we must add it
or suffer linker errors. */
# ifdef __cplusplus
extern "C" {
# endif
HIDAPI BOOL NTAPI HidD_SetOutputReport(HANDLE, PVOID, ULONG);
# ifdef __cplusplus
}
# endif
#endif
int wiiuse_os_find(struct wiimote_t** wm, int max_wiimotes, int timeout) {
GUID device_id;
HANDLE dev;

View File

@ -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.
# Modify this file to change the last-modified date when you add/remove a file.
# 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/*")

View File

@ -269,7 +269,7 @@ void PlayerManager::save()
if(m_current_player)
{
players_file << L" <current player=\""
<< StringUtils::xmlEncode(m_current_player->getName()) << L"\"/>\n";
<< StringUtils::xmlEncode(m_current_player->getName(true/*ignoreRTL*/)) << L"\"/>\n";
}
// Save all non-guest players
@ -327,7 +327,7 @@ void PlayerManager::enforceCurrentPlayer()
if (!player->isGuestAccount())
{
Log::info("PlayerManager", "Enforcing current player '%ls'.",
player->getName().c_str() );
player->getName(true/*ignoreRTL*/).c_str());
m_current_player = player;
return;
}
@ -341,7 +341,7 @@ void PlayerManager::enforceCurrentPlayer()
if (!player->isGuestAccount())
{
Log::info("PlayerManager", "Enforcing current player '%s'.",
player->getName().c_str());
player->getName(true/*ignoreRTL*/).c_str());
m_current_player = player;
return;
}
@ -454,7 +454,7 @@ PlayerProfile *PlayerManager::getPlayer(const irr::core::stringw &name)
{
for (PlayerProfile* player : m_all_players)
{
if(player->getName()==name)
if(player->getName(true/*ignoreRTL*/)==name)
return player;
}
return NULL;

View File

@ -24,6 +24,7 @@
#include "utils/leak_check.hpp"
#include "utils/no_copy.hpp"
#include "utils/types.hpp"
#include "utils/translation.hpp"
#include <irrString.h>
using namespace irr;
@ -154,10 +155,17 @@ public:
// ------------------------------------------------------------------------
/** Returns the name of this player. */
core::stringw getName() const
const core::stringw getName(bool ignore_rtl = false) const
{
assert(m_magic_number == 0xABCD1234);
return m_local_name.c_str();
if (ignore_rtl)
return m_local_name;
else
{
const core::stringw fribidized_name =
translations->fribidize(m_local_name);
return fribidized_name;
}
} // getName
// ------------------------------------------------------------------------
@ -171,9 +179,16 @@ public:
} // isGuestAccount
// ------------------------------------------------------------------------
/** Returns the last used online name. */
const core::stringw& getLastOnlineName() const
const core::stringw getLastOnlineName(bool ignore_rtl = false) const
{
return m_last_online_name;
if (ignore_rtl)
return m_last_online_name;
else
{
const core::stringw fribidized_name =
translations->fribidize(m_last_online_name);
return fribidized_name;
}
} // getLastOnlineName
// ------------------------------------------------------------------------
/** Sets the last used online name. */

View File

@ -27,7 +27,6 @@
#include "io/xml_node.hpp"
#include "items/item.hpp"
#include "karts/kart_properties.hpp"
#include "karts/player_difficulty.hpp"
#include "utils/log.hpp"
STKConfig* stk_config=0;
@ -119,19 +118,8 @@ void STKConfig::load(const std::string &filename)
}
CHECK_NEG(m_max_karts, "<karts max=..." );
CHECK_NEG(m_parachute_friction, "parachute-friction" );
CHECK_NEG(m_parachute_lbound_fraction, "parachute-lbound-fraction" );
CHECK_NEG(m_parachute_ubound_fraction, "parachute-ubound-fraction" );
CHECK_NEG(m_parachute_max_speed, "parachute-max-speed" );
CHECK_NEG(m_parachute_time, "parachute-time" );
CHECK_NEG(m_parachute_time_other, "parachute-time-other" );
CHECK_NEG(m_bomb_time, "bomb-time" );
CHECK_NEG(m_bomb_time_increase, "bomb-time-increase" );
CHECK_NEG(m_anvil_time, "anvil-time" );
CHECK_NEG(m_anvil_weight, "anvil-weight" );
CHECK_NEG(m_item_switch_time, "item-switch-time" );
CHECK_NEG(m_bubblegum_counter, "bubblegum disappear counter");
CHECK_NEG(m_bubblegum_shield_time, "bubblegum shield-time" );
CHECK_NEG(m_explosion_impulse_objects, "explosion-impulse-objects" );
CHECK_NEG(m_max_skidmarks, "max-skidmarks" );
CHECK_NEG(m_min_kart_version, "<kart-version min...>" );
@ -163,18 +151,13 @@ void STKConfig::load(const std::string &filename)
*/
void STKConfig::init_defaults()
{
m_anvil_weight = m_parachute_friction =
m_parachute_time = m_parachute_lbound_fraction =
m_parachute_time_other = m_anvil_speed_factor =
m_bomb_time = m_bomb_time_increase =
m_anvil_time = m_music_credit_time =
m_bomb_time = m_bomb_time_increase =
m_explosion_impulse_objects = m_music_credit_time =
m_delay_finish_time = m_skid_fadeout_time =
m_near_ground = m_item_switch_time =
m_smooth_angle_limit = m_parachute_ubound_fraction =
m_penalty_time = m_explosion_impulse_objects =
m_parachute_max_speed = UNDEFINED;
m_smooth_angle_limit = m_penalty_time =
UNDEFINED;
m_bubblegum_counter = -100;
m_bubblegum_shield_time = -100;
m_shield_restrict_weapos = false;
m_max_karts = -100;
m_max_skidmarks = -100;
@ -310,23 +293,6 @@ void STKConfig::getAllData(const XMLNode * root)
credits_node->get("music", &m_music_credit_time);
if(const XMLNode *anvil_node= root->getNode("anvil"))
{
anvil_node->get("weight", &m_anvil_weight );
anvil_node->get("speed-factor", &m_anvil_speed_factor);
anvil_node->get("time", &m_anvil_time );
}
if(const XMLNode *parachute_node= root->getNode("parachute"))
{
parachute_node->get("friction", &m_parachute_friction );
parachute_node->get("time", &m_parachute_time );
parachute_node->get("time-other", &m_parachute_time_other );
parachute_node->get("lbound-fraction", &m_parachute_lbound_fraction);
parachute_node->get("ubound-fraction", &m_parachute_ubound_fraction);
parachute_node->get("max-speed", &m_parachute_max_speed );
}
if(const XMLNode *bomb_node= root->getNode("bomb"))
{
bomb_node->get("time", &m_bomb_time);
@ -359,7 +325,6 @@ void STKConfig::getAllData(const XMLNode * root)
if(const XMLNode *bubblegum_node= root->getNode("bubblegum"))
{
bubblegum_node->get("disappear-counter", &m_bubblegum_counter );
bubblegum_node->get("shield-time", &m_bubblegum_shield_time );
bubblegum_node->get("restrict-weapons", &m_shield_restrict_weapos);
}
@ -415,14 +380,6 @@ void STKConfig::getAllData(const XMLNode * root)
m_kart_properties[type->getName()]->copyFrom(m_default_kart_properties);
m_kart_properties[type->getName()]->getAllData(type);
}
child_node = node->getNode("difficulties");
for (unsigned int i = 0; i < child_node->getNumNodes(); ++i)
{
const XMLNode* type = child_node->getNode(i);
m_player_difficulties[i] = new PlayerDifficulty();
m_player_difficulties[i]->getAllData(type);
}
} // getAllData
// ----------------------------------------------------------------------------

View File

@ -34,7 +34,6 @@
#include <map>
class KartProperties;
class PlayerDifficulty;
class MusicInformation;
class XMLNode;
@ -51,8 +50,6 @@ protected:
/** Default kart properties. */
KartProperties *m_default_kart_properties;
std::map<std::string, KartProperties*> m_kart_properties;
/** Per-player difficulties. */
PlayerDifficulty* m_player_difficulties[PLAYER_DIFFICULTY_COUNT];
public:
/** What to do if a kart already has a powerup when it hits a bonus box:
@ -66,26 +63,12 @@ public:
m_same_powerup_mode;
static float UNDEFINED;
float m_anvil_weight; /**<Additional kart weight if anvil is
attached. */
float m_anvil_speed_factor; /**<Speed decrease when attached first. */
float m_parachute_friction; /**<Increased parachute air friction. */
float m_parachute_ubound_fraction; /**<Upper bound fraction of speed when
lost will detach parachute. */
float m_parachute_lbound_fraction; /**<Lower bound fraction of speed when
lost will detach parachute. */
float m_parachute_max_speed; /**<Max speed to rate current speed */
float m_parachute_time; /**<Time a parachute is active. */
float m_parachute_time_other; /**<Time a parachute attached to other
karts is active. */
float m_bomb_time; /**<Time before a bomb explodes. */
float m_bomb_time_increase; /**<Time added to bomb timer when it's
passed on. */
float m_anvil_time; /**<Time an anvil is active. */
float m_item_switch_time; /**< Time items will be switched. */
int m_bubblegum_counter; /**< How many times bubble gums must be
driven over before they disappear. */
float m_bubblegum_shield_time; /**<How long a bubble gum shield lasts. */
bool m_shield_restrict_weapos; /**<Wether weapon usage is punished. */
float m_explosion_impulse_objects; /**<Impulse of explosion on moving
objects, e.g. road cones, ... */
@ -199,10 +182,6 @@ public:
{
return *m_kart_properties.at(type);
} // getKartProperties
// ------------------------------------------------------------------------
const PlayerDifficulty * getPlayerDifficulty(PerPlayerDifficulty difficulty)
{ return m_player_difficulties[difficulty]; }
}
; // STKConfig

View File

@ -380,7 +380,7 @@ void Camera::smoothMoveCamera(float dt)
// Smoothly interpolate towards the position and target
const KartProperties *kp = m_kart->getKartProperties();
float max_increase_with_zipper = kp->getZipperMaxSpeedIncrease();
float max_speed_without_zipper = kp->getMaxSpeed();
float max_speed_without_zipper = kp->getEngineMaxSpeed();
float current_speed = m_kart->getSpeed();
const Skidding *ks = m_kart->getSkidding();
@ -469,7 +469,7 @@ void Camera::getCameraSettings(float *above_kart, float *cam_angle,
else
{
*above_kart = 0.75f;
*cam_angle = kp->getCameraForwardUpAngle();
*cam_angle = kp->getCameraForwardUpAngle() * DEGREE_TO_RAD;
*distance = -m_distance;
}
float steering = m_kart->getSteerPercent()
@ -484,7 +484,7 @@ void Camera::getCameraSettings(float *above_kart, float *cam_angle,
case CM_REVERSE: // Same as CM_NORMAL except it looks backwards
{
*above_kart = 0.75f;
*cam_angle = kp->getCameraBackwardUpAngle();
*cam_angle = kp->getCameraBackwardUpAngle() * DEGREE_TO_RAD;
*sideway = 0;
*distance = 2.0f*m_distance;
*smoothing = false;
@ -843,8 +843,7 @@ void Camera::handleEndCamera(float dt)
}
case EndCameraInformation::EC_AHEAD_OF_KART:
{
const KartProperties *kp=m_kart->getKartProperties();
float cam_angle = kp->getCameraBackwardUpAngle();
float cam_angle = m_kart->getKartProperties()->getCameraBackwardUpAngle() * DEGREE_TO_RAD;
positionCamera(dt, /*above_kart*/0.75f,
cam_angle, /*side_way*/0,

View File

@ -36,6 +36,7 @@ void CentralVideoSettings::init()
hasBuffserStorage = false;
hasDrawIndirect = false;
hasComputeShaders = false;
hasArraysOfArrays = false;
hasTextureStorage = false;
hasTextureView = false;
hasBindlessTexture = false;
@ -46,10 +47,11 @@ void CentralVideoSettings::init()
hasTextureCompression = false;
hasUBO = false;
hasGS = false;
m_GI_has_artifact = false;
m_GI_has_artifact = false;
m_need_rh_workaround = false;
m_need_srgb_workaround = false;
m_need_srgb_visual_workaround = false;
// Call to glGetIntegerv should not be made if --no-graphics is used
if (!ProfileWorld::isNoGraphics())
@ -102,6 +104,11 @@ void CentralVideoSettings::init()
hasComputeShaders = true;
Log::info("GLDriver", "ARB Compute Shader Present");
}
if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_ARRAYS_OF_ARRAYS) &&
hasGLExtension("GL_ARB_arrays_of_arrays")) {
hasArraysOfArrays = true;
Log::info("GLDriver", "ARB Arrays of Arrays Present");
}
if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_TEXTURE_STORAGE) &&
hasGLExtension("GL_ARB_texture_storage")) {
hasTextureStorage = true;
@ -179,6 +186,16 @@ void CentralVideoSettings::init()
// Bindless textures are all treated RGB even sRGB one
m_need_srgb_workaround = true;
}
// Check if visual is sRGB-capable
if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_FRAMEBUFFER_SRGB_CAPABLE) &&
m_glsl == true)
{
GLint param = GL_SRGB;
glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_BACK_LEFT,
GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &param);
m_need_srgb_visual_workaround = (param != GL_SRGB);
}
}
}
@ -207,6 +224,11 @@ bool CentralVideoSettings::needsRGBBindlessWorkaround() const
return m_need_srgb_workaround;
}
bool CentralVideoSettings::needsSRGBCapableVisualWorkaround() const
{
return m_need_srgb_visual_workaround;
}
bool CentralVideoSettings::isARBGeometryShader4Usable() const
{
return hasGS;
@ -247,6 +269,11 @@ bool CentralVideoSettings::isARBComputeShaderUsable() const
return hasComputeShaders;
}
bool CentralVideoSettings::isARBArraysOfArraysUsable() const
{
return hasArraysOfArrays;
}
bool CentralVideoSettings::isARBTextureStorageUsable() const
{
return hasTextureStorage;
@ -299,7 +326,7 @@ bool CentralVideoSettings::supportsIndirectInstancingRendering() const
bool CentralVideoSettings::supportsComputeShadersFiltering() const
{
return isARBBufferStorageUsable() && isARBImageLoadStoreUsable() && isARBComputeShaderUsable();
return isARBBufferStorageUsable() && isARBImageLoadStoreUsable() && isARBComputeShaderUsable() && isARBArraysOfArraysUsable();
}
bool CentralVideoSettings::supportsAsyncInstanceUpload() const

View File

@ -30,6 +30,7 @@ private:
bool hasDrawIndirect;
bool hasBuffserStorage;
bool hasComputeShaders;
bool hasArraysOfArrays;
bool hasTextureStorage;
bool hasTextureView;
bool hasBindlessTexture;
@ -43,6 +44,7 @@ private:
bool m_need_rh_workaround;
bool m_need_srgb_workaround;
bool m_need_srgb_visual_workaround;
bool m_GI_has_artifact;
public:
void init();
@ -52,6 +54,7 @@ public:
// Needs special handle ?
bool needRHWorkaround() const;
bool needsRGBBindlessWorkaround() const;
bool needsSRGBCapableVisualWorkaround() const;
// Extension is available and safe to use
bool isARBUniformBufferObjectUsable() const;
@ -61,6 +64,7 @@ public:
bool isARBTextureStorageUsable() const;
bool isAMDVertexShaderLayerUsable() const;
bool isARBComputeShaderUsable() const;
bool isARBArraysOfArraysUsable() const;
bool isARBBindlessTextureUsable() const;
bool isARBBufferStorageUsable() const;
bool isARBBaseInstanceUsable() const;

View File

@ -41,7 +41,7 @@ extern "C" {
#elif defined(ANDROID)
# include <GLES/gl.h>
#elif defined(WIN32)
# define _WINSOCKAPI_
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#else
#define GL_GLEXT_PROTOTYPES

View File

@ -48,6 +48,7 @@ namespace GraphicsRestrictions
"ImageLoadStore",
"BaseInstance",
"ComputeShader",
"ArraysOfArrays",
"ShaderStorageBufferObject",
"MultiDrawIndirect",
"ShaderAtomicCounters",
@ -59,6 +60,7 @@ namespace GraphicsRestrictions
"HighDefinitionTextures",
"AdvancedPipeline",
"FramebufferSRGBWorking",
"FramebufferSRGBCapable",
"GI",
};
} // namespace Private

View File

@ -42,6 +42,7 @@ namespace GraphicsRestrictions
GR_IMAGE_LOAD_STORE,
GR_BASE_INSTANCE,
GR_COMPUTE_SHADER,
GR_ARRAYS_OF_ARRAYS,
GR_SHADER_STORAGE_BUFFER_OBJECT,
GR_MULTI_DRAW_INDIRECT,
GR_SHADER_ATOMIC_COUNTERS,
@ -53,6 +54,7 @@ namespace GraphicsRestrictions
GR_HIGHDEFINITION_TEXTURES,
GR_ADVANCED_PIPELINE,
GR_FRAMEBUFFER_SRGB_WORKING,
GR_FRAMEBUFFER_SRGB_CAPABLE,
GR_GI,
GR_COUNT /** MUST be last entry. */
} ;

View File

@ -60,6 +60,7 @@
#include "modes/profile_world.hpp"
#include "modes/world.hpp"
#include "physics/physics.hpp"
#include "scriptengine/property_animator.hpp"
#include "states_screens/dialogs/confirm_resolution_dialog.hpp"
#include "states_screens/state_manager.hpp"
#include "tracks/track_manager.hpp"
@ -85,6 +86,7 @@ please use the included version."
using namespace irr;
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#if defined(__linux__) && !defined(ANDROID)
@ -342,6 +344,8 @@ void IrrDriver::createListOfVideoModes()
*/
void IrrDriver::initDevice()
{
SIrrlichtCreationParameters params;
// If --no-graphics option was used, the null device can still be used.
if (!ProfileWorld::isNoGraphics())
{
@ -424,7 +428,6 @@ void IrrDriver::initDevice()
m_device->drop();
m_device = NULL;
SIrrlichtCreationParameters params;
params.ForceLegacyDevice = (UserConfigParams::m_force_legacy_device ||
UserConfigParams::m_gamepad_visualisation);
@ -445,7 +448,8 @@ void IrrDriver::initDevice()
params.WindowSize =
core::dimension2du(UserConfigParams::m_width,
UserConfigParams::m_height);
params.HandleSRGB = true;
/*
switch ((int)UserConfigParams::m_antialiasing)
{
@ -503,6 +507,30 @@ void IrrDriver::initDevice()
{
Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n");
}
CVS->init();
// This is the ugly hack for intel driver on linux, which doesn't
// use sRGB-capable visual, even if we request it. This causes
// the screen to be darker than expected. It affects mesa 10.6 and newer.
// Though we are able to force to use the proper format on mesa side by
// setting WithAlphaChannel parameter.
if (!ProfileWorld::isNoGraphics() && CVS->needsSRGBCapableVisualWorkaround())
{
Log::warn("irr_driver", "Created visual is not sRGB-capable. "
"Re-creating device to workaround the issue.");
m_device->closeDevice();
m_device->drop();
params.WithAlphaChannel = true;
m_device = createDeviceEx(params);
if(!m_device)
{
Log::fatal("irr_driver", "Couldn't initialise irrlicht device. Quitting.\n");
}
}
m_scene_manager = m_device->getSceneManager();
m_gui_env = m_device->getGUIEnvironment();
@ -521,7 +549,7 @@ void IrrDriver::initDevice()
(UserConfigParams::m_shadows_resolution < 512 ||
UserConfigParams::m_shadows_resolution > 2048))
{
Log::warn("IrrDriver",
Log::warn("irr_driver",
"Invalid value for UserConfigParams::m_shadows_resolution : %i",
(int)UserConfigParams::m_shadows_resolution);
UserConfigParams::m_shadows_resolution = 0;
@ -1868,7 +1896,7 @@ void IrrDriver::doScreenShot()
video::IImage* image = m_video_driver->createScreenShot();
if(!image)
{
Log::error("IrrDriver", "Could not create screen shot.");
Log::error("irr_driver", "Could not create screen shot.");
return;
}
@ -1948,6 +1976,8 @@ void IrrDriver::update(float dt)
m_wind->update();
PropertyAnimator::get()->update(dt);
World *world = World::getWorld();
if (GUIEngine::getCurrentScreen() != NULL &&

View File

@ -130,13 +130,13 @@ Material* MaterialManager::getDefaultMaterial(video::E_MATERIAL_TYPE shader_type
// Try to find a cleaner way
// If graphics are disabled, shaders should not be accessed (getShader
// asserts that shaders are initialised).
if(CVS->isGLSL() && !ProfileWorld::isNoGraphics() &&
if(!ProfileWorld::isNoGraphics() && CVS->isGLSL() &&
shader_type == Shaders::getShader(ShaderType::ES_OBJECT_UNLIT))
default_material->setShaderType(Material::SHADERTYPE_SOLID_UNLIT);
else if (CVS->isGLSL() && !ProfileWorld::isNoGraphics() &&
else if (!ProfileWorld::isNoGraphics() && CVS->isGLSL() &&
shader_type == Shaders::getShader(ShaderType::ES_OBJECTPASS_REF))
default_material->setShaderType(Material::SHADERTYPE_ALPHA_TEST);
//else if (!ProfileWorld::isNoGraphics() &&
//else if (!ProfileWorld::isNoGraphics() && CVS->isGLSL() &&
// shader_type == Shaders::getShader(ShaderType::ES_OBJECTPASS))
// default_material->setShaderType(Material::SHADERTYPE_ALPHA_BLEND);
else

View File

@ -28,6 +28,7 @@
#include "graphics/shaders.hpp"
#include "graphics/shared_gpu_objects.hpp"
#include "graphics/stk_mesh_scene_node.hpp"
#include "graphics/weather.hpp"
#include "io/file_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/kart_model.hpp"
@ -1638,6 +1639,22 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode,
}
PROFILER_POP_CPU_MARKER();
}
// Handle lightning rendering
{
PROFILER_PUSH_CPU_MARKER("- Lightning", 0xFF, 0x00, 0x00);
ScopedGPUTimer Timer(irr_driver->getGPUTimer(Q_LIGHTNING));
if (World::getWorld() != NULL)
{
Weather* m_weather = World::getWorld()->getWeather();
if (m_weather != NULL && m_weather->shouldLightning())
{
renderLightning(m_weather->getIntensity());
}
}
PROFILER_POP_CPU_MARKER();
}
// Workaround a bug with srgb fbo on sandy bridge windows
if (!CVS->isARBUniformBufferObjectUsable())

View File

@ -151,7 +151,6 @@ void GL3RenderTarget::draw2DImage(const irr::core::rect<s32>& dest_rect,
{
irr::core::rect<s32> source_rect(0, 0, m_frame_buffer->getWidth(),
m_frame_buffer->getHeight());
draw2DImageFromRTT(m_texture_id,
m_frame_buffer->getWidth(), m_frame_buffer->getHeight(),
dest_rect, source_rect,

View File

@ -66,8 +66,24 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type)
std::ostringstream code;
code << "#version " << CVS->getGLSLVersion()<<"\n";
// Some drivers report that the compute shaders extension is available,
// but they report only OpenGL 3.x version, and thus these extensions
// must be enabled manually. Otherwise the shaders compilation will fail
// because STK tries to use extensions which are available, but disabled
// by default.
if (type == GL_COMPUTE_SHADER)
{
if (CVS->isARBComputeShaderUsable())
code << "#extension GL_ARB_compute_shader : enable\n";
if (CVS->isARBImageLoadStoreUsable())
code << "#extension GL_ARB_shader_image_load_store : enable\n";
if (CVS->isARBArraysOfArraysUsable())
code << "#extension GL_ARB_arrays_of_arrays : enable\n";
}
if (CVS->isAMDVertexShaderLayerUsable())
code << "#extension GL_AMD_vertex_shader_layer : enable\n";
if (CVS->isAZDOEnabled())
{
code << "#extension GL_ARB_bindless_texture : enable\n";
@ -80,7 +96,7 @@ GLuint ShaderBase::loadShader(const std::string &file, unsigned type)
code << "#define VSLayer\n";
if (CVS->needsRGBBindlessWorkaround())
code << "#define SRGBBindlessFix\n";
//shader compilation fails with some drivers if there is no precision qualifier
if (type == GL_FRAGMENT_SHADER)
code << "precision mediump float;\n";

View File

@ -25,8 +25,8 @@
#include "graphics/material_manager.hpp"
#include "graphics/stk_mesh_scene_node.hpp"
#include "io/file_manager.hpp"
#include "karts/controller/controller.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/controller.hpp"
#include "karts/kart_properties.hpp"
#include "karts/max_speed.hpp"
#include "modes/world.hpp"
@ -67,11 +67,9 @@ SlipStream::SlipStream(AbstractKart* kart) : MovingTexture(0, 0), m_kart(kart)
setTextureMatrix(&(m_node->getMaterial(0).getTextureMatrix(0)));
m_slipstream_time = 0.0f;
float length = m_kart->getKartProperties()->getSlipstreamLength() *
m_kart->getPlayerDifficulty()->getSlipstreamLength();
float length = m_kart->getKartProperties()->getSlipstreamLength();
float kw = m_kart->getKartWidth();
float ew = m_kart->getKartProperties()->getSlipstreamWidth() *
m_kart->getPlayerDifficulty()->getSlipstreamWidth();
float ew = m_kart->getKartProperties()->getSlipstreamWidth();
float kl = m_kart->getKartLength();
Vec3 p[4];
@ -314,8 +312,7 @@ void SlipStream::setIntensity(float f, const AbstractKart *kart)
bool SlipStream::isSlipstreamReady() const
{
return m_slipstream_time>
m_kart->getKartProperties()->getSlipstreamCollectTime() *
m_kart->getPlayerDifficulty()->getSlipstreamCollectTime();
m_kart->getKartProperties()->getSlipstreamCollectTime();
} // isSlipstreamReady
//-----------------------------------------------------------------------------
@ -329,16 +326,12 @@ void SlipStream::updateSlipstreamPower()
if(m_slipstream_mode==SS_USE)
{
setIntensity(2.0f, NULL);
const KartProperties *kp=m_kart->getKartProperties();
const KartProperties *kp = m_kart->getKartProperties();
m_kart->increaseMaxSpeed(MaxSpeed::MS_INCREASE_SLIPSTREAM,
kp->getSlipstreamMaxSpeedIncrease() *
m_kart->getPlayerDifficulty()->getSlipstreamMaxSpeedIncrease(),
kp->getSlipstreamAddPower() *
m_kart->getPlayerDifficulty()->getSlipstreamAddPower(),
kp->getSlipstreamDuration() *
m_kart->getPlayerDifficulty()->getSlipstreamDuration(),
kp->getSlipstreamFadeOutTime() *
m_kart->getPlayerDifficulty()->getSlipstreamFadeOutTime());
kp->getSlipstreamMaxSpeedIncrease(),
kp->getSlipstreamAddPower(),
kp->getSlipstreamDuration(),
kp->getSlipstreamFadeOutTime());
}
} // upateSlipstreamPower
@ -368,6 +361,8 @@ void SlipStream::setDebugColor(const video::SColor &color)
*/
void SlipStream::update(float dt)
{
const KartProperties *kp = m_kart->getKartProperties();
// Low level AIs should not do any slipstreaming.
if(m_kart->getController()->disableSlipstreamBonus())
return;
@ -394,8 +389,7 @@ void SlipStream::update(float dt)
// not moving. This is useful for debugging the graphics of SS-ing.
//#define DISPLAY_SLIPSTREAM_WITH_0_SPEED_FOR_DEBUGGING
#ifndef DISPLAY_SLIPSTREAM_WITH_0_SPEED_FOR_DEBUGGING
if(m_kart->getSpeed()<m_kart->getKartProperties()->getSlipstreamMinSpeed() *
m_kart->getPlayerDifficulty()->getSlipstreamMinSpeed())
if(m_kart->getSpeed() < kp->getSlipstreamMinSpeed())
{
setIntensity(0, NULL);
m_slipstream_mode = SS_NONE;
@ -436,9 +430,7 @@ void SlipStream::update(float dt)
// entirely sure if this makes sense, but it makes it easier to
// give karts different slipstream properties.
#ifndef DISPLAY_SLIPSTREAM_WITH_0_SPEED_FOR_DEBUGGING
if(m_target_kart->getSpeed() <
m_kart->getKartProperties()->getSlipstreamMinSpeed() *
m_kart->getPlayerDifficulty()->getSlipstreamMinSpeed())
if (m_target_kart->getSpeed() < kp->getSlipstreamMinSpeed())
{
if(UserConfigParams::m_slipstream_debug &&
m_kart->getController()->isPlayerController())
@ -452,8 +444,7 @@ void SlipStream::update(float dt)
// slipstream length+0.5*kart_length()+0.5*target_kart_length
// away from the other kart
Vec3 delta = m_kart->getXYZ() - m_target_kart->getXYZ();
float l = m_kart->getKartProperties()->getSlipstreamLength() *
m_kart->getPlayerDifficulty()->getSlipstreamLength()
float l = kp->getSlipstreamLength()
+ 0.5f*( m_target_kart->getKartLength()
+m_kart->getKartLength() );
if(delta.length2_2d() > l*l)
@ -493,9 +484,7 @@ void SlipStream::update(float dt)
{
m_slipstream_mode = SS_USE;
m_kart->handleZipper();
m_slipstream_time =
m_kart->getKartProperties()->getSlipstreamCollectTime() *
m_kart->getPlayerDifficulty()->getSlipstreamCollectTime();
m_slipstream_time = kp->getSlipstreamCollectTime();
return;
}
}
@ -516,8 +505,7 @@ void SlipStream::update(float dt)
setIntensity(m_slipstream_time, m_target_kart);
m_slipstream_mode = SS_COLLECT;
if(m_slipstream_time>m_kart->getKartProperties()->getSlipstreamCollectTime() *
m_kart->getPlayerDifficulty()->getSlipstreamCollectTime())
if (m_slipstream_time > kp->getSlipstreamCollectTime())
{
setIntensity(1.0f, m_target_kart);
}

View File

@ -20,7 +20,6 @@
#include "audio/sfx_manager.hpp"
#include "graphics/weather.hpp"
#include "modes/world.hpp"
#include "states_screens/race_gui.hpp"
#include "utils/random_generator.hpp"
@ -28,11 +27,12 @@
Weather::Weather(bool lightning, std::string sound)
{
m_lightning = lightning;
m_lightning_enabled = lightning;
m_thunder_sound = NULL;
m_weather_sound = NULL;
m_lightning = 0.0f;
if (m_lightning)
if (m_lightning_enabled)
{
m_thunder_sound = SFXManager::get()->createSoundSource("thunder");
}
@ -61,27 +61,30 @@ Weather::~Weather()
void Weather::update(float dt)
{
if (m_lightning)
if (!m_lightning_enabled)
return;
if (World::getWorld()->getRaceGUI() == NULL)
return;
m_next_lightning -= dt;
if (m_next_lightning < 0.0f)
{
m_next_lightning -= dt;
if (m_next_lightning < 0.0f)
startLightning();
if (m_thunder_sound)
{
RaceGUIBase* gui_base = World::getWorld()->getRaceGUI();
if (gui_base != NULL)
{
gui_base->doLightning();
if (m_thunder_sound)
{
m_thunder_sound->play();
}
}
RandomGenerator g;
m_next_lightning = 35 + (float)g.get(35);
m_thunder_sound->play();
}
RandomGenerator g;
m_next_lightning = 35 + (float)g.get(35);
}
if (m_lightning > 0.0f)
{
m_lightning -= dt;
}
} // update
@ -95,3 +98,12 @@ void Weather::playSound()
m_weather_sound->play();
}
}
irr::core::vector3df Weather::getIntensity()
{
irr::core::vector3df value = {0.7f * m_lightning,
0.7f * m_lightning,
0.7f * std::min(1.0f, m_lightning * 1.5f)};
return value;
}

View File

@ -19,12 +19,15 @@
#ifndef HEADER_WEATHER_HPP
#define HEADER_WEATHER_HPP
#include <vector3d.h>
class SFXBase;
class Weather
{
bool m_lightning;
bool m_lightning_enabled;
float m_next_lightning;
float m_lightning;
SFXBase* m_thunder_sound;
SFXBase* m_weather_sound;
@ -35,6 +38,12 @@ public:
void update(float dt);
void playSound();
/** Set the flag that a lightning should be shown. */
void startLightning() { m_lightning = 1.0f; }
bool shouldLightning() { return m_lightning > 0.0f; }
irr::core::vector3df getIntensity();
};
#endif

View File

@ -22,6 +22,7 @@
#include "guiengine/engine.hpp"
#include "guiengine/skin.hpp"
#include "io/file_manager.hpp"
#include "utils/string_utils.hpp"
#include "utils/translation.hpp"
#include <clocale>
@ -62,7 +63,8 @@ ScalableFont::ScalableFont(IGUIEnvironment *env, TTFLoadingType type)
// don't grab environment, to avoid circular references
m_video_driver = m_gui_env->getVideoDriver();
m_spritebank = m_gui_env->addEmptySpriteBank((std::to_string(type)).c_str());
m_spritebank = m_gui_env->addEmptySpriteBank(
(StringUtils::toString(type)).c_str());
if (m_spritebank)
m_spritebank->grab();
}
@ -489,6 +491,7 @@ std::set<wchar_t> ScalableFont::getPreloadCharacters(const GUIEngine::TTFLoading
preload_char.insert((wchar_t)i); //Include basic Latin too, starting from A (char code 65)
setlocale(LC_ALL, "en_US.UTF8");
std::set<wchar_t> upper;
std::set<wchar_t>::iterator it = preload_char.begin();
while (it != preload_char.end())
@ -496,7 +499,15 @@ std::set<wchar_t> ScalableFont::getPreloadCharacters(const GUIEngine::TTFLoading
//Only use all capital letter for bold char with latin (<640 of char code).
//Remove all characters (>char code 8191) not used by the title
if (((iswlower((wchar_t)*it) || !iswalpha((wchar_t)*it)) && *it < 640) || *it > 8191)
{
if (*it < 8192 && iswalpha((wchar_t)*it))
{
//Make sure we include all upper case letters,
//because the title font shows all characters as capital letters
upper.insert(towupper((wchar_t)*it));
}
it = preload_char.erase(it);
}
else
++it;
}
@ -505,6 +516,7 @@ std::set<wchar_t> ScalableFont::getPreloadCharacters(const GUIEngine::TTFLoading
for (u32 i = 32; i < 65; ++i)
preload_char.insert((wchar_t)i); //Include basic symbol (from space (char code 32) to @(char code 64))
preload_char.insert(upper.begin(), upper.end());
preload_char.insert((wchar_t)160); //Non-breaking space
//Remove Ordinal indicator (char code 170 and 186)
@ -824,7 +836,7 @@ void ScalableFont::doDraw(const core::stringw& text,
[GUIEngine::getFont()->getSpriteNoFromChar(&c)].Frames[0].textureNumber
== m_spritebank->getTextureCount() - 1) //Prevent overwriting texture used by billboard text
{
Log::debug("ScalableFont::doDraw",
Log::debug("ScalableFont::doDraw",
"Character used by billboard text is in the last "
"glyph page of normal font. Create a new glyph "
"page for new characters inserted later to prevent "
@ -1000,12 +1012,12 @@ void ScalableFont::doDraw(const core::stringw& text,
if (fallback[n] || m_type == T_BOLD)
{
video::SColor title_colors[] = {GUIEngine::getSkin()->getColor("font::top" ),
GUIEngine::getSkin()->getColor("font::bottom"),
GUIEngine::getSkin()->getColor("font::top" ),
GUIEngine::getSkin()->getColor("font::bottom")
};
video::SColor top = GUIEngine::getSkin()->getColor("font::top");
video::SColor bottom = GUIEngine::getSkin()->getColor("font::bottom");
top.setAlpha(color.getAlpha());
bottom.setAlpha(color.getAlpha());
video::SColor title_colors[] = {top, bottom, top, bottom};
if (charCollector != NULL)
{
charCollector->collectChar(texture,

View File

@ -1959,12 +1959,6 @@ void Skin::process3DPane(IGUIElement *element, const core::recti &rect,
{
ModelViewWidget* mvw = dynamic_cast<ModelViewWidget*>(widget);
mvw->drawRTTScene(rect);
/*FrameBuffer* fb = mvw->getFrameBuffer();
if (fb != NULL && fb->getRTT().size() > 0)
{
draw2DImageFromRTT(fb->getRTT()[0], 512, 512,
rect, core::rect<s32>(0, 0, 512, 512), NULL, SColor(255, 255, 255, 255), true);
}*/
}
else if (type == WTYPE_ICON_BUTTON || type == WTYPE_MODEL_VIEW)
{

View File

@ -24,8 +24,18 @@
double click/ctrl click: word select + drag to select whole words, triple click to select line
optional? dragging selected text
numerical
correct the mark position in RTL text, currently you can identify by highlight the text
*/
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
#define UTF16_IS_SURROGATE_LO(c) (((c) & 0xFC00) == 0xDC00)
#define UTF16_IS_SURROGATE_HI(c) (((c) & 0xFC00) == 0xD800)
namespace irr
{
void updateICPos(void* hWnd, s32 x, s32 y, s32 height);
}
#endif
StkTime::TimeType getTime()
{
@ -44,7 +54,11 @@ CGUIEditBox::CGUIEditBox(const wchar_t* text, bool border,
PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER),
CurrentTextRect(0,0,1,1), FrameRect(rectangle)
{
m_rtl = is_rtl;
//m_rtl = is_rtl;
m_rtl = false;
// FIXME quick hack to enable mark movement with keyboard and mouse for rtl language,
// don't know why it's disabled in the first place, because STK fail
// to input unicode characters before?
#ifdef _DEBUG
setDebugName("CGUIEditBox");
@ -229,6 +243,12 @@ bool CGUIEditBox::OnEvent(const SEvent& event)
}
}
break;
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
case EET_IMPUT_METHOD_EVENT:
if (processIMEEvent(event))
return true;
break;
#endif
case EET_KEY_INPUT_EVENT:
if (processKey(event))
return true;
@ -282,7 +302,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
core::stringw s;
s = Text.subString(realmbgn, realmend - realmbgn).c_str();
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
Operator->copyToClipboard(s.c_str());
#else
Operator->copyToClipboard(StringUtils::wideToUtf8(s).c_str());
#endif
}
break;
case KEY_KEY_X:
@ -295,7 +319,11 @@ bool CGUIEditBox::processKey(const SEvent& event)
// copy
core::stringw sc;
sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
Operator->copyToClipboard(sc.c_str());
#else
Operator->copyToClipboard(StringUtils::wideToUtf8(sc).c_str());
#endif
if (isEnabled())
{
@ -324,20 +352,32 @@ bool CGUIEditBox::processKey(const SEvent& event)
const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
// add new character
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
const wchar_t* p = Operator->getTextFromClipboard();
#else
const c8* p = Operator->getTextFromClipboard();
#endif
if (p)
{
if (MarkBegin == MarkEnd)
{
// insert text
core::stringw s = Text.subString(0, CursorPos);
#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
s.append(StringUtils::utf8ToWide(p));
#else
s.append(p);
#endif
s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
if (!Max || s.size()<=Max) // thx to Fish FH for fix
{
Text = s;
#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
s = StringUtils::utf8ToWide(p);
#else
s = p;
#endif
CursorPos += s.size();
}
}
@ -346,13 +386,22 @@ bool CGUIEditBox::processKey(const SEvent& event)
// replace text
core::stringw s = Text.subString(0, realmbgn);
#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
s.append(StringUtils::utf8ToWide(p));
#else
s.append(p);
#endif
s.append( Text.subString(realmend, Text.size()-realmend) );
if (!Max || s.size()<=Max) // thx to Fish FH for fix
{
Text = s;
#ifndef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
s = StringUtils::utf8ToWide(p);
#else
s = p;
#endif
CursorPos = realmbgn + s.size();
}
}
@ -734,10 +783,102 @@ bool CGUIEditBox::processKey(const SEvent& event)
calculateScrollPos();
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
switch(event.KeyInput.Key)
{
// If cursor points the surrogate low, send KEY_LEFT event.
case KEY_UP:
case KEY_DOWN:
if (MultiLine || (WordWrap && BrokenText.size() > 1) )
{
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
{
SEvent leftEvent;
leftEvent = event;
leftEvent.KeyInput.Key = KEY_LEFT;
Environment->postEventFromUser(leftEvent);
}
}
break;
// If cursor points the surrogate low, send a same event.
case KEY_LEFT:
case KEY_RIGHT:
case KEY_DELETE:
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
Environment->postEventFromUser(event);
break;
// If cursor points front of the surrogate high, send a same event.
case KEY_BACK:
if (CursorPos > 0)
{
if (UTF16_IS_SURROGATE_HI(Text[CursorPos-1]))
Environment->postEventFromUser(event);
}
break;
default:
break;
}
#endif
return true;
}
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
bool CGUIEditBox::processIMEEvent(const SEvent& event)
{
switch(event.InputMethodEvent.Event)
{
case EIME_CHAR_INPUT:
inputChar(event.InputMethodEvent.Char);
return true;
case EIME_CHANGE_POS:
{
core::position2di pos = calculateICPos();
IGUIFont* font = OverrideFont;
IGUISkin* skin = Environment->getSkin();
if (!OverrideFont)
font = skin->getFont();
irr::updateICPos(event.InputMethodEvent.Handle, pos.X,pos.Y, font->getDimension(L"|").Height);
return true;
}
default:
break;
}
return false;
}
//! calculate the position of input composition window
core::position2di CGUIEditBox::calculateICPos()
{
core::position2di pos;
IGUIFont* font = OverrideFont;
IGUISkin* skin = Environment->getSkin();
if (!OverrideFont)
font = skin->getFont();
//drop the text that clipping on the right side
if (WordWrap | MultiLine)
{
// todo : It looks like a heavy drinker. Strange!!
pos.X = CurrentTextRect.LowerRightCorner.X - font->getDimension(Text.subString(CursorPos, BrokenTextPositions[getLineFromPos(CursorPos)] + BrokenText[getLineFromPos(CursorPos)].size() - CursorPos).c_str()).Width;
pos.Y = CurrentTextRect.UpperLeftCorner.Y + font->getDimension(L"|").Height + (Border ? 3 : 0) - ((MultiLine | WordWrap) ? 3 : 0);
}
else
{
pos.X = CurrentTextRect.LowerRightCorner.X - font->getDimension(Text.subString(CursorPos, Text.size() - CursorPos).c_str()).Width;
pos.Y = AbsoluteRect.getCenter().Y + (Border ? 3 : 0); //bug? The text is always drawn in the height of the center. SetTextAlignment() doesn't influence.
}
return pos;
}
#endif
//! draws the element and its children
void CGUIEditBox::draw()
{
@ -849,20 +990,12 @@ void CGUIEditBox::draw()
startPos = ml ? BrokenTextPositions[i] : 0;
}
if (m_rtl)
{
font->draw(translations->fribidize(txtLine->c_str()), CurrentTextRect,
OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
false, true, &localClipRect);
}
else
{
// draw normal text
font->draw(txtLine->c_str(), CurrentTextRect,
OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
false, true, &localClipRect);
}
font->draw(translations->fribidize(txtLine->c_str()), CurrentTextRect,
OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
false, true, &localClipRect);
// draw with fribidize no matter what language, because in fribidize function,
// it will return the input pointer if (this->isRTLLanguage()) from Translations::isRTLText
// is false
// draw mark and marked text
if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
@ -1028,6 +1161,13 @@ bool CGUIEditBox::processMouse(const SEvent& event)
if (Environment->hasFocus(this) && !m_rtl)
{
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
{
if (CursorPos > 0)
--CursorPos;
}
#endif
if (MouseMarking)
{
setTextMarkers( MarkBegin, CursorPos );
@ -1042,6 +1182,13 @@ bool CGUIEditBox::processMouse(const SEvent& event)
if (MouseMarking)
{
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
{
if (CursorPos > 0)
--CursorPos;
}
#endif
setTextMarkers( MarkBegin, CursorPos );
calculateScrollPos();
return true;
@ -1054,6 +1201,13 @@ bool CGUIEditBox::processMouse(const SEvent& event)
BlinkStartTime = getTime();
MouseMarking = true;
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
{
if (CursorPos > 0)
--CursorPos;
}
#endif
setTextMarkers(CursorPos, CursorPos );
calculateScrollPos();
return true;
@ -1070,6 +1224,13 @@ bool CGUIEditBox::processMouse(const SEvent& event)
// move cursor
CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
if (UTF16_IS_SURROGATE_LO(Text[CursorPos]))
{
if (CursorPos > 0)
--CursorPos;
}
#endif
s32 newMarkBegin = MarkBegin;
if (!MouseMarking)
newMarkBegin = CursorPos;

View File

@ -137,6 +137,11 @@ using namespace gui;
bool processKey(const SEvent& event);
bool processMouse(const SEvent& event);
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
bool processIMEEvent(const SEvent& event);
//! calculates the input composition position
core::position2di calculateICPos();
#endif
s32 getCursorPos(s32 x, s32 y);
bool MouseMarking;

View File

@ -46,6 +46,8 @@ DynamicRibbonWidget::DynamicRibbonWidget(const bool combo, const bool multi_row)
m_supports_multiplayer = true;
m_scrolling_enabled = true;
m_animated_contents = false;
// Don't initialize m_font here to make lazy loading characters work
m_font = NULL;
// by default, set all players to have no selection in this ribbon
for (unsigned int n=0; n<MAX_PLAYER_COUNT; n++)
@ -730,6 +732,16 @@ void DynamicRibbonWidget::onRibbonWidgetScroll(const int delta_x)
// -----------------------------------------------------------------------------
void DynamicRibbonWidget::setText(const wchar_t *text)
{
Widget::setText(text);
if (m_label != NULL)
m_label->setText(text);
}
// -----------------------------------------------------------------------------
void DynamicRibbonWidget::onRibbonWidgetFocus(RibbonWidget* emitter, const int playerID)
{
if (m_deactivated) return;

View File

@ -302,6 +302,8 @@ namespace GUIEngine
/** \brief callback from IRibbonListener */
virtual void onSelectionChange(){}
virtual void setText(const wchar_t *text);
virtual void update(float delta);
/** Set approximately how many items are expected to be in this ribbon; will help the layout

View File

@ -15,21 +15,19 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "config/user_config.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/widgets/kart_stats_widget.hpp"
#include "utils/string_utils.hpp"
#include <string.h>
#include "karts/abstract_characteristic.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "utils/log.hpp"
#include "utils/string_utils.hpp"
#include "config/user_config.hpp"
#include <IGUIEnvironment.h>
#include <IGUIElement.h>
#include <IGUIButton.h>
#include <string>
using namespace GUIEngine;
using namespace irr::core;
@ -41,6 +39,7 @@ KartStatsWidget::KartStatsWidget(core::recti area, const int player_id,
std::string kart_group, bool multiplayer,
bool display_text) : Widget(WTYPE_DIV)
{
m_title_font = !multiplayer;
m_player_id = player_id;
const std::string default_kart = UserConfigParams::m_default_kart;
@ -81,15 +80,21 @@ KartStatsWidget::KartStatsWidget(core::recti area, const int player_id,
m_children.push_back(skill_bar);
}
m_skills[SKILL_MASS]->setValue((int)(props->getMass()/5));
// Scale the values so they look better
// The scaling factor and offset were found by trial and error.
// It should look nice and you should be able to see the difference between
// different masses or velocities.
m_skills[SKILL_MASS]->setValue((int)
((props->getCombinedCharacteristic()->getMass() - 20) / 4));
m_skills[SKILL_MASS]->setLabel(_("WEIGHT"));
m_skills[SKILL_MASS]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_mass", m_player_id);
m_skills[SKILL_SPEED]->setValue((int)((props->getAbsMaxSpeed()-20)*9));
m_skills[SKILL_SPEED]->setValue((int)
((props->getCombinedCharacteristic()->getEngineMaxSpeed() - 15) * 6));
m_skills[SKILL_SPEED]->setLabel(_("SPEED"));
m_skills[SKILL_SPEED]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_speed", m_player_id);
m_skills[SKILL_POWER]->setValue((int)(props->getAvgPower()));
m_skills[SKILL_POWER]->setValue((int) ((props->getAvgPower() - 30) / 20));
m_skills[SKILL_POWER]->setLabel(_("POWER"));
m_skills[SKILL_POWER]->m_properties[PROP_ID] = StringUtils::insertValues("@p%i_power", m_player_id);
@ -149,7 +154,7 @@ void KartStatsWidget::setSize(const int x, const int y, const int w, const int h
// -- sizes
m_skill_bar_w = w;
m_skill_bar_h = GUIEngine::getTitleFontHeight();
m_skill_bar_h = (m_title_font ? GUIEngine::getTitleFontHeight() : GUIEngine::getFontHeight());
// for shrinking effect
if (h < 175)

View File

@ -310,6 +310,8 @@ void ModelViewWidget::clearRttProvider()
void ModelViewWidget::drawRTTScene(const irr::core::rect<s32>& dest_rect) const
{
glEnable(GL_FRAMEBUFFER_SRGB);
m_render_target->draw2DImage(dest_rect, NULL, video::SColor(255, 255, 255, 255), true);
glDisable(GL_FRAMEBUFFER_SRGB);
}

View File

@ -52,6 +52,7 @@ namespace irr {
# include <dirent.h>
# include <unistd.h>
#else
# define WIN32_LEAN_AND_MEAN
# include <direct.h>
# include <windows.h>
# include <stdio.h>

View File

@ -225,6 +225,7 @@ void Attachment::clear()
*/
void Attachment::hitBanana(Item *item, int new_attachment)
{
const KartProperties *kp = m_kart->getKartProperties();
const StateManager::ActivePlayer *const ap = m_kart->getController()
->getPlayer();
if(ap && ap->getConstProfile()==PlayerManager::getCurrentPlayer())
@ -266,9 +267,7 @@ void Attachment::hitBanana(Item *item, int new_attachment)
// default time. This is necessary to avoid that a kart lands on the
// same banana again once the explosion animation is finished, giving
// the kart the same penalty twice.
float f = std::max(item->getDisableTime(),
m_kart->getKartProperties()->getExplosionTime() *
m_kart->getPlayerDifficulty()->getExplosionTime() + 2.0f);
float f = std::max(item->getDisableTime(), kp->getExplosionDuration() + 2.0f);
item->setDisableTime(f);
break;
}
@ -296,7 +295,7 @@ void Attachment::hitBanana(Item *item, int new_attachment)
switch (new_attachment)
{
case 0:
set( ATTACH_PARACHUTE,stk_config->m_parachute_time+leftover_time);
set(ATTACH_PARACHUTE, kp->getParachuteDuration() + leftover_time);
m_initial_speed = m_kart->getSpeed();
// if going very slowly or backwards,
@ -310,12 +309,12 @@ void Attachment::hitBanana(Item *item, int new_attachment)
// sound -> playSfx ( SOUND_SHOOMF ) ;
break ;
case 2:
set( ATTACH_ANVIL, stk_config->m_anvil_time+leftover_time);
set(ATTACH_ANVIL, kp->getAnvilDuration() + leftover_time);
// if ( m_kart == m_kart[0] )
// sound -> playSfx ( SOUND_SHOOMF ) ;
// Reduce speed once (see description above), all other changes are
// handled in Kart::updatePhysics
m_kart->adjustSpeed(stk_config->m_anvil_speed_factor);
m_kart->adjustSpeed(kp->getAnvilSpeedFactor());
m_kart->updateWeight();
break ;
} // switch
@ -420,12 +419,14 @@ void Attachment::update(float dt)
// This percentage is based on the ratio of
// initial_speed / initial_max_speed
float f = m_initial_speed / stk_config->m_parachute_max_speed;
const KartProperties *kp = m_kart->getKartProperties();
float f = m_initial_speed / kp->getParachuteMaxSpeed();
if (f > 1.0f) f = 1.0f; // cap fraction
if (m_kart->getSpeed() <= m_initial_speed *
(stk_config->m_parachute_lbound_fraction +
f * ( stk_config->m_parachute_ubound_fraction
- stk_config->m_parachute_lbound_fraction)))
(kp->getParachuteLboundFraction() +
f * (kp->getParachuteUboundFraction()
- kp->getParachuteLboundFraction())))
{
m_time_left = -1;
}
@ -507,6 +508,12 @@ void Attachment::update(float dt)
clear();
} // update
// ----------------------------------------------------------------------------
float Attachment::weightAdjust() const
{
return m_type == ATTACH_ANVIL ? m_kart->getKartProperties()->getAnvilWeight() : 0.0f;
}
// ----------------------------------------------------------------------------
/** Inform any eventual plugin when an animation is done. */
void Attachment::OnAnimationEnd(scene::IAnimatedMeshSceneNode* node)

View File

@ -130,8 +130,7 @@ public:
AbstractKart* 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; }
float weightAdjust() const;
// ------------------------------------------------------------------------
/** Return the currently associated scene node (used by e.g the swatter) */
scene::IAnimatedMeshSceneNode* getNode() {return m_node;}

View File

@ -176,8 +176,7 @@ bool Plunger::hit(AbstractKart *kart, PhysicalObject *obj)
}
else
{
m_keep_alive = m_owner->getKartProperties()->getRubberBandDuration() *
m_owner->getPlayerDifficulty()->getRubberBandDuration();
m_keep_alive = m_owner->getKartProperties()->getPlungerBandDuration();
// Make this object invisible by placing it faaar down. Not that if this
// objects is simply removed from the scene graph, it might be auto-deleted

View File

@ -171,6 +171,8 @@ void Powerup::adjustSound()
*/
void Powerup::use()
{
const KartProperties *kp = m_owner->getKartProperties();
// The player gets an achievement point for using a powerup
StateManager::ActivePlayer * player = m_owner->getController()->getPlayer();
if (m_type != PowerupManager::POWERUP_NOTHING &&
@ -221,9 +223,7 @@ void Powerup::use()
case PowerupManager::POWERUP_SWATTER:
m_owner->getAttachment()
->set(Attachment::ATTACH_SWATTER,
m_owner->getKartProperties()->getSwatterDuration() *
m_owner->getPlayerDifficulty()->getSwatterDuration());
->set(Attachment::ATTACH_SWATTER, kp->getSwatterDuration());
break;
case PowerupManager::POWERUP_BUBBLEGUM:
@ -258,12 +258,12 @@ void Powerup::use()
if (m_owner->getIdent() == "nolok")
{
m_owner->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD,
stk_config->m_bubblegum_shield_time);
kp->getBubblegumShieldDuration());
}
else
{
m_owner->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD,
stk_config->m_bubblegum_shield_time);
kp->getBubblegumShieldDuration());
}
}
else // using a bubble gum while still having a shield
@ -271,12 +271,12 @@ void Powerup::use()
if (m_owner->getIdent() == "nolok")
{
m_owner->getAttachment()->set(Attachment::ATTACH_NOLOK_BUBBLEGUM_SHIELD,
stk_config->m_bubblegum_shield_time + m_owner->getShieldTime());
kp->getBubblegumShieldDuration() + m_owner->getShieldTime());
}
else
{
m_owner->getAttachment()->set(Attachment::ATTACH_BUBBLEGUM_SHIELD,
stk_config->m_bubblegum_shield_time + m_owner->getShieldTime());
kp->getBubblegumShieldDuration() + m_owner->getShieldTime());
}
}
@ -300,9 +300,9 @@ void Powerup::use()
if(kart->getPosition() == 1)
{
kart->getAttachment()->set(Attachment::ATTACH_ANVIL,
stk_config->m_anvil_time);
kp->getAnvilDuration());
kart->updateWeight();
kart->adjustSpeed(stk_config->m_anvil_speed_factor*0.5f);
kart->adjustSpeed(kp->getAnvilSpeedFactor() * 0.5f);
// should we position the sound at the kart that is hit,
// or the kart "throwing" the anvil? Ideally it should be both.
@ -337,9 +337,8 @@ void Powerup::use()
}
if(m_owner->getPosition() > kart->getPosition())
{
kart->getAttachment()
->set(Attachment::ATTACH_PARACHUTE,
stk_config->m_parachute_time_other);
kart->getAttachment()->set(Attachment::ATTACH_PARACHUTE,
kp->getParachuteDurationOther());
if(kart->getController()->isPlayerController())
player_kart = kart;

View File

@ -134,6 +134,8 @@ void RubberBand::updatePosition()
*/
void RubberBand::update(float dt)
{
const KartProperties *kp = m_owner->getKartProperties();
if(m_owner->isEliminated())
{
// Rubber band snaps
@ -149,8 +151,7 @@ void RubberBand::update(float dt)
// Check for rubber band snapping
// ------------------------------
float l = (m_end_position-k).length2();
float max_len = m_owner->getKartProperties()->getRubberBandMaxLength() *
m_owner->getPlayerDifficulty()->getRubberBandMaxLength();
float max_len = kp->getPlungerBandMaxLength();
if(l>max_len*max_len)
{
// Rubber band snaps
@ -163,8 +164,7 @@ void RubberBand::update(float dt)
// ----------------------------
if(m_attached_state!=RB_TO_PLUNGER)
{
float force = m_owner->getKartProperties()->getRubberBandForce() *
m_owner->getPlayerDifficulty()->getRubberBandForce();
float force = kp->getPlungerBandForce();
Vec3 diff = m_end_position-k;
// detach rubber band if kart gets very close to hit point
@ -180,12 +180,10 @@ void RubberBand::update(float dt)
diff.normalize(); // diff can't be zero here
m_owner->getBody()->applyCentralForce(diff*force);
m_owner->increaseMaxSpeed(MaxSpeed::MS_INCREASE_RUBBER,
m_owner->getKartProperties()->getRubberBandSpeedIncrease() *
m_owner->getPlayerDifficulty()->getRubberBandSpeedIncrease(),
kp->getPlungerBandSpeedIncrease(),
/*engine_force*/ 0.0f,
/*duration*/0.1f,
m_owner->getKartProperties()->getRubberBandFadeOutTime() *
m_owner->getPlayerDifficulty()->getRubberBandFadeOutTime());
kp->getPlungerBandFadeOutTime());
if(m_attached_state==RB_TO_KART)
m_hit_kart->getBody()->applyCentralForce(diff*(-force));
}

View File

@ -59,7 +59,10 @@ Swatter::Swatter(AbstractKart *kart, bool was_bomb,
: AttachmentPlugin(kart)
{
m_animation_phase = SWATTER_AIMING;
m_discard_now = false;
m_discard_timeout = 0.0f;
m_target = NULL;
m_closest_kart = NULL;
m_removing_bomb = was_bomb;
m_bomb_scene_node = bomb_scene_node;
m_swat_bomb_frame = 0.0f;
@ -112,89 +115,106 @@ Swatter::~Swatter()
*/
bool Swatter::updateAndTestFinished(float dt)
{
if (m_removing_bomb)
if (!m_discard_now)
{
m_swat_bomb_frame += dt*25.0f;
m_scene_node->setRotation(core::vector3df(0.0, -180.0, 0.0));
m_scene_node->setCurrentFrame(m_swat_bomb_frame);
if (m_swat_bomb_frame >= 32.5f && m_bomb_scene_node != NULL)
if (m_removing_bomb)
{
m_bomb_scene_node->setPosition(m_bomb_scene_node->getPosition() +
core::vector3df(-dt*15.0f, 0.0f, 0.0f) );
m_bomb_scene_node->setRotation(m_bomb_scene_node->getRotation() +
core::vector3df(-dt*15.0f, 0.0f, 0.0f) );
}
m_swat_bomb_frame += dt*25.0f;
m_scene_node->setRotation(core::vector3df(0.0, -180.0, 0.0));
if (m_swat_bomb_frame >= m_scene_node->getEndFrame())
{
return true;
}
else if (m_swat_bomb_frame >= 35)
{
if (m_bomb_scene_node != NULL)
m_scene_node->setCurrentFrame(m_swat_bomb_frame);
if (m_swat_bomb_frame >= 32.5f && m_bomb_scene_node != NULL)
{
irr_driver->removeNode(m_bomb_scene_node);
m_bomb_scene_node = NULL;
m_bomb_scene_node->setPosition(m_bomb_scene_node
->getPosition() + core::vector3df(-dt*15.0f, 0.0f, 0.0f) );
m_bomb_scene_node->setRotation(m_bomb_scene_node
->getRotation() + core::vector3df(-dt*15.0f, 0.0f, 0.0f) );
}
} // bom_frame > 35
return false;
} // if removing bomb
switch(m_animation_phase)
{
case SWATTER_AIMING:
{
chooseTarget();
pointToTarget();
if(!m_target) break;
// Is the target too near?
float dist_to_target2 =
(m_target->getXYZ()- Vec3(m_scene_node->getAbsolutePosition()))
.length2();
float min_dist2
= m_kart->getKartProperties()->getSwatterDistance2();
if(dist_to_target2 < min_dist2)
if (m_swat_bomb_frame >= m_scene_node->getEndFrame())
{
// Start squashing
m_animation_phase = SWATTER_TO_TARGET;
// Setup the animation
m_scene_node->setCurrentFrame(0.0f);
m_scene_node->setLoopMode(false);
m_scene_node->setAnimationSpeed(SWATTER_ANIMATION_SPEED);
return true;
}
}
break;
case SWATTER_TO_TARGET:
{
pointToTarget();
const float middle_frame = m_scene_node->getEndFrame()/2.0f;
float current_frame = m_scene_node->getFrameNr();
// Did we just finish the first part of the movement?
if(current_frame >= middle_frame)
else if (m_swat_bomb_frame >= 35)
{
// Squash the karts and items around and
// change the current phase
squashThingsAround();
m_animation_phase = SWATTER_FROM_TARGET;
}
}
break;
if (m_bomb_scene_node != NULL)
{
irr_driver->removeNode(m_bomb_scene_node);
m_bomb_scene_node = NULL;
}
} // bom_frame > 35
case SWATTER_FROM_TARGET:
break;
return false;
} // if removing bomb
switch(m_animation_phase)
{
case SWATTER_AIMING:
{
chooseTarget();
pointToTarget();
if(!m_target || !m_closest_kart) break;
// Get the node corresponding to the joint at the center of the
// swatter (by swatter, I mean the thing hold in the hand, not
// the whole thing)
scene::ISceneNode* swatter_node =
m_scene_node->getJointNode("Swatter");
assert(swatter_node);
Vec3 swatter_pos = swatter_node->getAbsolutePosition();
float dist2 = (m_closest_kart->getXYZ()-swatter_pos).length2();
float min_dist2
= m_kart->getKartProperties()->getSwatterDistance();
if(dist2 < min_dist2)
{
// Start squashing
m_animation_phase = SWATTER_TO_TARGET;
// Setup the animation
m_scene_node->setCurrentFrame(0.0f);
m_scene_node->setLoopMode(false);
m_scene_node->setAnimationSpeed(SWATTER_ANIMATION_SPEED);
// Play swat sound
m_swat_sound->setPosition(swatter_pos);
m_swat_sound->play();
}
}
break;
case SWATTER_TO_TARGET:
{
pointToTarget();
const float middle_frame = m_scene_node->getEndFrame()/2.0f;
float current_frame = m_scene_node->getFrameNr();
// Did we just finish the first part of the movement?
if(current_frame >= middle_frame)
{
// Squash the karts and items around and
// change the current phase
squashThingsAround();
m_animation_phase = SWATTER_FROM_TARGET;
if (race_manager->getMinorMode()==
RaceManager::MINOR_MODE_3_STRIKES)
{
// Remove swatter from kart in 3 strikes battle
// after one successful hit
m_discard_now = true;
}
}
}
break;
case SWATTER_FROM_TARGET:
break;
}
}
else
m_discard_timeout += dt;
// If the swatter is used up, trigger cleaning up
// TODO: use a timeout
// TODO: how does it work currently...?
return false;
return (m_discard_now && m_discard_timeout > 0.5f ? true : false);
} // updateAndTestFinished
// ----------------------------------------------------------------------------
@ -234,6 +254,7 @@ void Swatter::chooseTarget()
}
}
m_target = closest_kart; // may be NULL
m_closest_kart = closest_kart;
}
// ----------------------------------------------------------------------------
@ -264,58 +285,30 @@ void Swatter::pointToTarget()
*/
void Swatter::squashThingsAround()
{
const KartProperties* kp = m_kart->getKartProperties();
// Square of the minimum distance
float min_dist2 = kp->getSwatterDistance2();
const World* world = World::getWorld();
const KartProperties *kp = m_kart->getKartProperties();
// Get the node corresponding to the joint at the center of the swatter
// (by swatter, I mean the thing hold in the hand, not the whole thing)
scene::ISceneNode* swatter_node = m_scene_node->getJointNode("Swatter");
assert(swatter_node);
Vec3 swatter_pos = swatter_node->getAbsolutePosition();
m_closest_kart->setSquash(kp->getSwatterSquashDuration(),
kp->getSwatterSquashSlowdown());
m_swat_sound->setPosition(swatter_pos);
m_swat_sound->play();
// Squash karts around
for(unsigned int i=0; i<world->getNumKarts(); i++)
//Handle achievement if the swatter is used by the current player
const StateManager::ActivePlayer *const ap = m_kart->getController()
->getPlayer();
if (ap && ap->getConstProfile() == PlayerManager::getCurrentPlayer())
{
AbstractKart *kart = world->getKart(i);
// TODO: isSwatterReady()
if(kart->isEliminated() || kart==m_kart)
continue;
// don't swat an already hurt kart
if (kart->isInvulnerable() || kart->isSquashed())
continue;
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_MOSQUITO,
"swatter", 1);
}
float dist2 = (kart->getXYZ()-swatter_pos).length2();
if(dist2 >= min_dist2) continue; // too far away, ignore this kart
kart->setSquash(kp->getSquashDuration() * kart->getPlayerDifficulty()->getSquashDuration(),
kp->getSquashSlowdown() * kart->getPlayerDifficulty()->getSquashSlowdown());
//Handle achievement if the swatter is used by the current player
const StateManager::ActivePlayer *const ap = m_kart->getController()
->getPlayer();
if (ap && ap->getConstProfile() == PlayerManager::getCurrentPlayer())
{
PlayerManager::increaseAchievement(AchievementInfo::ACHIEVE_MOSQUITO,
"swatter", 1);
}
if (kart->getAttachment()->getType()==Attachment::ATTACH_BOMB)
{ // make bomb explode
kart->getAttachment()->update(10000);
HitEffect *he = new Explosion(m_kart->getXYZ(), "explosion", "explosion.xml");
if(m_kart->getController()->isPlayerController())
he->setPlayerKartHit();
projectile_manager->addHitEffect(he);
ExplosionAnimation::create(kart);
} // if kart has bomb attached
World::getWorld()->kartHit(kart->getWorldKartId());
} // for i < num_kartrs
if (m_closest_kart->getAttachment()->getType()==Attachment::ATTACH_BOMB)
{ // make bomb explode
m_closest_kart->getAttachment()->update(10000);
HitEffect *he = new Explosion(m_kart->getXYZ(), "explosion", "explosion.xml");
if(m_kart->getController()->isPlayerController())
he->setPlayerKartHit();
projectile_manager->addHitEffect(he);
ExplosionAnimation::create(m_closest_kart);
} // if kart has bomb attached
World::getWorld()->kartHit(m_closest_kart->getWorldKartId());
// TODO: squash items
} // squashThingsAround

View File

@ -49,9 +49,17 @@ private:
enum {SWATTER_AIMING, SWATTER_TO_TARGET, SWATTER_FROM_TARGET}
m_animation_phase;
/** True if the swatter will be discarded now. */
bool m_discard_now;
/** Require for the sfx to complete. */
float m_discard_timeout;
/** The kart the swatter is aiming at. */
Moveable *m_target;
AbstractKart *m_closest_kart;
SFXBase *m_swat_sound;
/** True if the swatter is removing an attached bomb. */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,383 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_ABSTRACT_CHARACTERISTICS_HPP
#define HEADER_ABSTRACT_CHARACTERISTICS_HPP
#include <string>
#include <vector>
class InterpolationArray;
/**
* Characteristics are the properties of a kart that influence
* gameplay mechanics.
* The biggest parts are:
* - Physics
* - Visuals
* - Items
* - and miscellaneous properties like nitro and startup boost.
*
* The documentation of these properties can be found in
* the kart_characteristics.xml file.
* Large parts of this file are generated by tools/create_kart_properties.py.
* Please don't change the generated code here, instead change the script,
* regenerate the code and overwrite the whole generated part with the result.
*/
class AbstractCharacteristic
{
public:
union Value
{
float *f;
bool *b;
std::vector<float> *fv;
InterpolationArray *ia;
Value(float *f) : f(f) {}
Value(bool *b) : b(b) {}
Value(std::vector<float> *fv) : fv(fv) {}
Value(InterpolationArray *ia) : ia(ia) {}
};
enum ValueType
{
TYPE_FLOAT,
TYPE_BOOL,
TYPE_FLOAT_VECTOR,
TYPE_INTERPOLATION_ARRAY
};
enum CharacteristicType
{
// Script-generated content generated by tools/create_kart_properties.py enum
// Please don't change the following tag. It will be automatically detected
// by the script and replace the contained content.
// To update the code, use tools/update_characteristics.py
/* <characteristics-start enum> */
// Suspension
SUSPENSION_STIFFNESS,
SUSPENSION_REST,
SUSPENSION_TRAVEL,
SUSPENSION_EXP_SPRING_RESPONSE,
SUSPENSION_MAX_FORCE,
// Stability
STABILITY_ROLL_INFLUENCE,
STABILITY_CHASSIS_LINEAR_DAMPING,
STABILITY_CHASSIS_ANGULAR_DAMPING,
STABILITY_DOWNWARD_IMPULSE_FACTOR,
STABILITY_TRACK_CONNECTION_ACCEL,
STABILITY_SMOOTH_FLYING_IMPULSE,
// Turn
TURN_RADIUS,
TURN_TIME_RESET_STEER,
TURN_TIME_FULL_STEER,
// Engine
ENGINE_POWER,
ENGINE_MAX_SPEED,
ENGINE_BRAKE_FACTOR,
ENGINE_BRAKE_TIME_INCREASE,
ENGINE_MAX_SPEED_REVERSE_RATIO,
// Gear
GEAR_SWITCH_RATIO,
GEAR_POWER_INCREASE,
// Mass
MASS,
// Wheels
WHEELS_DAMPING_RELAXATION,
WHEELS_DAMPING_COMPRESSION,
// Camera
CAMERA_DISTANCE,
CAMERA_FORWARD_UP_ANGLE,
CAMERA_BACKWARD_UP_ANGLE,
// Jump
JUMP_ANIMATION_TIME,
// Lean
LEAN_MAX,
LEAN_SPEED,
// Anvil
ANVIL_DURATION,
ANVIL_WEIGHT,
ANVIL_SPEED_FACTOR,
// Parachute
PARACHUTE_FRICTION,
PARACHUTE_DURATION,
PARACHUTE_DURATION_OTHER,
PARACHUTE_LBOUND_FRACTION,
PARACHUTE_UBOUND_FRACTION,
PARACHUTE_MAX_SPEED,
// Bubblegum
BUBBLEGUM_DURATION,
BUBBLEGUM_SPEED_FRACTION,
BUBBLEGUM_TORQUE,
BUBBLEGUM_FADE_IN_TIME,
BUBBLEGUM_SHIELD_DURATION,
// Zipper
ZIPPER_DURATION,
ZIPPER_FORCE,
ZIPPER_SPEED_GAIN,
ZIPPER_MAX_SPEED_INCREASE,
ZIPPER_FADE_OUT_TIME,
// Swatter
SWATTER_DURATION,
SWATTER_DISTANCE,
SWATTER_SQUASH_DURATION,
SWATTER_SQUASH_SLOWDOWN,
// Plunger
PLUNGER_BAND_MAX_LENGTH,
PLUNGER_BAND_FORCE,
PLUNGER_BAND_DURATION,
PLUNGER_BAND_SPEED_INCREASE,
PLUNGER_BAND_FADE_OUT_TIME,
PLUNGER_IN_FACE_TIME,
// Startup
STARTUP_TIME,
STARTUP_BOOST,
// Rescue
RESCUE_DURATION,
RESCUE_VERT_OFFSET,
RESCUE_HEIGHT,
// Explosion
EXPLOSION_DURATION,
EXPLOSION_RADIUS,
EXPLOSION_INVULNERABILITY_TIME,
// Nitro
NITRO_DURATION,
NITRO_ENGINE_FORCE,
NITRO_CONSUMPTION,
NITRO_SMALL_CONTAINER,
NITRO_BIG_CONTAINER,
NITRO_MAX_SPEED_INCREASE,
NITRO_FADE_OUT_TIME,
NITRO_MAX,
// Slipstream
SLIPSTREAM_DURATION,
SLIPSTREAM_LENGTH,
SLIPSTREAM_WIDTH,
SLIPSTREAM_COLLECT_TIME,
SLIPSTREAM_USE_TIME,
SLIPSTREAM_ADD_POWER,
SLIPSTREAM_MIN_SPEED,
SLIPSTREAM_MAX_SPEED_INCREASE,
SLIPSTREAM_FADE_OUT_TIME,
// Skid
SKID_INCREASE,
SKID_DECREASE,
SKID_MAX,
SKID_TIME_TILL_MAX,
SKID_VISUAL,
SKID_VISUAL_TIME,
SKID_REVERT_VISUAL_TIME,
SKID_MIN_SPEED,
SKID_TIME_TILL_BONUS,
SKID_BONUS_SPEED,
SKID_BONUS_TIME,
SKID_BONUS_FORCE,
SKID_PHYSICAL_JUMP_TIME,
SKID_GRAPHICAL_JUMP_TIME,
SKID_POST_SKID_ROTATE_FACTOR,
SKID_REDUCE_TURN_MIN,
SKID_REDUCE_TURN_MAX,
SKID_ENABLED,
/* <characteristics-end enum> */
// Count
CHARACTERISTIC_COUNT
};
public:
AbstractCharacteristic();
virtual ~AbstractCharacteristic() {}
/**
* The process function is the core of this characteristics system.
* Any computation of the properties should happen here and modify the
* values of the value-pointer (be sure to use the right type!) and the
* is_set parameter when the value was set by the call (and wasn't set
* before).
*
* \param type The characteristic that should be modified.
* \param value The current value and result at the same time.
* \param is_set If the current value was already set (so it can be used
* for computations).
*/
virtual void process(CharacteristicType type, Value value, bool *is_set) const;
static ValueType getType(CharacteristicType type);
static std::string getName(CharacteristicType type);
// Script-generated content generated by tools/create_kart_properties.py defs
// Please don't change the following tag. It will be automatically detected
// by the script and replace the contained content.
// To update the code, use tools/update_characteristics.py
/* <characteristics-start acdefs> */
float getSuspensionStiffness() const;
float getSuspensionRest() const;
float getSuspensionTravel() const;
bool getSuspensionExpSpringResponse() const;
float getSuspensionMaxForce() const;
float getStabilityRollInfluence() const;
float getStabilityChassisLinearDamping() const;
float getStabilityChassisAngularDamping() const;
float getStabilityDownwardImpulseFactor() const;
float getStabilityTrackConnectionAccel() const;
float getStabilitySmoothFlyingImpulse() const;
InterpolationArray getTurnRadius() const;
float getTurnTimeResetSteer() const;
InterpolationArray getTurnTimeFullSteer() const;
float getEnginePower() const;
float getEngineMaxSpeed() const;
float getEngineBrakeFactor() const;
float getEngineBrakeTimeIncrease() const;
float getEngineMaxSpeedReverseRatio() const;
std::vector<float> getGearSwitchRatio() const;
std::vector<float> getGearPowerIncrease() const;
float getMass() const;
float getWheelsDampingRelaxation() const;
float getWheelsDampingCompression() const;
float getCameraDistance() const;
float getCameraForwardUpAngle() const;
float getCameraBackwardUpAngle() const;
float getJumpAnimationTime() const;
float getLeanMax() const;
float getLeanSpeed() const;
float getAnvilDuration() const;
float getAnvilWeight() const;
float getAnvilSpeedFactor() const;
float getParachuteFriction() const;
float getParachuteDuration() const;
float getParachuteDurationOther() const;
float getParachuteLboundFraction() const;
float getParachuteUboundFraction() const;
float getParachuteMaxSpeed() const;
float getBubblegumDuration() const;
float getBubblegumSpeedFraction() const;
float getBubblegumTorque() const;
float getBubblegumFadeInTime() const;
float getBubblegumShieldDuration() const;
float getZipperDuration() const;
float getZipperForce() const;
float getZipperSpeedGain() const;
float getZipperMaxSpeedIncrease() const;
float getZipperFadeOutTime() const;
float getSwatterDuration() const;
float getSwatterDistance() const;
float getSwatterSquashDuration() const;
float getSwatterSquashSlowdown() const;
float getPlungerBandMaxLength() const;
float getPlungerBandForce() const;
float getPlungerBandDuration() const;
float getPlungerBandSpeedIncrease() const;
float getPlungerBandFadeOutTime() const;
float getPlungerInFaceTime() const;
std::vector<float> getStartupTime() const;
std::vector<float> getStartupBoost() const;
float getRescueDuration() const;
float getRescueVertOffset() const;
float getRescueHeight() const;
float getExplosionDuration() const;
float getExplosionRadius() const;
float getExplosionInvulnerabilityTime() const;
float getNitroDuration() const;
float getNitroEngineForce() const;
float getNitroConsumption() const;
float getNitroSmallContainer() const;
float getNitroBigContainer() const;
float getNitroMaxSpeedIncrease() const;
float getNitroFadeOutTime() const;
float getNitroMax() const;
float getSlipstreamDuration() const;
float getSlipstreamLength() const;
float getSlipstreamWidth() const;
float getSlipstreamCollectTime() const;
float getSlipstreamUseTime() const;
float getSlipstreamAddPower() const;
float getSlipstreamMinSpeed() const;
float getSlipstreamMaxSpeedIncrease() const;
float getSlipstreamFadeOutTime() const;
float getSkidIncrease() const;
float getSkidDecrease() const;
float getSkidMax() const;
float getSkidTimeTillMax() const;
float getSkidVisual() const;
float getSkidVisualTime() const;
float getSkidRevertVisualTime() const;
float getSkidMinSpeed() const;
std::vector<float> getSkidTimeTillBonus() const;
std::vector<float> getSkidBonusSpeed() const;
std::vector<float> getSkidBonusTime() const;
std::vector<float> getSkidBonusForce() const;
float getSkidPhysicalJumpTime() const;
float getSkidGraphicalJumpTime() const;
float getSkidPostSkidRotateFactor() const;
float getSkidReduceTurnMin() const;
float getSkidReduceTurnMax() const;
bool getSkidEnabled() const;
/* <characteristics-end acdefs> */
};
#endif

View File

@ -35,14 +35,15 @@
AbstractKart::AbstractKart(const std::string& ident,
int world_kart_id, int position,
const btTransform& init_transform,
const PlayerDifficulty *difficulty)
PerPlayerDifficulty difficulty)
: Moveable()
{
m_world_kart_id = world_kart_id;
m_kart_properties = kart_properties_manager->getKart(ident);
m_kart_properties.reset(new KartProperties());
m_kart_properties->copyForPlayer(kart_properties_manager->getKart(ident));
m_difficulty = difficulty;
m_kart_animation = NULL;
assert(m_kart_properties != NULL);
assert(m_kart_properties);
// We have to take a copy of the kart model, since otherwise
// the animations will be mixed up (i.e. different instances of

View File

@ -19,10 +19,11 @@
#ifndef HEADER_ABSTRACT_KART_HPP
#define HEADER_ABSTRACT_KART_HPP
#include <memory>
#include "items/powerup_manager.hpp"
#include "karts/moveable.hpp"
#include "karts/controller/kart_control.hpp"
#include "karts/player_difficulty.hpp"
#include "race/race_manager.hpp"
namespace irr
@ -70,10 +71,10 @@ private:
protected:
/** The kart properties. */
const KartProperties *m_kart_properties;
std::unique_ptr<KartProperties> m_kart_properties;
/** The per-player difficulty. */
const PlayerDifficulty *m_difficulty;
PerPlayerDifficulty m_difficulty;
/** This stores a copy of the kart model. It has to be a copy
* since otherwise incosistencies can happen if the same kart
@ -95,7 +96,7 @@ public:
AbstractKart(const std::string& ident,
int world_kart_id,
int position, const btTransform& init_transform,
const PlayerDifficulty *difficulty);
PerPlayerDifficulty difficulty);
virtual ~AbstractKart();
virtual core::stringw getName() const;
virtual void reset();
@ -120,20 +121,17 @@ public:
// ------------------------------------------------------------------------
/** Returns the kart properties of this kart. */
const KartProperties* getKartProperties() const
{ return m_kart_properties; }
// ------------------------------------------------------------------------
/** Sets the kart properties. */
void setKartProperties(const KartProperties *kp) { m_kart_properties=kp; }
{ return m_kart_properties.get(); }
// ========================================================================
// Access to the per-player difficulty.
// ------------------------------------------------------------------------
/** Returns the per-player difficulty of this kart. */
const PlayerDifficulty* getPlayerDifficulty() const
const PerPlayerDifficulty getPerPlayerDifficulty() const
{ return m_difficulty; }
// ------------------------------------------------------------------------
/** Sets the per-player difficulty. */
void setPlayerDifficulty(const PlayerDifficulty *pd) { m_difficulty=pd; }
void setPerPlayerDifficulty(const PerPlayerDifficulty d) { m_difficulty=d; }
// ------------------------------------------------------------------------
/** Returns a unique identifier for this kart (name of the directory the
@ -144,6 +142,10 @@ public:
* speed. */
virtual float getMaxSteerAngle () const = 0;
// ------------------------------------------------------------------------
/** Returns the (maximum) speed for a given turn radius.
* \param radius The radius for which the speed needs to be computed. */
virtual float getSpeedForTurnRadius(float radius) const = 0;
// ------------------------------------------------------------------------
/** Returns the time till full steering is reached for this kart.
* This can depend on the current steering value, which must be >= 0.
*/
@ -445,6 +447,9 @@ public:
/** Counter which is used for displaying wrong way message after a delay */
virtual float getWrongwayCounter() = 0;
virtual void setWrongwayCounter(float counter) = 0;
// ------------------------------------------------------------------------
/** Returns whether this kart wins or loses. */
virtual bool getRaceResult() const = 0;
}; // AbstractKart

View File

@ -0,0 +1,203 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/cached_characteristic.hpp"
#include "utils/interpolation_array.hpp"
CachedCharacteristic::CachedCharacteristic(const AbstractCharacteristic *origin) :
m_values(CHARACTERISTIC_COUNT),
m_origin(origin)
{
updateSource();
}
// ----------------------------------------------------------------------------
/** Deletes all allocated values. */
CachedCharacteristic::~CachedCharacteristic()
{
// Delete all not-null values
for (int i = 0; i < CHARACTERISTIC_COUNT; i++)
{
SaveValue &v = m_values[i];
if (v.content)
{
switch (getType(static_cast<CharacteristicType>(i)))
{
case TYPE_FLOAT:
delete static_cast<float*>(v.content);
break;
case TYPE_FLOAT_VECTOR:
delete static_cast<std::vector<float>*>(v.content);
break;
case TYPE_INTERPOLATION_ARRAY:
delete static_cast<InterpolationArray*>(v.content);
break;
case TYPE_BOOL:
delete static_cast<bool*>(v.content);
break;
}
v.content = nullptr;
}
}
} // ~CachedCharacteristic
// ----------------------------------------------------------------------------
/** Recompute the values of all characteristics based on the list of
* source-characteristics.
*/
void CachedCharacteristic::updateSource()
{
for (int i = 0; i < CHARACTERISTIC_COUNT; i++)
{
SaveValue &v = m_values[i];
bool is_set = false;
switch (getType(static_cast<CharacteristicType>(i)))
{
case TYPE_FLOAT:
{
float value;
float *ptr = static_cast<float*>(v.content);
m_origin->process(static_cast<CharacteristicType>(i), &value, &is_set);
if (is_set)
{
if (!ptr)
{
float *newPtr = new float();
v.content = newPtr;
ptr = newPtr;
}
*ptr = value;
}
else
{
if (ptr)
{
delete ptr;
v.content = nullptr;
}
}
break;
}
case TYPE_FLOAT_VECTOR:
{
std::vector<float> value;
std::vector<float> *ptr = static_cast<std::vector<float>*>(v.content);
m_origin->process(static_cast<CharacteristicType>(i), &value, &is_set);
if (is_set)
{
if (!ptr)
{
std::vector<float> *newPtr = new std::vector<float>();
v.content = newPtr;
ptr = newPtr;
}
*ptr = value;
}
else
{
if (ptr)
{
delete ptr;
v.content = nullptr;
}
}
break;
}
case TYPE_INTERPOLATION_ARRAY:
{
InterpolationArray value;
InterpolationArray *ptr = static_cast<InterpolationArray*>(v.content);
m_origin->process(static_cast<CharacteristicType>(i), &value, &is_set);
if (is_set)
{
if (!ptr)
{
InterpolationArray *newPtr = new InterpolationArray();
v.content = newPtr;
ptr = newPtr;
}
*ptr = value;
}
else
{
if (ptr)
{
delete ptr;
v.content = nullptr;
}
}
break;
}
case TYPE_BOOL:
{
bool value;
bool *ptr = static_cast<bool*>(v.content);
m_origin->process(static_cast<CharacteristicType>(i), &value, &is_set);
if (is_set)
{
if (!ptr)
{
bool *newPtr = new bool();
v.content = newPtr;
ptr = newPtr;
}
*ptr = value;
}
else
{
if (ptr)
{
delete ptr;
v.content = nullptr;
}
}
break;
}
} // switch (type)
} // foreach characteristic
} // updateSource
// ----------------------------------------------------------------------------
/** Returns the stored value. */
void CachedCharacteristic::process(CharacteristicType type, Value value,
bool *is_set) const
{
void *v = m_values[type].content;
if (v)
{
switch (getType(type))
{
case TYPE_FLOAT:
*value.f = *static_cast<float*>(v);
break;
case TYPE_FLOAT_VECTOR:
*value.fv = *static_cast<std::vector<float>*>(v);
break;
case TYPE_INTERPOLATION_ARRAY:
*value.ia = *static_cast<InterpolationArray*>(v);
break;
case TYPE_BOOL:
*value.b = *static_cast<bool*>(v);
break;
}
*is_set = true;
}
} // process

View File

@ -0,0 +1,54 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_CACHED_CHARACTERISTICS_HPP
#define HEADER_CACHED_CHARACTERISTICS_HPP
#include "karts/abstract_characteristic.hpp"
class CachedCharacteristic : public AbstractCharacteristic
{
private:
/** Used to store a value. */
struct SaveValue
{
void *content;
SaveValue() : content(nullptr) {}
SaveValue(void *content) : content(content) {}
};
/** All values for a characteristic. A nullptr means it is not set. */
std::vector<SaveValue> m_values;
/** The characteristics that hold the original values. */
const AbstractCharacteristic *m_origin;
public:
CachedCharacteristic(const AbstractCharacteristic *origin);
CachedCharacteristic(const CachedCharacteristic &characteristics) = delete;
virtual ~CachedCharacteristic();
/** Fetches all cached values from the original source. */
void updateSource();
virtual void process(CharacteristicType type, Value value, bool *is_set) const;
};
#endif

View File

@ -69,7 +69,7 @@ CannonAnimation::~CannonAnimation()
m_kart->getHeading() ));
m_kart->getBody()->setCenterOfMassTransform(pos);
Vec3 v(0, 0, m_kart->getKartProperties()->getMaxSpeed());
Vec3 v(0, 0, m_kart->getKartProperties()->getEngineMaxSpeed());
m_kart->setVelocity(pos.getBasis()*v);
} // ~CannonAnimation

View File

@ -0,0 +1,35 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/combined_characteristic.hpp"
void CombinedCharacteristic::addCharacteristic(
const AbstractCharacteristic *characteristic)
{
m_childs.push_back(characteristic);
} // addCharacteristic
// ----------------------------------------------------------------------------
/** Combines all contained source characteristics. */
void CombinedCharacteristic::process(CharacteristicType type, Value value,
bool *is_set) const
{
for (const AbstractCharacteristic *characteristic : m_childs)
characteristic->process(type, value, is_set);
} // process

View File

@ -0,0 +1,36 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_COMBINDED_CHARACTERISTICS_HPP
#define HEADER_COMBINDED_CHARACTERISTICS_HPP
#include "karts/abstract_characteristic.hpp"
class CombinedCharacteristic : public AbstractCharacteristic
{
private:
std::vector<const AbstractCharacteristic*> m_childs;
public:
void addCharacteristic(const AbstractCharacteristic *characteristic);
virtual void process(CharacteristicType type, Value value, bool *is_set) const;
};
#endif

View File

@ -22,9 +22,8 @@
#include "config/user_config.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/kart_properties.hpp"
#include "karts/skidding_properties.hpp"
#include "karts/controller/ai_properties.hpp"
#include "modes/linear_world.hpp"
#include "modes/world.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
@ -32,59 +31,6 @@
bool AIBaseController::m_ai_debug = false;
/**
This is the base class for all AIs. At this stage there are two similar
AIs: one is the SkiddingAI, which is the AI used in lap based races
(including follow-the-leader mode), the other one is the end controller,
I.e. the controller that takes over from a player (or AI) when the race is
finished.
This base class defines some basic operations:
- It takes care on which part of the QuadGraph the AI currently is.
- It determines which path the AI should take (in case of shortcuts
or forks in the road).
At race start and every time a new lap is started, the AI will compute the
path the kart is taking this lap (computePath). At this stage the decision
which road in case of shortcut to take is purely random. It stores the
information in two arrays:
m_successor_index[i] stores which successor to take from node i.
The successor is a number between 0 and number_of_successors - 1.
m_next_node_index[i] stores the actual index of the graph node that
follows after node i.
Depending on operation one of the other data is more useful, so this
class stores both information to avoid looking it up over and over.
Once this is done (still in computePath), the array m_all_look_aheads is
computed. This array stores for each quad a list of the next (atm) 10 quads.
This is used when the AI is selecting where to drive next, and it will just
pass the list of next quads to findRoadSector.
Note that the quad graph information is stored for every quad in the quad
graph, even if the quad is not on the path chosen. This is necessary since
it can happen that a kart ends up on a path not choses (e.g. perhaps it was
pushed on that part, or couldn't get a sharp corner).
In update(), which gets called one per frame per AI, this object will
determine the quad the kart is currently on (which is then used to determine
where the kart will be driving to). This uses the m_all_look_aheads to
speed up this process (since the kart is likely to be either on the same
quad as it was before, or the next quad in the m_all_look_aheads list).
It will also check if the kart is stuck:
this is done by maintaining a list of times when the kart hits the track. If
(atm) more than 3 collisions happen in 1.5 seconds, the kart is considered
stuck and will trigger a rescue (due to the pushback from the track it will
take some time if a kart is really stuck before it will hit the track again).
This base class also contains some convenience functions which are useful
in all AIs, e.g.:
- steerToPoint: determine the steering angle to use depending on the
current location and the point the kart is driving to.
- normalizeAngle: To normalise the steering angle to be in [-PI,PI].
- setSteering: Converts the steering angle into a steering fraction
in [-1,1].
*/
AIBaseController::AIBaseController(AbstractKart *kart,
StateManager::ActivePlayer *player)
: Controller(kart, player)
@ -92,38 +38,24 @@ AIBaseController::AIBaseController(AbstractKart *kart,
m_kart = kart;
m_kart_length = m_kart->getKartLength();
m_kart_width = m_kart->getKartWidth();
m_ai_properties =
m_kart->getKartProperties()->getAIPropertiesForDifficulty();
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
{
m_world = dynamic_cast<LinearWorld*>(World::getWorld());
m_track = m_world->getTrack();
computePath();
}
else
{
// Those variables are not defined in a battle mode (m_world is
// a linear world, since it assumes the existance of drivelines)
m_world = NULL;
m_track = NULL;
m_next_node_index.clear();
m_all_look_aheads.clear();
m_successor_index.clear();
} // if battle mode
// Don't call our own setControllerName, since this will add a
// billboard showing 'aibasecontroller' to the kar.
Controller::setControllerName("AIBaseController");
} // AIBaseController
m_ai_properties = m_kart->getKartProperties()->getAIPropertiesForDifficulty();
}
//-----------------------------------------------------------------------------
void AIBaseController::reset()
{
m_stuck_trigger_rescue = false;
m_stuck = false;
m_collision_times.clear();
} // reset
//-----------------------------------------------------------------------------
void AIBaseController::update(float dt)
{
m_stuck = false;
}
//-----------------------------------------------------------------------------
/** In debug mode when the user specified --ai-debug on the command line set
* the name of the controller as on-screen text, so that the different AI
@ -139,210 +71,6 @@ void AIBaseController::setControllerName(const std::string &name)
Controller::setControllerName(name);
} // setControllerName
//-----------------------------------------------------------------------------
/** Triggers a recomputation of the path to use, so that the AI does not
* always use the same way.
*/
void AIBaseController::newLap(int lap)
{
if(lap>0)
{
computePath();
}
} // newLap
//-----------------------------------------------------------------------------
/** Computes a path for the AI to follow. This function is called at race
* start and every time a new lap is started. Recomputing the path every
* time will mean that the kart will not always take the same path, but
* (potentially) vary from lap to lap. At this stage the decision is done
* randomly. The AI could be improved by collecting more information about
* each branch of a track, and selecting the 'appropriate' one (e.g. if the
* AI is far ahead, chose a longer/slower path).
*/
void AIBaseController::computePath()
{
m_next_node_index.resize(QuadGraph::get()->getNumNodes());
m_successor_index.resize(QuadGraph::get()->getNumNodes());
std::vector<unsigned int> next;
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
{
next.clear();
// Get all successors the AI is allowed to take.
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/true);
// In case of short cuts hidden for the AI it can be that a node
// might not have a successor (since the first and last edge of
// a hidden shortcut is ignored). Since in the case that the AI
// ends up on a short cut (e.g. by accident) and doesn't have an
// allowed way to drive, it should still be able to drive, so add
// the non-AI successors of that node in this case.
if(next.size()==0)
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/false);
// For now pick one part on random, which is not adjusted during the
// race. Long term statistics might be gathered to determine the
// best way, potentially depending on race position etc.
int r = rand();
int indx = (int)( r / ((float)(RAND_MAX)+1.0f) * next.size() );
// In case of rounding errors0
if(indx>=(int)next.size()) indx--;
m_successor_index[i] = indx;
assert(indx <(int)next.size() && indx>=0);
m_next_node_index[i] = next[indx];
}
const unsigned int look_ahead=10;
// Now compute for each node in the graph the list of the next 'look_ahead'
// graph nodes. This is the list of node that is tested in checkCrashes.
// If the look_ahead is too big, the AI can skip loops (see
// QuadGraph::findRoadSector for details), if it's too short the AI won't
// find too good a driveline. Note that in general this list should
// be computed recursively, but since the AI for now is using only
// (randomly picked) path this is fine
m_all_look_aheads.resize(QuadGraph::get()->getNumNodes());
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
{
std::vector<int> l;
int current = i;
for(unsigned int j=0; j<look_ahead; j++)
{
assert(current < (int)m_next_node_index.size());
l.push_back(m_next_node_index[current]);
current = m_next_node_index[current];
} // for j<look_ahead
m_all_look_aheads[i] = l;
}
} // computePath
//-----------------------------------------------------------------------------
/** Updates the ai base controller each time step. Note that any calls to
* isStuck() must be done before update is called, since update will reset
* the isStuck flag!
* \param dt Time step size.
*/
void AIBaseController::update(float dt)
{
m_stuck_trigger_rescue = false;
if(QuadGraph::get())
{
// Update the current node:
int old_node = m_track_node;
if(m_track_node!=QuadGraph::UNKNOWN_SECTOR)
{
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node,
&m_all_look_aheads[m_track_node]);
}
// If we can't find a proper place on the track, to a broader search
// on off-track locations.
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
// IF the AI is off track (or on a branch of the track it did not
// select to be on), keep the old position.
if(m_track_node==QuadGraph::UNKNOWN_SECTOR ||
m_next_node_index[m_track_node]==-1)
m_track_node = old_node;
}
} // update
//-----------------------------------------------------------------------------
/** This is called when the kart crashed with the terrain. This subroutine
* tries to detect if the AI is stuck by determining if a certain number
* of collisions happened in a certain amount of time, and if so rescues
* the kart.
* \paran m Pointer to the material that was hit (NULL if no specific
* material was used for the part of the track that was hit).
*/
void AIBaseController::crashed(const Material *m)
{
// Defines how many collision in what time will trigger a rescue.
// Note that typically it takes ~0.5 seconds for the AI to hit
// the track again if it is stuck (i.e. time for the push back plus
// time for the AI to accelerate and hit the terrain again).
const unsigned int NUM_COLLISION = 3;
const float COLLISION_TIME = 1.5f;
float time = World::getWorld()->getTime();
if(m_collision_times.size()==0)
{
m_collision_times.push_back(time);
return;
}
// Filter out multiple collisions report caused by single collision
// (bullet can report a collision more than once per frame, and
// resolving it can take a few frames as well, causing more reported
// collisions to happen). The time of 0.2 seconds was experimentally
// found, typically it takes 0.5 seconds for a kart to be pushed back
// from the terrain and accelerate to hit the same terrain again.
if(time - m_collision_times.back() < 0.2f)
return;
// Remove all outdated entries, i.e. entries that are older than the
// collision time plus 1 second. Older entries must be deleted,
// otherwise a collision that happened (say) 10 seconds ago could
// contribute to a stuck condition.
while(m_collision_times.size()>0 &&
time - m_collision_times[0] > 1.0f+COLLISION_TIME)
m_collision_times.erase(m_collision_times.begin());
m_collision_times.push_back(time);
// Now detect if there are enough collision records in the
// specified time interval.
if(time - m_collision_times.front() > COLLISION_TIME
&& m_collision_times.size()>=NUM_COLLISION)
{
// We can't call m_kart->forceRescue here, since crased() is
// called during physics processing, and forceRescue() removes the
// chassis from the physics world, which would then cause
// inconsistencies and potentially a crash during the physics
// processing. So only set a flag, which is tested during update.
m_stuck_trigger_rescue = true;
}
} // crashed(Material)
//-----------------------------------------------------------------------------
/** Returns the next sector of the given sector index. This is used
* for branches in the quad graph to select which way the AI kart should
* go. This is a very simple implementation that always returns the first
* successor, but it can be overridden to allow a better selection.
* \param index Index of the graph node for which the successor is searched.
* \return Returns the successor of this graph node.
*/
unsigned int AIBaseController::getNextSector(unsigned int index)
{
std::vector<unsigned int> successors;
QuadGraph::get()->getSuccessors(index, successors);
return successors[0];
} // getNextSector
//-----------------------------------------------------------------------------
/** This function steers towards a given angle. It also takes a plunger
** attached to this kart into account by modifying the actual steer angle
* somewhat to simulate driving without seeing.
*/
float AIBaseController::steerToAngle(const unsigned int sector,
const float add_angle)
{
float angle = QuadGraph::get()->getAngleToNext(sector,
getNextSector(sector));
//Desired angle minus current angle equals how many angles to turn
float steer_angle = angle - m_kart->getHeading();
if(m_kart->getBlockedByPlungerTime()>0)
steer_angle += add_angle*0.2f;
else
steer_angle += add_angle;
steer_angle = normalizeAngle( steer_angle );
return steer_angle;
} // steerToAngle
//-----------------------------------------------------------------------------
/** Computes the steering angle to reach a certain point. The function will
* request steering by setting the steering angle to maximum steer angle
@ -493,22 +221,7 @@ void AIBaseController::setSteering(float angle, float dt)
* AIBaseController.
* \return True if the kart should skid.
*/
bool AIBaseController::doSkid(float steer_fraction)
{
// Disable skidding when a plunger is in the face
if(m_kart->getBlockedByPlungerTime()>0) return false;
// FIXME: Disable skidding for now if the new skidding
// code is activated, since the AI can not handle this
// properly.
if(m_kart->getKartProperties()->getSkiddingProperties()
->getSkidVisualTime()>0)
return false;
// Otherwise return if we need a sharp turn (which is
// for the old skidding implementation).
return fabsf(steer_fraction)>=m_ai_properties->m_skidding_threshold;
} // doSkid
// ------------------------------------------------------------------------
/** Certain AI levels will not receive a slipstream bonus in order to
* be not as hard.
@ -517,3 +230,78 @@ bool AIBaseController::disableSlipstreamBonus() const
{
return m_ai_properties->disableSlipstreamUsage();
} // disableSlipstreamBonus
bool AIBaseController::doSkid(float steer_fraction)
{
// Disable skidding when a plunger is in the face
if(m_kart->getBlockedByPlungerTime()>0) return false;
// FIXME: Disable skidding for now if the new skidding
// code is activated, since the AI can not handle this
// properly.
if(m_kart->getKartProperties()->getSkidVisualTime() > 0)
return false;
// Otherwise return if we need a sharp turn (which is
// for the old skidding implementation).
return fabsf(steer_fraction)>=m_ai_properties->m_skidding_threshold;
} // doSkid
//-----------------------------------------------------------------------------
/** This is called when the kart crashed with the terrain. This subroutine
* tries to detect if the AI is stuck by determining if a certain number
* of collisions happened in a certain amount of time, and if so rescues
* the kart.
* \paran m Pointer to the material that was hit (NULL if no specific
* material was used for the part of the track that was hit).
*/
void AIBaseController::crashed(const Material *m)
{
// Defines how many collision in what time will trigger a rescue.
// Note that typically it takes ~0.5 seconds for the AI to hit
// the track again if it is stuck (i.e. time for the push back plus
// time for the AI to accelerate and hit the terrain again).
const unsigned int NUM_COLLISION = 3;
const float COLLISION_TIME = 1.5f;
float time = World::getWorld()->getTime();
if(m_collision_times.size()==0)
{
m_collision_times.push_back(time);
return;
}
// Filter out multiple collisions report caused by single collision
// (bullet can report a collision more than once per frame, and
// resolving it can take a few frames as well, causing more reported
// collisions to happen). The time of 0.2 seconds was experimentally
// found, typically it takes 0.5 seconds for a kart to be pushed back
// from the terrain and accelerate to hit the same terrain again.
if(time - m_collision_times.back() < 0.2f)
return;
// Remove all outdated entries, i.e. entries that are older than the
// collision time plus 1 second. Older entries must be deleted,
// otherwise a collision that happened (say) 10 seconds ago could
// contribute to a stuck condition.
while(m_collision_times.size()>0 &&
time - m_collision_times[0] > 1.0f+COLLISION_TIME)
m_collision_times.erase(m_collision_times.begin());
m_collision_times.push_back(time);
// Now detect if there are enough collision records in the
// specified time interval.
if(time - m_collision_times.front() > COLLISION_TIME
&& m_collision_times.size()>=NUM_COLLISION)
{
// We can't call m_kart->forceRescue here, since crased() is
// called during physics processing, and forceRescue() removes the
// chassis from the physics world, which would then cause
// inconsistencies and potentially a crash during the physics
// processing. So only set a flag, which is tested during update.
m_stuck = true;
}
} // crashed(Material)

View File

@ -23,8 +23,6 @@
#include "states_screens/state_manager.hpp"
class AIProperties;
class LinearWorld;
class QuadGraph;
class Track;
class Vec3;
@ -42,7 +40,7 @@ private:
/** A flag that is set during the physics processing to indicate that
* this kart is stuck and needs to be rescued. */
bool m_stuck_trigger_rescue;
bool m_stuck;
protected:
/** Length of the kart, storing it here saves many function calls. */
@ -54,67 +52,30 @@ protected:
/** Keep a pointer to the track to reduce calls */
Track *m_track;
/** Keep a pointer to world. */
LinearWorld *m_world;
/** A pointer to the AI properties for this kart. */
const AIProperties *m_ai_properties;
/** The current node the kart is on. This can be different from the value
* in LinearWorld, since it takes the chosen path of the AI into account
* (e.g. the closest point in LinearWorld might be on a branch not
* chosen by the AI). */
int m_track_node;
/** Which of the successors of a node was selected by the AI. */
std::vector<int> m_successor_index;
/** For each node in the graph this list contains the chosen next node.
* For normal lap track without branches we always have
* m_next_node_index[i] = (i+1) % size;
* but if a branch is possible, the AI will select one option here.
* If the node is not used, m_next_node_index will be -1. */
std::vector<int> m_next_node_index;
/** For each graph node this list contains a list of the next X
* graph nodes. */
std::vector<std::vector<int> > m_all_look_aheads;
static bool m_ai_debug;
virtual void update (float delta) ;
virtual unsigned int getNextSector(unsigned int index);
virtual void newLap (int lap);
virtual void setControllerName(const std::string &name);
virtual void setSteering (float angle, float dt);
float steerToAngle (const unsigned int sector, const float angle);
float steerToPoint (const Vec3 &point);
void setControllerName(const std::string &name);
float steerToPoint(const Vec3 &point);
float normalizeAngle(float angle);
void computePath();
virtual bool doSkid(float steer_fraction);
// ------------------------------------------------------------------------
/** Nothing special to do when the race is finished. */
virtual void raceFinished() {};
// ------------------------------------------------------------------------
/** This can be called to detect if the kart is stuck (i.e. repeatedly
* hitting part of the track). */
bool isStuck() const { return m_stuck_trigger_rescue; }
bool isStuck() const { return m_stuck; }
static bool m_ai_debug;
public:
AIBaseController(AbstractKart *kart,
StateManager::ActivePlayer *player=NULL);
virtual ~AIBaseController() {};
virtual void reset();
static void enableDebug() {m_ai_debug = true; }
virtual void crashed(const AbstractKart *k) {};
virtual void crashed(const Material *m);
virtual void handleZipper(bool play_sound) {};
virtual void finishedRace(float time) {};
virtual void collectedItem(const Item &item, int add_info=-1,
float previous_energy=0) {};
virtual void setPosition(int p) {};
virtual bool isNetworkController() const { return false; }
virtual bool isPlayerController() const { return false; }
virtual void action(PlayerAction action, int value) {};
virtual void skidBonusTriggered() {};
virtual bool disableSlipstreamBonus() const;
virtual void crashed(const Material *m);
}; // AIBaseController
#endif

View File

@ -0,0 +1,262 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2009 Eduardo Hernandez Munoz
// Copyright (C) 2009-2015 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/controller/ai_base_lap_controller.hpp"
#include <assert.h>
#include "karts/abstract_kart.hpp"
#include "karts/kart_properties.hpp"
#include "karts/controller/ai_properties.hpp"
#include "modes/linear_world.hpp"
#include "tracks/track.hpp"
#include "utils/constants.hpp"
/**
This is the base class for all AIs. At this stage there are two similar
AIs: one is the SkiddingAI, which is the AI used in lap based races
(including follow-the-leader mode), the other one is the end controller,
I.e. the controller that takes over from a player (or AI) when the race is
finished.
This base class defines some basic operations:
- It takes care on which part of the QuadGraph the AI currently is.
- It determines which path the AI should take (in case of shortcuts
or forks in the road).
At race start and every time a new lap is started, the AI will compute the
path the kart is taking this lap (computePath). At this stage the decision
which road in case of shortcut to take is purely random. It stores the
information in two arrays:
m_successor_index[i] stores which successor to take from node i.
The successor is a number between 0 and number_of_successors - 1.
m_next_node_index[i] stores the actual index of the graph node that
follows after node i.
Depending on operation one of the other data is more useful, so this
class stores both information to avoid looking it up over and over.
Once this is done (still in computePath), the array m_all_look_aheads is
computed. This array stores for each quad a list of the next (atm) 10 quads.
This is used when the AI is selecting where to drive next, and it will just
pass the list of next quads to findRoadSector.
Note that the quad graph information is stored for every quad in the quad
graph, even if the quad is not on the path chosen. This is necessary since
it can happen that a kart ends up on a path not choses (e.g. perhaps it was
pushed on that part, or couldn't get a sharp corner).
In update(), which gets called one per frame per AI, this object will
determine the quad the kart is currently on (which is then used to determine
where the kart will be driving to). This uses the m_all_look_aheads to
speed up this process (since the kart is likely to be either on the same
quad as it was before, or the next quad in the m_all_look_aheads list).
It will also check if the kart is stuck:
this is done by maintaining a list of times when the kart hits the track. If
(atm) more than 3 collisions happen in 1.5 seconds, the kart is considered
stuck and will trigger a rescue (due to the pushback from the track it will
take some time if a kart is really stuck before it will hit the track again).
This base class also contains some convenience functions which are useful
in all AIs, e.g.:
- steerToPoint: determine the steering angle to use depending on the
current location and the point the kart is driving to.
- normalizeAngle: To normalise the steering angle to be in [-PI,PI].
- setSteering: Converts the steering angle into a steering fraction
in [-1,1].
*/
AIBaseLapController::AIBaseLapController(AbstractKart *kart,
StateManager::ActivePlayer *player)
: AIBaseController(kart, player)
{
if (race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
{
m_world = dynamic_cast<LinearWorld*>(World::getWorld());
m_track = m_world->getTrack();
computePath();
}
else
{
// Those variables are not defined in a battle mode (m_world is
// a linear world, since it assumes the existance of drivelines)
m_world = NULL;
m_track = NULL;
m_next_node_index.clear();
m_all_look_aheads.clear();
m_successor_index.clear();
} // if battle mode
// Don't call our own setControllerName, since this will add a
// billboard showing 'AIBaseLapController' to the kar.
Controller::setControllerName("AIBaseLapController");
} // AIBaseLapController
//-----------------------------------------------------------------------------
void AIBaseLapController::reset()
{
AIBaseController::reset();
} // reset
//-----------------------------------------------------------------------------
/** Triggers a recomputation of the path to use, so that the AI does not
* always use the same way.
*/
void AIBaseLapController::newLap(int lap)
{
if(lap>0)
{
computePath();
}
} // newLap
//-----------------------------------------------------------------------------
/** Computes a path for the AI to follow. This function is called at race
* start and every time a new lap is started. Recomputing the path every
* time will mean that the kart will not always take the same path, but
* (potentially) vary from lap to lap. At this stage the decision is done
* randomly. The AI could be improved by collecting more information about
* each branch of a track, and selecting the 'appropriate' one (e.g. if the
* AI is far ahead, chose a longer/slower path).
*/
void AIBaseLapController::computePath()
{
m_next_node_index.resize(QuadGraph::get()->getNumNodes());
m_successor_index.resize(QuadGraph::get()->getNumNodes());
std::vector<unsigned int> next;
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
{
next.clear();
// Get all successors the AI is allowed to take.
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/true);
// In case of short cuts hidden for the AI it can be that a node
// might not have a successor (since the first and last edge of
// a hidden shortcut is ignored). Since in the case that the AI
// ends up on a short cut (e.g. by accident) and doesn't have an
// allowed way to drive, it should still be able to drive, so add
// the non-AI successors of that node in this case.
if(next.size()==0)
QuadGraph::get()->getSuccessors(i, next, /*for_ai*/false);
// For now pick one part on random, which is not adjusted during the
// race. Long term statistics might be gathered to determine the
// best way, potentially depending on race position etc.
int r = rand();
int indx = (int)( r / ((float)(RAND_MAX)+1.0f) * next.size() );
// In case of rounding errors0
if(indx>=(int)next.size()) indx--;
m_successor_index[i] = indx;
assert(indx <(int)next.size() && indx>=0);
m_next_node_index[i] = next[indx];
}
const unsigned int look_ahead=10;
// Now compute for each node in the graph the list of the next 'look_ahead'
// graph nodes. This is the list of node that is tested in checkCrashes.
// If the look_ahead is too big, the AI can skip loops (see
// QuadGraph::findRoadSector for details), if it's too short the AI won't
// find too good a driveline. Note that in general this list should
// be computed recursively, but since the AI for now is using only
// (randomly picked) path this is fine
m_all_look_aheads.resize(QuadGraph::get()->getNumNodes());
for(unsigned int i=0; i<QuadGraph::get()->getNumNodes(); i++)
{
std::vector<int> l;
int current = i;
for(unsigned int j=0; j<look_ahead; j++)
{
assert(current < (int)m_next_node_index.size());
l.push_back(m_next_node_index[current]);
current = m_next_node_index[current];
} // for j<look_ahead
m_all_look_aheads[i] = l;
}
} // computePath
//-----------------------------------------------------------------------------
/** Updates the ai base controller each time step. Note that any calls to
* isStuck() must be done before update is called, since update will call
* AIBaseController::update() which will reset the isStuck flag!
* \param dt Time step size.
*/
void AIBaseLapController::update(float dt)
{
AIBaseController::update(dt);
if(QuadGraph::get())
{
// Update the current node:
int old_node = m_track_node;
if(m_track_node!=QuadGraph::UNKNOWN_SECTOR)
{
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node,
&m_all_look_aheads[m_track_node]);
}
// If we can't find a proper place on the track, to a broader search
// on off-track locations.
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
{
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
// IF the AI is off track (or on a branch of the track it did not
// select to be on), keep the old position.
if(m_track_node==QuadGraph::UNKNOWN_SECTOR ||
m_next_node_index[m_track_node]==-1)
m_track_node = old_node;
}
} // update
//-----------------------------------------------------------------------------
/** Returns the next sector of the given sector index. This is used
* for branches in the quad graph to select which way the AI kart should
* go. This is a very simple implementation that always returns the first
* successor, but it can be overridden to allow a better selection.
* \param index Index of the graph node for which the successor is searched.
* \return Returns the successor of this graph node.
*/
unsigned int AIBaseLapController::getNextSector(unsigned int index)
{
std::vector<unsigned int> successors;
QuadGraph::get()->getSuccessors(index, successors);
return successors[0];
} // getNextSector
//-----------------------------------------------------------------------------
/** This function steers towards a given angle. It also takes a plunger
** attached to this kart into account by modifying the actual steer angle
* somewhat to simulate driving without seeing.
*/
float AIBaseLapController::steerToAngle(const unsigned int sector,
const float add_angle)
{
float angle = QuadGraph::get()->getAngleToNext(sector,
getNextSector(sector));
//Desired angle minus current angle equals how many angles to turn
float steer_angle = angle - m_kart->getHeading();
if(m_kart->getBlockedByPlungerTime()>0)
steer_angle += add_angle*0.2f;
else
steer_angle += add_angle;
steer_angle = normalizeAngle( steer_angle );
return steer_angle;
} // steerToAngle

View File

@ -0,0 +1,87 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010-2015 Joerg Henrichs
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_AI_BASE_LAP_CONTROLLER_HPP
#define HEADER_AI_BASE_LAP_CONTROLLER_HPP
#include "karts/controller/ai_base_controller.hpp"
#include "states_screens/state_manager.hpp"
class AIProperties;
class LinearWorld;
class QuadGraph;
class Track;
class Vec3;
/** A base class for all AI karts. This class basically provides some
* common low level functions.
* \ingroup controller
*/
class AIBaseLapController : public AIBaseController
{
protected:
/** The current node the kart is on. This can be different from the value
* in LinearWorld, since it takes the chosen path of the AI into account
* (e.g. the closest point in LinearWorld might be on a branch not
* chosen by the AI). */
int m_track_node;
/** Keep a pointer to world. */
LinearWorld *m_world;
/** Which of the successors of a node was selected by the AI. */
std::vector<int> m_successor_index;
/** For each node in the graph this list contains the chosen next node.
* For normal lap track without branches we always have
* m_next_node_index[i] = (i+1) % size;
* but if a branch is possible, the AI will select one option here.
* If the node is not used, m_next_node_index will be -1. */
std::vector<int> m_next_node_index;
/** For each graph node this list contains a list of the next X
* graph nodes. */
std::vector<std::vector<int> > m_all_look_aheads;
virtual void update (float delta) ;
virtual unsigned int getNextSector(unsigned int index);
virtual void newLap (int lap);
//virtual void setControllerName(const std::string &name);
float steerToAngle (const unsigned int sector, const float angle);
void computePath();
// ------------------------------------------------------------------------
/** Nothing special to do when the race is finished. */
virtual void raceFinished() {};
public:
AIBaseLapController(AbstractKart *kart,
StateManager::ActivePlayer *player=NULL);
virtual ~AIBaseLapController() {};
virtual void reset();
virtual void crashed(const AbstractKart *k) {};
virtual void handleZipper(bool play_sound) {};
virtual void finishedRace(float time) {};
virtual void collectedItem(const Item &item, int add_info=-1,
float previous_energy=0) {};
virtual void setPosition(int p) {};
virtual bool isNetworkController() const { return false; }
virtual bool isPlayerController() const { return false; }
virtual void action(PlayerAction action, int value) {};
virtual void skidBonusTriggered() {};
}; // AIBaseLapController
#endif

View File

@ -43,7 +43,9 @@ public:
protected:
// Give them access to the members
friend class AIBaseController;
friend class AIBaseLapController;
friend class SkiddingAI;
friend class BattleAI;
/** Used to check that all values are defined in the xml file. */
static float UNDEFINED;

View File

@ -0,0 +1,931 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) 2008-2015 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/controller/battle_ai.hpp"
#include "items/attachment.hpp"
#include "items/item_manager.hpp"
#include "items/powerup.hpp"
#include "items/projectile_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/controller/kart_control.hpp"
#include "karts/controller/player_controller.hpp"
#include "karts/controller/ai_properties.hpp"
#include "karts/kart_properties.hpp"
#include "karts/max_speed.hpp"
#include "karts/rescue_animation.hpp"
#include "karts/skidding.hpp"
#include "modes/three_strikes_battle.hpp"
#include "tracks/battle_graph.hpp"
#include "utils/log.hpp"
#ifdef AI_DEBUG
#include "irrlicht.h"
#include <iostream>
using namespace irr;
using namespace std;
#endif
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
#define isnan _isnan
#else
#include <math.h>
#endif
BattleAI::BattleAI(AbstractKart *kart,
StateManager::ActivePlayer *player)
: AIBaseController(kart, player)
{
reset();
#ifdef AI_DEBUG
video::SColor col_debug(128, 128, 0, 0);
m_debug_sphere = irr_driver->addSphere(1.0f, col_debug);
m_debug_sphere->setVisible(true);
#endif
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES)
{
m_world = dynamic_cast<ThreeStrikesBattle*>(World::getWorld());
m_track = m_world->getTrack();
}
else
{
// Those variables are not defined in a battle mode (m_world is
// a linear world, since it assumes the existance of drivelines)
m_world = NULL;
m_track = NULL;
}
// Don't call our own setControllerName, since this will add a
// billboard showing 'AIBaseController' to the kart.
Controller::setControllerName("BattleAI");
} // BattleAI
//-----------------------------------------------------------------------------
BattleAI::~BattleAI()
{
#ifdef AI_DEBUG
irr_driver->removeNode(m_debug_sphere);
#endif
} // ~BattleAI
//-----------------------------------------------------------------------------
/** Resets the AI when a race is restarted.
*/
void BattleAI::reset()
{
m_target_node = BattleGraph::UNKNOWN_POLY;
m_adjusting_side = false;
m_closest_kart = NULL;
m_closest_kart_node = BattleGraph::UNKNOWN_POLY;
m_closest_kart_point = Vec3(0, 0, 0);
m_closest_kart_pos_data = {0};
m_cur_kart_pos_data = {0};
m_is_steering_overridden = false;
m_is_stuck = false;
m_is_uturn = false;
m_target_point = Vec3(0, 0, 0);
m_time_since_last_shot = 0.0f;
m_time_since_driving = 0.0f;
m_time_since_reversing = 0.0f;
m_time_since_steering_overridden = 0.0f;
m_time_since_uturn = 0.0f;
m_on_node.clear();
m_path_corners.clear();
m_portals.clear();
m_cur_difficulty = race_manager->getDifficulty();
AIBaseController::reset();
} // reset
//-----------------------------------------------------------------------------
/** This is the main entry point for the AI.
* It is called once per frame for each AI and determines the behaviour of
* the AI, e.g. steering, accelerating/braking, firing.
*/
void BattleAI::update(float dt)
{
// This is used to enable firing an item backwards.
m_controls->m_look_back = false;
m_controls->m_nitro = false;
// Don't do anything if there is currently a kart animations shown.
if (m_kart->getKartAnimation())
return;
if (m_world->isStartPhase())
{
AIBaseController::update(dt);
return;
}
checkIfStuck(dt);
if (m_is_stuck && !m_is_uturn)
{
setSteering(0.0f, dt);
if (fabsf(m_kart->getSpeed()) >
(m_kart->getKartProperties()->getEngineMaxSpeed() / 5)
&& m_kart->getSpeed() < 0)
m_controls->m_accel = -0.06f;
else
m_controls->m_accel = -4.0f;
m_time_since_reversing += dt;
if (m_time_since_reversing >= 1.0f)
{
m_is_stuck = false;
m_time_since_reversing = 0.0f;
}
AIBaseController::update(dt);
return;
}
findClosestKart(true);
findTarget();
handleItems(dt);
handleBanana();
if (m_kart->getSpeed() > 15.0f && m_cur_kart_pos_data.angle < 0.2f)
{
// Only use nitro when target is straight
m_controls->m_nitro = true;
}
if (m_is_uturn)
{
handleUTurn(dt);
}
else
{
handleAcceleration(dt);
handleSteering(dt);
handleBraking();
}
AIBaseController::update(dt);
} // update
//-----------------------------------------------------------------------------
void BattleAI::checkIfStuck(const float dt)
{
if (m_is_stuck) return;
if (m_kart->getKartAnimation() || m_world->isStartPhase())
{
m_on_node.clear();
m_time_since_driving = 0.0f;
}
m_on_node.insert(m_world->getKartNode(m_kart->getWorldKartId()));
m_time_since_driving += dt;
if ((m_time_since_driving >=
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 2.0f : 1.5f)
&& m_on_node.size() < 2 && !m_is_uturn &&
fabsf(m_kart->getSpeed()) < 3.0f) || isStuck() == true)
{
// Check whether a kart stay on the same node for a period of time
// Or crashed 3 times
m_on_node.clear();
m_time_since_driving = 0.0f;
AIBaseController::reset();
m_is_stuck = true;
}
else if (m_time_since_driving >=
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 2.0f : 1.5f))
{
m_on_node.clear(); // Reset for any correct movement
m_time_since_driving = 0.0f;
}
} // checkIfStuck
//-----------------------------------------------------------------------------
void BattleAI::checkPosition(const Vec3 &point, posData *pos_data)
{
// Convert to local coordinates from the point of view of current kart
btQuaternion q(btVector3(0, 1, 0), -m_kart->getHeading());
Vec3 p = point - m_kart->getXYZ();
Vec3 local_coordinates = quatRotate(q, p);
// on_side: tell whether it's left or right hand side
if (local_coordinates.getX() < 0)
pos_data->on_side = true;
else
pos_data->on_side = false;
// behind: tell whether it's behind or not
if (local_coordinates.getZ() < 0)
pos_data->behind = true;
else
pos_data->behind = false;
pos_data->angle = atan2(fabsf(local_coordinates.getX()),
fabsf(local_coordinates.getZ()));
pos_data->distance = p.length_2d();
} // checkPosition
//-----------------------------------------------------------------------------
void BattleAI::findClosestKart(bool difficulty)
{
float distance = 99999.9f;
const unsigned int n = m_world->getNumKarts();
int closest_kart_num = 0;
for (unsigned int i = 0; i < n; i++)
{
const AbstractKart* kart = m_world->getKart(i);
if (kart->isEliminated()) continue;
if (kart->getWorldKartId() == m_kart->getWorldKartId())
continue; // Skip the same kart
// Test whether takes current difficulty into account for closest kart
// Notice: it don't affect aiming, this function will be called once
// more in handleItems, which ignore difficulty.
if (m_cur_difficulty == RaceManager::DIFFICULTY_EASY && difficulty)
{
// Skip human players for novice mode unless only human players left
const AbstractKart* temp = m_world->getKart(i);
if (temp->getController()->isPlayerController() &&
(m_world->getCurrentNumKarts() -
m_world->getCurrentNumPlayers()) > 1)
continue;
}
else if (m_cur_difficulty == RaceManager::DIFFICULTY_BEST && difficulty)
{
// Skip AI players for supertux mode
const AbstractKart* temp = m_world->getKart(i);
if (!(temp->getController()->isPlayerController()))
continue;
}
Vec3 d = kart->getXYZ() - m_kart->getXYZ();
if (d.length_2d() <= distance)
{
distance = d.length_2d();
closest_kart_num = i;
}
}
const AbstractKart* closest_kart = m_world->getKart(closest_kart_num);
m_closest_kart_node = m_world->getKartNode(closest_kart_num);
m_closest_kart_point = closest_kart->getXYZ();
if (!difficulty)
{
m_closest_kart = m_world->getKart(closest_kart_num);
checkPosition(m_closest_kart_point, &m_closest_kart_pos_data);
}
} // findClosestKart
//-----------------------------------------------------------------------------
void BattleAI::findTarget()
{
// Find a suitable target to drive to, either powerup or kart
if (m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING &&
m_kart->getAttachment()->getType() != Attachment::ATTACH_SWATTER)
handleItemCollection(&m_target_point , &m_target_node);
else
{
m_target_point = m_closest_kart_point;
m_target_node = m_closest_kart_node;
}
} // findTarget
//-----------------------------------------------------------------------------
/** Handles acceleration.
* \param dt Time step size.
*/
void BattleAI::handleAcceleration(const float dt)
{
if (m_controls->m_brake)
{
m_controls->m_accel = 0.0f;
return;
}
const float handicap =
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 0.7f : 1.0f);
m_controls->m_accel = stk_config->m_ai_acceleration * handicap;
} // handleAcceleration
//-----------------------------------------------------------------------------
void BattleAI::handleUTurn(const float dt)
{
const float turn_side = (m_adjusting_side ? 1.0f : -1.0f);
if (fabsf(m_kart->getSpeed()) >
(m_kart->getKartProperties()->getEngineMaxSpeed() / 5)
&& m_kart->getSpeed() < 0) // Try to emulate reverse like human players
m_controls->m_accel = -0.06f;
else
m_controls->m_accel = -5.0f;
if (m_time_since_uturn >=
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 2.0f : 1.5f))
setSteering(-(turn_side), dt); // Preventing keep going around circle
else
setSteering(turn_side, dt);
m_time_since_uturn += dt;
checkPosition(m_target_point, &m_cur_kart_pos_data);
if (!m_cur_kart_pos_data.behind || m_time_since_uturn >
(m_cur_difficulty == RaceManager::DIFFICULTY_EASY ? 3.5f : 3.0f))
{
m_is_uturn = false;
m_time_since_uturn = 0.0f;
}
else
m_is_uturn = true;
} // handleUTurn
//-----------------------------------------------------------------------------
/** This function sets the steering.
* \param dt Time step size.
*/
void BattleAI::handleSteering(const float dt)
{
const int current_node = m_world->getKartNode(m_kart->getWorldKartId());
if (current_node == BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY) return;
if (m_is_steering_overridden)
{
// Steering is overridden to avoid eating banana
const float turn_side = (m_adjusting_side ? 1.0f : -1.0f);
m_time_since_steering_overridden += dt;
if (m_time_since_steering_overridden > 0.35f)
setSteering(-(turn_side), dt);
else
setSteering(turn_side, dt);
if (m_time_since_steering_overridden > 0.7f)
{
m_is_steering_overridden = false;
m_time_since_steering_overridden = 0.0f;
}
return;
}
if (m_target_node == current_node)
{
// Very close to the item, steer directly
checkPosition(m_target_point, &m_cur_kart_pos_data);
#ifdef AI_DEBUG
m_debug_sphere->setPosition(m_target_point.toIrrVector());
#endif
if (m_cur_kart_pos_data.behind)
{
m_adjusting_side = m_cur_kart_pos_data.on_side;
m_is_uturn = true;
}
else
{
float target_angle = steerToPoint(m_target_point);
setSteering(target_angle, dt);
}
return;
}
else if (m_target_node != current_node)
{
findPortals(current_node, m_target_node);
stringPull(m_kart->getXYZ(), m_target_point);
if (m_path_corners.size() > 0)
m_target_point = m_path_corners[0];
checkPosition(m_target_point, &m_cur_kart_pos_data);
#ifdef AI_DEBUG
m_debug_sphere->setPosition(m_target_point.toIrrVector());
#endif
if (m_cur_kart_pos_data.behind)
{
m_adjusting_side = m_cur_kart_pos_data.on_side;
m_is_uturn = true;
}
else
{
float target_angle = steerToPoint(m_target_point);
setSteering(target_angle, dt);
}
return;
}
else
{
// Do nothing (go straight) if no targets found
setSteering(0.0f, dt);
return;
}
} // handleSteering
//-----------------------------------------------------------------------------
void BattleAI::handleBanana()
{
if (m_is_steering_overridden || m_is_uturn) return;
const std::vector< std::pair<const Item*, int> >& item_list =
BattleGraph::get()->getItemList();
const unsigned int items_count = item_list.size();
for (unsigned int i = 0; i < items_count; ++i)
{
const Item* item = item_list[i].first;
if (item->getType() == Item::ITEM_BANANA && !item->wasCollected())
{
posData banana_pos = {0};
checkPosition(item->getXYZ(), &banana_pos);
if (banana_pos.angle < 0.2f && banana_pos.distance < 7.5f &&
!banana_pos.behind)
{
// Check whether it's straight ahead towards a banana
// If so, try to do hard turn to avoid
m_adjusting_side = banana_pos.on_side;
m_is_steering_overridden = true;
}
}
}
} // handleBanana
//-----------------------------------------------------------------------------
/** This function finds the polyon edges(portals) that the AI will cross before
* reaching its destination. We start from the current polygon and call
* BattleGraph::getNextShortestPathPoly() to find the next polygon on the shortest
* path to the destination. Then find the common edge between the current
* poly and the next poly, store it and step through the channel.
*
* 1----2----3 In this case, the portals are:
* |strt| | (2,5) (4,5) (10,7) (10,9) (11,12)
* 6----5----4
* | |
* 7----10----11----14
* | | | end |
* 8----9-----12----13
*
* \param start The start node(polygon) of the channel.
* \param end The end node(polygon) of the channel.
*/
void BattleAI::findPortals(int start, int end)
{
int this_node = start;
// We can't use NULL because NULL==0 which is a valid node, so we initialize
// with a value that is always invalid.
int next_node = -999;
m_portals.clear();
while (next_node != end && this_node != -1 && next_node != -1 && this_node != end)
{
next_node = BattleGraph::get()->getNextShortestPathPoly(this_node, end);
if (next_node == BattleGraph::UNKNOWN_POLY || next_node == -999) return;
std::vector<int> this_node_verts =
NavMesh::get()->getNavPoly(this_node).getVerticesIndex();
std::vector<int> next_node_verts=
NavMesh::get()->getNavPoly(next_node).getVerticesIndex();
// this_node_verts and next_node_verts hold vertices of polygons in CCW order
// We reverse next_node_verts so it becomes easy to compare edges in the next step
std::reverse(next_node_verts.begin(),next_node_verts.end());
Vec3 portalLeft, portalRight;
//bool flag = 0;
for (unsigned int n_i = 0; n_i < next_node_verts.size(); n_i++)
{
for (unsigned int t_i = 0; t_i < this_node_verts.size(); t_i++)
{
if ((next_node_verts[n_i] == this_node_verts[t_i]) &&
(next_node_verts[(n_i+1)%next_node_verts.size()] ==
this_node_verts[(t_i+1)%this_node_verts.size()]))
{
portalLeft = NavMesh::get()->
getVertex(this_node_verts[(t_i+1)%this_node_verts.size()]);
portalRight = NavMesh::get()->getVertex(this_node_verts[t_i]);
}
}
}
m_portals.push_back(std::make_pair(portalLeft, portalRight));
// for debugging:
//m_debug_sphere->setPosition((portalLeft).toIrrVector());
this_node = next_node;
}
} // findPortals
//-----------------------------------------------------------------------------
/** This function implements the funnel algorithm for finding shortest paths
* through a polygon channel. This means that we should move from corner to
* corner to move on the most straight and shortest path to the destination.
* This can be visualized as pulling a string from the end point to the start.
* The string will bend at the corners, and this algorithm will find those
* corners using portals from findPortals(). The AI will aim at the first
* corner and the rest can be used for estimating the curve (braking).
*
* 1----2----3 In this case, the corners are:
* |strt| | <5,10,end>
* 6----5----4
* | |
* 7----10----11----14
* | | | end |
* 8----9-----12----13
*
* \param start_pos The start position (usually the AI's current position).
* \param end_pos The end position (m_target_point).
*/
void BattleAI::stringPull(const Vec3& start_pos, const Vec3& end_pos)
{
Vec3 funnel_apex = start_pos;
Vec3 funnel_left = m_portals[0].first;
Vec3 funnel_right = m_portals[0].second;
unsigned int apex_index=0, fun_left_index=0, fun_right_index=0;
m_portals.push_back(std::make_pair(end_pos, end_pos));
m_path_corners.clear();
const float eps=0.0001f;
for (unsigned int i = 0; i < m_portals.size(); i++)
{
Vec3 portal_left = m_portals[i].first;
Vec3 portal_right = m_portals[i].second;
//Compute for left edge
if ((funnel_left == funnel_apex) ||
portal_left.sideOfLine2D(funnel_apex, funnel_left) <= -eps)
{
funnel_left = 0.98f*portal_left + 0.02f*portal_right;
//funnel_left = portal_left;
fun_left_index = i;
if (portal_left.sideOfLine2D(funnel_apex, funnel_right) < -eps)
{
funnel_apex = funnel_right;
apex_index = fun_right_index;
m_path_corners.push_back(funnel_apex);
funnel_left = funnel_apex;
funnel_right = funnel_apex;
i = apex_index;
continue;
}
}
//Compute for right edge
if ((funnel_right == funnel_apex) ||
portal_right.sideOfLine2D(funnel_apex, funnel_right) >= eps)
{
funnel_right = 0.98f*portal_right + 0.02f*portal_left;
//funnel_right = portal_right;
fun_right_index = i;
if (portal_right.sideOfLine2D(funnel_apex, funnel_left) > eps)
{
funnel_apex = funnel_left;
apex_index = fun_left_index;
m_path_corners.push_back(funnel_apex);
funnel_left = funnel_apex;
funnel_right = funnel_apex;
i = apex_index;
continue;
}
}
}
//Push end_pos to m_path_corners so if no corners, we aim at target
m_path_corners.push_back(end_pos);
} // stringPull
//-----------------------------------------------------------------------------
/** This function handles braking. It calls determineTurnRadius() to find out
* the curve radius. Depending on the turn radius, it finds out the maximum
* speed. If the current speed is greater than the max speed and a set minimum
* speed, brakes are applied.
*/
void BattleAI::handleBraking()
{
m_controls->m_brake = false;
if (m_world->getKartNode(m_kart->getWorldKartId())
== BattleGraph::UNKNOWN_POLY ||
m_target_node == BattleGraph::UNKNOWN_POLY ||
m_is_steering_overridden) return;
// A kart will not brake when the speed is already slower than this
// value. This prevents a kart from going too slow (or even backwards)
// in tight curves.
const float MIN_SPEED = 5.0f;
std::vector<Vec3> points;
points.push_back(m_kart->getXYZ());
points.push_back(m_path_corners[0]);
points.push_back((m_path_corners.size()>=2) ? m_path_corners[1] : m_path_corners[0]);
float current_curve_radius = BattleAI::determineTurnRadius(points);
Vec3 d1 = m_kart->getXYZ() - m_target_point;
Vec3 d2 = m_kart->getXYZ() - m_path_corners[0];
if (d1.length2_2d() < d2.length2_2d())
current_curve_radius = d1.length_2d();
float max_turn_speed = m_kart->getSpeedForTurnRadius(current_curve_radius);
if (m_kart->getSpeed() > max_turn_speed &&
m_kart->getSpeed() > MIN_SPEED)
{
m_controls->m_brake = true;
}
} // handleBraking
//-----------------------------------------------------------------------------
/** The turn radius is determined by fitting a parabola to 3 points: current
* location of AI, first corner and the second corner. Once the constants are
* computed, a formula is used to find the radius of curvature at the kart's
* current location.
* NOTE: This method does not apply enough braking, should think of something
* else.
*/
float BattleAI::determineTurnRadius( std::vector<Vec3>& points )
{
// Declaring variables
float a, b;
irr::core::CMatrix4<float> A;
irr::core::CMatrix4<float> X;
irr::core::CMatrix4<float> B;
//Populating matrices
for (unsigned int i = 0; i < 3; i++)
{
A(i, 0) = points[i].x()*points[i].x();
A(i, 1) = points[i].x();
A(i, 2) = 1.0f;
A(i, 3) = 0.0f;
}
A(3, 0) = A(3, 1) = A(3, 2) = 0.0f;
A(3, 3) = 1.0f;
for (unsigned int i = 0; i < 3; i++)
{
B(i, 0) = points[i].z();
B(i, 1) = 0.0f;
B(i, 2) = 0.0f;
B(i, 3) = 0.0f;
}
B(3, 0) = B(3, 1) = B(3, 2) = B(3, 3) = 0.0f;
//Computing inverse : X = inv(A)*B
irr::core::CMatrix4<float> invA;
if (!A.getInverse(invA))
return -1;
X = invA*B;
a = X(0, 0);
b = X(0, 1);
//c = X(0, 2);
float x = points.front().x();
//float z = a*pow(x, 2) + b*x + c;
float dx_by_dz = 2*a*x + b;
float d2x_by_dz = 2*a;
float radius = pow(abs(1 + pow(dx_by_dz, 2)), 1.5f)/ abs(d2x_by_dz);
return radius;
} // determineTurnRadius
//-----------------------------------------------------------------------------
void BattleAI::handleItems(const float dt)
{
m_controls->m_fire = false;
if (m_kart->getKartAnimation() ||
m_kart->getPowerup()->getType() == PowerupManager::POWERUP_NOTHING)
return;
// Find a closest kart again, this time we ignore difficulty
findClosestKart(false);
if (!m_closest_kart) return;
m_time_since_last_shot += dt;
float min_bubble_time = 2.0f;
const bool difficulty = m_cur_difficulty == RaceManager::DIFFICULTY_EASY ||
m_cur_difficulty == RaceManager::DIFFICULTY_MEDIUM;
const bool fire_behind = m_closest_kart_pos_data.behind && !difficulty;
const bool perfect_aim = m_closest_kart_pos_data.angle < 0.2f;
switch(m_kart->getPowerup()->getType())
{
case PowerupManager::POWERUP_BUBBLEGUM:
{
Attachment::AttachmentType type = m_kart->getAttachment()->getType();
// Don't use shield when we have a swatter.
if (type == Attachment::ATTACH_SWATTER ||
type == Attachment::ATTACH_NOLOKS_SWATTER)
break;
// Check if a flyable (cake, ...) is close. If so, use bubblegum
// as shield
if (!m_kart->isShielded() &&
projectile_manager->projectileIsClose(m_kart,
m_ai_properties->m_shield_incoming_radius))
{
m_controls->m_fire = true;
m_controls->m_look_back = false;
break;
}
// Avoid dropping all bubble gums one after another
if (m_time_since_last_shot < 3.0f) break;
// Use bubblegum if the next kart behind is 'close' but not too close,
// or can't find a close kart for too long time
if ((m_closest_kart_pos_data.distance < 15.0f &&
m_closest_kart_pos_data.distance > 3.0f) ||
m_time_since_last_shot > 15.0f)
{
m_controls->m_fire = true;
m_controls->m_look_back = true;
break;
}
break; // POWERUP_BUBBLEGUM
}
case PowerupManager::POWERUP_CAKE:
{
// if the kart has a shield, do not break it by using a cake.
if (m_kart->getShieldTime() > min_bubble_time)
break;
// Leave some time between shots
if (m_time_since_last_shot < 1.0f) break;
if (m_closest_kart_pos_data.distance < 25.0f &&
!m_closest_kart->isInvulnerable())
{
m_controls->m_fire = true;
m_controls->m_look_back = fire_behind;
break;
}
break;
} // POWERUP_CAKE
case PowerupManager::POWERUP_BOWLING:
{
// if the kart has a shield, do not break it by using a bowling ball.
if (m_kart->getShieldTime() > min_bubble_time)
break;
// Leave some time between shots
if (m_time_since_last_shot < 1.0f) break;
if (m_closest_kart_pos_data.distance < 6.0f &&
(difficulty || perfect_aim))
{
m_controls->m_fire = true;
m_controls->m_look_back = fire_behind;
break;
}
break;
} // POWERUP_BOWLING
case PowerupManager::POWERUP_SWATTER:
{
// Squared distance for which the swatter works
float d2 = m_kart->getKartProperties()->getSwatterDistance();
// if the kart has a shield, do not break it by using a swatter.
if (m_kart->getShieldTime() > min_bubble_time)
break;
if (!m_closest_kart->isSquashed() &&
m_closest_kart_pos_data.distance < d2 &&
m_closest_kart->getSpeed() < m_kart->getSpeed())
{
m_controls->m_fire = true;
m_controls->m_look_back = false;
break;
}
break;
}
// Below powerups won't appear in battle mode, so skip them
case PowerupManager::POWERUP_ZIPPER:
break; // POWERUP_ZIPPER
case PowerupManager::POWERUP_PLUNGER:
break; // POWERUP_PLUNGER
case PowerupManager::POWERUP_SWITCH: // Don't handle switch
m_controls->m_fire = true; // (use it no matter what) for now
break; // POWERUP_SWITCH
case PowerupManager::POWERUP_PARACHUTE:
break; // POWERUP_PARACHUTE
case PowerupManager::POWERUP_ANVIL:
break; // POWERUP_ANVIL
case PowerupManager::POWERUP_RUBBERBALL:
break;
default:
Log::error("BattleAI",
"Invalid or unhandled powerup '%d' in default AI.",
m_kart->getPowerup()->getType());
assert(false);
}
if (m_controls->m_fire)
m_time_since_last_shot = 0.0f;
} // handleItems
//-----------------------------------------------------------------------------
void BattleAI::handleItemCollection(Vec3* aim_point, int* target_node)
{
float distance = 99999.9f;
const std::vector< std::pair<const Item*, int> >& item_list =
BattleGraph::get()->getItemList();
const unsigned int items_count = item_list.size();
if (item_list.empty())
{
// Notice: this should not happen, as it makes no sense
// for an arean without items, if so how can attack happen?
Log::fatal ("BattleAI",
"AI can't find any items in the arena, "
"maybe there is something wrong with the navmesh, "
"make sure it lies closely to the ground.");
return;
}
unsigned int closest_item_num = 0;
for (unsigned int i = 0; i < items_count; ++i)
{
const Item* item = item_list[i].first;
if (item->wasCollected()) continue;
if ((item->getType() == Item::ITEM_NITRO_BIG ||
item->getType() == Item::ITEM_NITRO_SMALL) &&
(m_kart->getEnergy() >
m_kart->getKartProperties()->getNitroSmallContainer()))
continue; // Ignore nitro when already has some
Vec3 d = item->getXYZ() - m_kart->getXYZ();
if (d.length_2d() <= distance &&
(item->getType() == Item::ITEM_BONUS_BOX ||
item->getType() == Item::ITEM_NITRO_BIG ||
item->getType() == Item::ITEM_NITRO_SMALL))
{
closest_item_num = i;
distance = d.length_2d();
}
}
const Item *item_selected = item_list[closest_item_num].first;
if (item_selected->getType() == Item::ITEM_BONUS_BOX ||
item_selected->getType() == Item::ITEM_NITRO_BIG ||
item_selected->getType() == Item::ITEM_NITRO_SMALL)
{
*aim_point = item_selected->getXYZ();
*target_node = item_list[closest_item_num].second;
}
else
{
// Handle when all targets are swapped, which make AIs follow karts
*aim_point = m_closest_kart_point;
*target_node = m_closest_kart_node;
}
} // handleItemCollection

View File

@ -0,0 +1,160 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2004-2005 Steve Baker <sjbaker1@airmail.net>
// Copyright (C) 2006-2007 Eduardo Hernandez Munoz
// Copyright (C) 2010-2015 Joerg Henrichs
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_BATTLE_AI_HPP
#define HEADER_BATTLE_AI_HPP
#undef AI_DEBUG
#ifdef AI_DEBUG
#include "graphics/irr_driver.hpp"
#endif
#include "karts/controller/ai_base_controller.hpp"
#include "race/race_manager.hpp"
#include "utils/random_generator.hpp"
class ThreeStrikesBattle;
class Vec3;
class Item;
namespace irr
{
namespace scene { class ISceneNode; }
namespace video { class ITexture; }
}
class BattleAI : public AIBaseController
{
private:
/** Holds the position info of targets. */
struct posData {bool behind; bool on_side; float angle; float distance;};
/** Used by handleBanana and UTurn, it tells whether to do left or right
* turning when steering is overridden. */
bool m_adjusting_side;
int m_closest_kart_node;
Vec3 m_closest_kart_point;
/** Pointer to the closest kart around this kart. */
AbstractKart *m_closest_kart;
posData m_closest_kart_pos_data;
posData m_cur_kart_pos_data;
/** Holds the current difficulty. */
RaceManager::Difficulty m_cur_difficulty;
/** Indicates that the steering of kart is overridden, and
* m_time_since_steering_overridden is counting down. */
bool m_is_steering_overridden;
/** Indicates that the kart is currently stuck, and m_time_since_reversing is
* counting down. */
bool m_is_stuck;
/** Indicates that the kart need a uturn to reach a node behind, and
* m_time_since_uturn is counting down. */
bool m_is_uturn;
/** Holds the unique node ai has driven through, useful to tell if AI is
* stuck by determine the size of this set. */
std::set <int> m_on_node;
/** Holds the corner points computed using the funnel algorithm that the AI
* will eventaully move through. See stringPull(). */
std::vector<Vec3> m_path_corners;
/** Holds the set of portals that the kart will cross when moving through
* polygon channel. See findPortals(). */
std::vector<std::pair<Vec3,Vec3> > m_portals;
/** The node(poly) at which the target point lies in. */
int m_target_node;
/** The target point. */
Vec3 m_target_point;
/** Time an item has been collected and not used. */
float m_time_since_last_shot;
/** This is a timer that counts down when the kart is reversing to get unstuck. */
float m_time_since_reversing;
/** This is a timer that counts down when the kart is starting to drive. */
float m_time_since_driving;
/** This is a timer that counts down when the steering of kart is overridden. */
float m_time_since_steering_overridden;
/** This is a timer that counts down when the kart is doing u-turn. */
float m_time_since_uturn;
void checkIfStuck(const float dt);
void checkPosition(const Vec3 &, posData*);
float determineTurnRadius(std::vector<Vec3>& points);
void findClosestKart(bool difficulty);
void findPortals(int start, int end);
void findTarget();
void handleAcceleration(const float dt);
void handleBanana();
void handleBraking();
void handleItems(const float dt);
void handleItemCollection(Vec3*, int*);
void handleSteering(const float dt);
void handleSwatter();
void handleUTurn(const float dt);
void stringPull(const Vec3&, const Vec3&);
protected:
/** Keep a pointer to world. */
ThreeStrikesBattle *m_world;
#ifdef AI_DEBUG
/** For debugging purpose: a sphere indicating where the AI
* is targeting at. */
irr::scene::ISceneNode *m_debug_sphere;
#endif
public:
BattleAI(AbstractKart *kart,
StateManager::ActivePlayer *player=NULL);
~BattleAI();
virtual void update (float delta);
virtual void reset ();
virtual void crashed(const AbstractKart *k) {};
virtual void handleZipper(bool play_sound) {};
virtual void finishedRace(float time) {};
virtual void collectedItem(const Item &item, int add_info=-1,
float previous_energy=0) {};
virtual void setPosition(int p) {};
virtual bool isNetworkController() const { return false; }
virtual bool isPlayerController() const { return false; }
virtual void action(PlayerAction action, int value) {};
virtual void skidBonusTriggered() {};
virtual bool disableSlipstreamBonus() const {return 0; }
virtual void newLap(int lap) {};
};
#endif

View File

@ -57,6 +57,7 @@ protected:
/** The name of the controller, mainly used for debugging purposes. */
std::string m_controller_name;
public:
Controller (AbstractKart *kart,
StateManager::ActivePlayer *player=NULL);

View File

@ -52,13 +52,13 @@
EndController::EndController(AbstractKart *kart, StateManager::ActivePlayer *player,
Controller *prev_controller)
: AIBaseController(kart, player)
: AIBaseLapController(kart, player)
{
m_previous_controller = prev_controller;
if(race_manager->getMinorMode()!=RaceManager::MINOR_MODE_3_STRIKES &&
race_manager->getMinorMode()!=RaceManager::MINOR_MODE_SOCCER)
{
// Overwrite the random selected default path from AIBaseController
// Overwrite the random selected default path from AIBaseLapController
// with a path that always picks the first branch (i.e. it follows
// the main driveline).
std::vector<unsigned int> next;
@ -124,7 +124,7 @@ EndController::~EndController()
//-----------------------------------------------------------------------------
void EndController::reset()
{
AIBaseController::reset();
AIBaseLapController::reset();
m_crash_time = 0.0f;
m_time_since_stuck = 0.0f;
@ -179,7 +179,7 @@ void EndController::update(float dt)
m_controls->m_brake = false;
m_controls->m_accel = 1.0f;
AIBaseController::update(dt);
AIBaseLapController::update(dt);
// In case of battle mode: don't do anything
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_3_STRIKES ||

View File

@ -21,7 +21,7 @@
#ifndef HEADER_END_CONTROLLER_HPP
#define HEADER_END_CONTROLLER_HPP
#include "karts/controller/ai_base_controller.hpp"
#include "karts/controller/ai_base_lap_controller.hpp"
class Camera;
class LinearWorld;
@ -40,7 +40,7 @@ namespace irr
/**
* \ingroup controller
*/
class EndController : public AIBaseController
class EndController : public AIBaseLapController
{
private:
/** Stores the type of the previous controller. This is necessary so that

View File

@ -197,7 +197,7 @@ void NetworkPlayerController::steer(float dt, int steer_val)
// change speed is used.
const float STEER_CHANGE = ( (steer_val<=0 && m_controls->m_steer<0) ||
(steer_val>=0 && m_controls->m_steer>0) )
? dt/m_kart->getKartProperties()->getTimeResetSteer()
? dt/m_kart->getKartProperties()->getTurnTimeResetSteer()
: dt/m_kart->getTimeFullSteer(fabsf(m_controls->m_steer));
if (steer_val < 0)
{

View File

@ -37,6 +37,7 @@
#include "network/network_world.hpp"
#include "race/history.hpp"
#include "states_screens/race_gui_base.hpp"
#include "tracks/battle_graph.hpp"
#include "utils/constants.hpp"
#include "utils/log.hpp"
#include "utils/translation.hpp"
@ -254,7 +255,7 @@ void PlayerController::steer(float dt, int steer_val)
// change speed is used.
const float STEER_CHANGE = ( (steer_val<=0 && m_controls->m_steer<0) ||
(steer_val>=0 && m_controls->m_steer>0) )
? dt/m_kart->getKartProperties()->getTimeResetSteer()
? dt/m_kart->getKartProperties()->getTurnTimeResetSteer()
: dt/m_kart->getTimeFullSteer(fabsf(m_controls->m_steer));
if (steer_val < 0)
{

View File

@ -38,7 +38,6 @@
#include "karts/max_speed.hpp"
#include "karts/rescue_animation.hpp"
#include "karts/skidding.hpp"
#include "karts/skidding_properties.hpp"
#include "modes/linear_world.hpp"
#include "modes/profile_world.hpp"
#include "race/race_manager.hpp"
@ -60,7 +59,7 @@
#include <iostream>
SkiddingAI::SkiddingAI(AbstractKart *kart)
: AIBaseController(kart)
: AIBaseLapController(kart)
{
reset();
// Determine if this AI has superpowers, which happens e.g.
@ -179,7 +178,7 @@ void SkiddingAI::reset()
m_skid_probability_state = SKID_PROBAB_NOT_YET;
m_last_item_random = NULL;
AIBaseController::reset();
AIBaseLapController::reset();
m_track_node = QuadGraph::UNKNOWN_SECTOR;
QuadGraph::get()->findRoadSector(m_kart->getXYZ(), &m_track_node);
if(m_track_node==QuadGraph::UNKNOWN_SECTOR)
@ -191,7 +190,7 @@ void SkiddingAI::reset()
m_track_node = QuadGraph::get()->findOutOfRoadSector(m_kart->getXYZ());
}
AIBaseController::reset();
AIBaseLapController::reset();
} // reset
//-----------------------------------------------------------------------------
@ -296,14 +295,14 @@ void SkiddingAI::update(float dt)
if(isStuck() && !m_kart->getKartAnimation())
{
new RescueAnimation(m_kart);
AIBaseController::update(dt);
AIBaseLapController::update(dt);
return;
}
if( m_world->isStartPhase() )
{
handleRaceStart();
AIBaseController::update(dt);
AIBaseLapController::update(dt);
return;
}
@ -378,7 +377,7 @@ void SkiddingAI::update(float dt)
}
/*And obviously general kart stuff*/
AIBaseController::update(dt);
AIBaseLapController::update(dt);
} // update
//-----------------------------------------------------------------------------
@ -430,8 +429,7 @@ void SkiddingAI::handleBraking()
m_current_track_direction==GraphNode::DIR_RIGHT )
{
float max_turn_speed =
m_kart->getKartProperties()
->getSpeedForTurnRadius(m_current_curve_radius);
m_kart->getSpeedForTurnRadius(m_current_curve_radius);
if(m_kart->getSpeed() > 1.5f*max_turn_speed &&
m_kart->getSpeed()>MIN_SPEED &&
@ -1032,6 +1030,8 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
std::vector<const Item *> *items_to_avoid,
std::vector<const Item *> *items_to_collect)
{
const KartProperties *kp = m_kart->getKartProperties();
// Ignore items that are currently disabled
if(item->getDisableTime()>0) return;
@ -1052,18 +1052,16 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
// Positive items: try to collect
case Item::ITEM_NITRO_BIG:
// Only collect nitro, if it can actually be stored.
if(m_kart->getEnergy() +
m_kart->getKartProperties()->getNitroBigContainer()
> m_kart->getKartProperties()->getNitroMax())
if (m_kart->getEnergy() + kp->getNitroBigContainer()
> kp->getNitroMax())
return;
// fall through: if we have enough space to store a big
// container, we can also store a small container, and
// finally fall through to the bonus box code.
case Item::ITEM_NITRO_SMALL: avoid = false;
// Only collect nitro, if it can actually be stored.
if (m_kart->getEnergy() +
m_kart->getKartProperties()->getNitroSmallContainer()
> m_kart->getKartProperties()->getNitroMax())
if (m_kart->getEnergy() + kp->getNitroSmallContainer()
> kp->getNitroMax())
return;
case Item::ITEM_BONUS_BOX:
break;
@ -1093,7 +1091,7 @@ void SkiddingAI::evaluateItems(const Item *item, float kart_aim_angle,
// be if the kart would need to turn sharper, therefore stops
// skidding, and will get the bonus speed.
bool high_speed = (m_kart->getCurrentMaxSpeed() >
m_kart->getKartProperties()->getMaxSpeed() ) ||
kp->getEngineMaxSpeed() ) ||
m_kart->getSkidding()->getSkidBonusReady();
float max_angle = high_speed
? m_ai_properties->m_max_item_angle_high_speed
@ -1355,13 +1353,13 @@ void SkiddingAI::handleItems(const float dt)
case PowerupManager::POWERUP_PARACHUTE:
// Wait one second more than a previous parachute
if(m_time_since_last_shot > stk_config->m_parachute_time_other+1.0f)
if(m_time_since_last_shot > m_kart->getKartProperties()->getParachuteDurationOther() + 1.0f)
m_controls->m_fire = true;
break; // POWERUP_PARACHUTE
case PowerupManager::POWERUP_ANVIL:
// Wait one second more than a previous anvil
if(m_time_since_last_shot < stk_config->m_anvil_time+1.0f) break;
if(m_time_since_last_shot < m_kart->getKartProperties()->getAnvilDuration() + 1.0f) break;
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
@ -1378,7 +1376,7 @@ void SkiddingAI::handleItems(const float dt)
case PowerupManager::POWERUP_SWATTER:
{
// Squared distance for which the swatter works
float d2 = m_kart->getKartProperties()->getSwatterDistance2();
float d2 = m_kart->getKartProperties()->getSwatterDistance();
// if the kart has a shield, do not break it by using a swatter.
if(m_kart->getShieldTime() > min_bubble_time)
break;
@ -2194,7 +2192,7 @@ void SkiddingAI::handleCurve()
/** Determines if the kart should skid. The base implementation enables
* skidding
* \param steer_fraction The steering fraction as computed by the
* AIBaseController.
* AIBaseLapController.
* \return True if the kart should skid.
*/
bool SkiddingAI::doSkid(float steer_fraction)
@ -2260,7 +2258,6 @@ bool SkiddingAI::doSkid(float steer_fraction)
// the actual path is adjusted during the turn. So apply an
// experimentally found factor in to get better estimates.
duration *= 1.5f;
const Skidding *skidding = m_kart->getSkidding();
// If the remaining estimated time for skidding is too short, stop
// it. This code will mostly trigger the bonus at the end of a skid.
@ -2289,8 +2286,8 @@ bool SkiddingAI::doSkid(float steer_fraction)
return false;
}
// If there is a skidding bonus, try to get it.
else if(skidding->getNumberOfBonusTimes()>0 &&
skidding->getTimeTillBonus(0) < duration)
else if (m_kart->getKartProperties()->getSkidBonusSpeed().size() > 0 &&
m_kart->getKartProperties()->getSkidTimeTillBonus()[0] < duration)
{
#ifdef DEBUG
if(!m_controls->m_skid && m_ai_debug)

View File

@ -43,7 +43,7 @@
#endif
#include "karts/controller/ai_base_controller.hpp"
#include "karts/controller/ai_base_lap_controller.hpp"
#include "race/race_manager.hpp"
#include "tracks/graph_node.hpp"
#include "utils/random_generator.hpp"
@ -111,7 +111,7 @@ the AI does the following steps:
\ingroup controller
*/
class SkiddingAI : public AIBaseController
class SkiddingAI : public AIBaseLapController
{
private:

View File

@ -78,8 +78,7 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
m_xyz = m_kart->getXYZ();
m_orig_y = m_xyz.getY();
m_kart->playCustomSFX(SFXManager::CUSTOM_EXPLODE);
m_timer = m_kart->getKartProperties()->getExplosionTime() *
m_kart->getPlayerDifficulty()->getExplosionTime();
m_timer = m_kart->getKartProperties()->getExplosionDuration();
// Non-direct hits will be only affected half as much.
if(!direct_hit) m_timer*=0.5f;
@ -106,8 +105,7 @@ ExplosionAnimation::ExplosionAnimation(AbstractKart *kart,
m_add_rotation.setRoll( (rand()%(2*max_rotation+1)-max_rotation)*f );
// Set invulnerable time, and graphical effects
float t = m_kart->getKartProperties()->getExplosionInvulnerabilityTime() *
m_kart->getPlayerDifficulty()->getExplosionInvulnerabilityTime();
float t = m_kart->getKartProperties()->getExplosionInvulnerabilityTime();
m_kart->setInvulnerableTime(t);
m_kart->showStarEffect(t);

View File

@ -24,8 +24,7 @@
GhostKart::GhostKart(const std::string& ident)
: Kart(ident, /*world kart id*/99999,
/*position*/-1, btTransform(), stk_config->getPlayerDifficulty(
PLAYER_DIFFICULTY_NORMAL))
/*position*/-1, btTransform(), PLAYER_DIFFICULTY_NORMAL)
{
m_current_transform = 0;
m_next_event = 0;

View File

@ -38,6 +38,8 @@
#include "graphics/stk_text_billboard.hpp"
#include "graphics/stars.hpp"
#include "guiengine/scalable_font.hpp"
#include "karts/abstract_characteristic.hpp"
#include "karts/cached_characteristic.hpp"
#include "karts/explosion_animation.hpp"
#include "karts/kart_gfx.hpp"
#include "karts/rescue_animation.hpp"
@ -95,7 +97,7 @@
*/
Kart::Kart (const std::string& ident, unsigned int world_kart_id,
int position, const btTransform& init_transform,
const PlayerDifficulty *difficulty)
PerPlayerDifficulty difficulty)
: AbstractKart(ident, world_kart_id, position, init_transform,
difficulty)
@ -111,6 +113,7 @@ Kart::Kart (const std::string& ident, unsigned int world_kart_id,
m_race_position = position;
m_collected_energy = 0;
m_finished_race = false;
m_race_result = false;
m_finish_time = 0.0f;
m_bubblegum_time = 0.0f;
m_bubblegum_torque = 0.0f;
@ -223,8 +226,7 @@ void Kart::init(RaceManager::KartType type)
loadData(type, animations);
m_kart_gfx = new KartGFX(this);
m_skidding = new Skidding(this,
m_kart_properties->getSkiddingProperties());
m_skidding = new Skidding(this);
// Create the stars effect
m_stars_effect =
new Stars(getNode(),
@ -363,8 +365,8 @@ void Kart::reset()
// In case that the kart was in the air, in which case its
// linear damping is 0
if(m_body)
m_body->setDamping(m_kart_properties->getChassisLinearDamping(),
m_kart_properties->getChassisAngularDamping() );
m_body->setDamping(m_kart_properties->getStabilityChassisLinearDamping(),
m_kart_properties->getStabilityChassisAngularDamping());
if(m_terrain_sound)
{
@ -521,8 +523,7 @@ void Kart::blockViewWithPlunger()
{
// Avoid that a plunger extends the plunger time
if(m_view_blocked_by_plunger<=0 && !isShielded())
m_view_blocked_by_plunger =
m_kart_properties->getPlungerInFaceTime() * m_difficulty->getPlungerInFaceTime();
m_view_blocked_by_plunger = m_kart_properties->getPlungerInFaceTime();
if(isShielded())
{
decreaseShieldTime();
@ -553,6 +554,12 @@ btTransform Kart::getAlignedTransform(const float custom_pitch)
return trans;
} // getAlignedTransform
// ----------------------------------------------------------------------------
float Kart::getTimeFullSteer(float steer) const
{
return m_kart_properties->getTurnTimeFullSteer().get(steer);
} // getTimeFullSteer
// ----------------------------------------------------------------------------
/** Creates the physical representation of this kart. Atm it uses the actual
* extention of the kart model to determine the size of the collision body.
@ -595,7 +602,7 @@ void Kart::createPhysics()
if (y == -1)
{
int index = (x + 1) / 2 + 1 - z; // get index of wheel
float f = getKartProperties()->getPhysicalWheelPosition();
float f = m_kart_properties->getPhysicalWheelPosition();
// f < 0 indicates to use the old physics position, i.e.
// to place the wheels outside of the chassis
if(f<0)
@ -642,8 +649,8 @@ void Kart::createPhysics()
createBody(mass, trans, &m_kart_chassis,
m_kart_properties->getRestitution());
m_user_pointer.set(this);
m_body->setDamping(m_kart_properties->getChassisLinearDamping(),
m_kart_properties->getChassisAngularDamping() );
m_body->setDamping(m_kart_properties->getStabilityChassisLinearDamping(),
m_kart_properties->getStabilityChassisAngularDamping() );
// Reset velocities
// ----------------
@ -672,9 +679,9 @@ void Kart::createPhysics()
tuning.m_maxSuspensionTravel =
m_kart_properties->getSuspensionTravel();
tuning.m_maxSuspensionForce =
m_kart_properties->getMaxSuspensionForce();
m_kart_properties->getSuspensionMaxForce();
const Vec3 &cs = getKartProperties()->getGravityCenterShift();
const Vec3 &cs = m_kart_properties->getGravityCenterShift();
for(unsigned int i=0; i<4; i++)
{
bool is_front_wheel = i<2;
@ -684,10 +691,10 @@ void Kart::createPhysics()
m_kart_model->getWheelGraphicsRadius(i),
tuning, is_front_wheel);
wheel.m_suspensionStiffness = m_kart_properties->getSuspensionStiffness();
wheel.m_wheelsDampingRelaxation = m_kart_properties->getWheelDampingRelaxation();
wheel.m_wheelsDampingCompression = m_kart_properties->getWheelDampingCompression();
wheel.m_wheelsDampingRelaxation = m_kart_properties->getWheelsDampingRelaxation();
wheel.m_wheelsDampingCompression = m_kart_properties->getWheelsDampingCompression();
wheel.m_frictionSlip = m_kart_properties->getFrictionSlip();
wheel.m_rollInfluence = m_kart_properties->getRollInfluence();
wheel.m_rollInfluence = m_kart_properties->getStabilityRollInfluence();
}
// Obviously these allocs have to be properly managed/freed
btTransform t;
@ -780,6 +787,34 @@ void Kart::updateWeight()
m_body->setMassProps(mass, inertia);
} // updateWeight
// ------------------------------------------------------------------------
/** Returns the (maximum) speed for a given turn radius.
* \param radius The radius for which the speed needs to be computed. */
float Kart::getSpeedForTurnRadius(float radius) const
{
InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius();
// Convert the turn radius into turn angle
for(std::size_t i = 0; i < turn_angle_at_speed.size(); i++)
turn_angle_at_speed.setY(i, sin(m_kart_properties->getWheelBase() /
turn_angle_at_speed.getY(i)));
float angle = sin(m_kart_properties->getWheelBase() / radius);
return turn_angle_at_speed.getReverse(angle);
} // getSpeedForTurnRadius
// ------------------------------------------------------------------------
/** Returns the maximum steering angle (depending on speed). */
float Kart::getMaxSteerAngle(float speed) const
{
InterpolationArray turn_angle_at_speed = m_kart_properties->getTurnRadius();
// Convert the turn radius into turn angle
for(std::size_t i = 0; i < turn_angle_at_speed.size(); i++)
turn_angle_at_speed.setY(i, sin(m_kart_properties->getWheelBase() /
turn_angle_at_speed.getY(i)));
return turn_angle_at_speed.get(speed);
} // getMaxSteerAngle
//-----------------------------------------------------------------------------
/** Sets that this kart has finished the race and finishing time. It also
* notifies the race_manager about the race completion for this kart.
@ -802,71 +837,103 @@ void Kart::finishedRace(float time)
m_kart_model->finishedRace();
race_manager->kartFinishedRace(this, time);
if ((race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
&& m_controller->isPlayerController())
{
RaceGUIBase* m = World::getWorld()->getRaceGUI();
if (m)
{
if (race_manager->
getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER &&
getPosition() == 2)
m->addMessage(_("You won the race!"), this, 2.0f);
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL)
{
m->addMessage((getPosition() == 1 ?
_("You won the race!") : _("You finished the race!")) ,
this, 2.0f);
}
}
}
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_EASTER_EGG)
{
// Save for music handling in race result gui
setRaceResult();
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
// Skip animation if this kart is eliminated
if (m_eliminated) return;
m_kart_model->setAnimation(m_race_result ?
KartModel::AF_WIN_START : KartModel::AF_LOSE_START);
}
} // finishedRace
//-----------------------------------------------------------------------------
void Kart::setRaceResult()
{
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_NORMAL_RACE ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_TIME_TRIAL)
{
// in modes that support it, start end animation
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
if (m_controller->isPlayerController()) // if player is on this computer
// TODO NetworkController?
if (this->getController()->isPlayerController())
{
PlayerProfile *player = PlayerManager::getCurrentPlayer();
const ChallengeStatus *challenge = player->getCurrentChallengeStatus();
// In case of a GP challenge don't make the end animation depend
// on if the challenge is fulfilled
if(challenge && !challenge->getData()->isGrandPrix())
if (challenge && !challenge->getData()->isGrandPrix())
{
if(challenge->getData()->isChallengeFulfilled())
m_kart_model->setAnimation(KartModel::AF_WIN_START);
if (challenge->getData()->isChallengeFulfilled())
m_race_result = true;
else
m_kart_model->setAnimation(KartModel::AF_LOSE_START);
m_race_result = false;
}
else if(m_race_position<=0.5f*race_manager->getNumberOfKarts() ||
m_race_position==1)
m_kart_model->setAnimation(KartModel::AF_WIN_START);
else if (this->getPosition() <= 0.5f*race_manager->getNumberOfKarts() ||
this->getPosition() == 1)
m_race_result = true;
else
m_kart_model->setAnimation(KartModel::AF_LOSE_START);
RaceGUIBase* m = World::getWorld()->getRaceGUI();
if(m)
{
m->addMessage((getPosition() == 1 ? _("You won the race!") : _("You finished the race!")) ,
this, 2.0f);
}
m_race_result = false;
}
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER)
{
// start end animation
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
if(m_race_position<=2)
m_kart_model->setAnimation(KartModel::AF_WIN_START);
else if(m_race_position>=0.7f*race_manager->getNumberOfKarts())
m_kart_model->setAnimation(KartModel::AF_LOSE_START);
RaceGUIBase* m = World::getWorld()->getRaceGUI();
if(m)
else
{
if (getPosition() == 2)
m->addMessage(_("You won the race!"), this, 2.0f);
if (this->getPosition() <= 0.5f*race_manager->getNumberOfKarts() ||
this->getPosition() == 1)
m_race_result = true;
else
m_race_result = false;
}
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_FOLLOW_LEADER ||
race_manager->getMinorMode() == RaceManager::MINOR_MODE_3_STRIKES)
{
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
// the kart wins if it isn't eliminated
m_race_result = !this->isEliminated();
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
{
// TODO complete together with soccer ai!
m_race_result = true;
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_EASTER_EGG)
{
m_kart_model->setAnimation(KartModel::AF_WIN_START);
setController(new EndController(this, m_controller->getPlayer(),
m_controller));
// Easter egg mode only has one player, so always win
m_race_result = true;
}
else
Log::warn("Kart", "Unknown game mode given.");
} // finishedRace
} // setKartResult
//-----------------------------------------------------------------------------
/** Called when an item is collected. It will either adjust the collected
@ -902,16 +969,13 @@ void Kart::collectedItem(Item *item, int add_info)
item->getEmitter()->getIdent() == "nolok");
// slow down
m_bubblegum_time = m_kart_properties->getBubblegumTime() * m_difficulty->getBubblegumTime();
m_bubblegum_time = m_kart_properties->getBubblegumDuration();
m_bubblegum_torque = ((rand()%2)
? m_kart_properties->getBubblegumTorque()
: -m_kart_properties->getBubblegumTorque()) *
m_difficulty->getBubblegumTorque();
: -m_kart_properties->getBubblegumTorque());
m_max_speed->setSlowdown(MaxSpeed::MS_DECREASE_BUBBLE,
m_kart_properties->getBubblegumSpeedFraction() *
m_difficulty->getBubblegumSpeedFraction(),
m_kart_properties->getBubblegumFadeInTime() *
m_difficulty->getBubblegumFadeInTime(),
m_kart_properties->getBubblegumSpeedFraction() ,
m_kart_properties->getBubblegumFadeInTime(),
m_bubblegum_time);
m_goo_sound->setPosition(getXYZ());
m_goo_sound->play();
@ -927,6 +991,25 @@ void Kart::collectedItem(Item *item, int add_info)
} // collectedItem
//-----------------------------------------------------------------------------
/** Called the first time a kart accelerates after 'ready-set-go'. It searches
* through the startup times to find the appropriate slot, and returns the
* speed-boost from the corresponding entry.
* If the kart started too slow (i.e. slower than the longest time in the
* startup times list), it returns 0.
*/
float Kart::getStartupBoost() const
{
float t = World::getWorld()->getTime();
std::vector<float> startup_times = m_kart_properties->getStartupTime();
for (unsigned int i = 0; i < startup_times.size(); i++)
{
if (t <= startup_times[i])
return m_kart_properties->getStartupBoost()[i];
}
return 0;
} // getStartupBoost
//-----------------------------------------------------------------------------
/** Simulates gears by adjusting the force of the engine. It also takes the
* effect of the zipper into account.
@ -938,21 +1021,17 @@ float Kart::getActualWheelForce()
const std::vector<float>& gear_ratio=m_kart_properties->getGearSwitchRatio();
for(unsigned int i=0; i<gear_ratio.size(); i++)
{
if(m_speed <= m_kart_properties->getMaxSpeed() *
m_difficulty->getMaxSpeed() * gear_ratio[i])
if(m_speed <= m_kart_properties->getEngineMaxSpeed() * gear_ratio[i])
{
assert(!isnan(m_kart_properties->getMaxPower() *
m_difficulty->getMaxPower()));
assert(!isnan(m_kart_properties->getEnginePower()));
assert(!isnan(m_kart_properties->getGearPowerIncrease()[i]));
return m_kart_properties->getMaxPower() *
m_difficulty->getMaxPower()
*m_kart_properties->getGearPowerIncrease()[i]
+add_force;
return m_kart_properties->getEnginePower()
* m_kart_properties->getGearPowerIncrease()[i]
+ add_force;
}
}
assert(!isnan(m_kart_properties->getMaxPower() * m_difficulty->getMaxPower()));
return m_kart_properties->getMaxPower() * m_difficulty->getMaxPower()
+add_force * 2;
assert(!isnan(m_kart_properties->getEnginePower()));
return m_kart_properties->getEnginePower() + add_force * 2;
} // getActualWheelForce
@ -1149,12 +1228,12 @@ void Kart::update(float dt)
// When the kart is jumping, linear damping reduces the falling speed
// of a kart so much that it can appear to be in slow motion. So
// disable linear damping if a kart is in the air
m_body->setDamping(0, m_kart_properties->getChassisAngularDamping());
m_body->setDamping(0, m_kart_properties->getStabilityChassisAngularDamping());
}
else
{
m_body->setDamping(m_kart_properties->getChassisLinearDamping(),
m_kart_properties->getChassisAngularDamping());
m_body->setDamping(m_kart_properties->getStabilityChassisLinearDamping(),
m_kart_properties->getStabilityChassisAngularDamping());
}
if(m_kart_animation)
@ -1322,7 +1401,7 @@ void Kart::update(float dt)
static video::SColor green(255, 61, 87, 23);
// draw skidmarks if relevant (we force pink skidmarks on when hitting a bubblegum)
if(m_kart_properties->getSkiddingProperties()->hasSkidmarks())
if(m_kart_properties->getSkidEnabled())
{
m_skidmarks->update(dt,
m_bubblegum_time > 0,
@ -1365,7 +1444,7 @@ void Kart::update(float dt)
// Jump if either the jump is estimated to be long enough, or
// the texture has the jump property set.
if (t > getKartProperties()->getJumpAnimationTime() ||
if (t > m_kart_properties->getJumpAnimationTime() ||
last_m->isJumpTexture())
{
m_kart_model->setAnimation(KartModel::AF_JUMP_START);
@ -1660,33 +1739,23 @@ void Kart::handleZipper(const Material *material, bool play_sound)
material->getZipperParameter(&max_speed_increase, &duration,
&speed_gain, &fade_out_time, &engine_force);
if(max_speed_increase<0)
max_speed_increase = m_kart_properties->getZipperMaxSpeedIncrease() *
m_difficulty->getZipperMaxSpeedIncrease();
max_speed_increase = m_kart_properties->getZipperMaxSpeedIncrease();
if(duration<0)
duration = m_kart_properties->getZipperTime() *
m_difficulty->getZipperTime();
duration = m_kart_properties->getZipperDuration();
if(speed_gain<0)
speed_gain = m_kart_properties->getZipperSpeedGain() *
m_difficulty->getZipperSpeedGain();
speed_gain = m_kart_properties->getZipperSpeedGain();
if(fade_out_time<0)
fade_out_time = m_kart_properties->getZipperFadeOutTime() *
m_difficulty->getZipperFadeOutTime();
fade_out_time = m_kart_properties->getZipperFadeOutTime();
if(engine_force<0)
engine_force = m_kart_properties->getZipperForce() *
m_difficulty->getZipperForce();
engine_force = m_kart_properties->getZipperForce();
}
else
{
max_speed_increase = m_kart_properties->getZipperMaxSpeedIncrease() *
m_difficulty->getZipperMaxSpeedIncrease();
duration = m_kart_properties->getZipperTime() *
m_difficulty->getZipperTime();
speed_gain = m_kart_properties->getZipperSpeedGain() *
m_difficulty->getZipperSpeedGain();
fade_out_time = m_kart_properties->getZipperFadeOutTime() *
m_difficulty->getZipperFadeOutTime();
engine_force = m_kart_properties->getZipperForce() *
m_difficulty->getZipperForce();
max_speed_increase = m_kart_properties->getZipperMaxSpeedIncrease();
duration = m_kart_properties->getZipperDuration();
speed_gain = m_kart_properties->getZipperSpeedGain();
fade_out_time = m_kart_properties->getZipperFadeOutTime();
engine_force = m_kart_properties->getZipperForce();
}
// Ignore a zipper that's activated while braking
if(m_controls.m_brake || m_speed<0) return;
@ -1727,8 +1796,7 @@ void Kart::updateNitro(float dt)
return;
}
m_collected_energy -= dt * m_kart_properties->getNitroConsumption() *
m_difficulty->getNitroConsumption();
m_collected_energy -= dt * m_kart_properties->getNitroConsumption();
if (m_collected_energy < 0)
{
if(m_nitro_sound->getStatus() == SFXBase::SFX_PLAYING)
@ -1742,14 +1810,10 @@ void Kart::updateNitro(float dt)
if(m_nitro_sound->getStatus() != SFXBase::SFX_PLAYING)
m_nitro_sound->play();
m_max_speed->increaseMaxSpeed(MaxSpeed::MS_INCREASE_NITRO,
m_kart_properties->getNitroMaxSpeedIncrease() *
m_difficulty->getNitroMaxSpeedIncrease(),
m_kart_properties->getNitroEngineForce() *
m_difficulty->getNitroEngineForce(),
m_kart_properties->getNitroDuration() *
m_difficulty->getNitroDuration(),
m_kart_properties->getNitroFadeOutTime() *
m_difficulty->getNitroFadeOutTime());
m_kart_properties->getNitroMaxSpeedIncrease(),
m_kart_properties->getNitroEngineForce(),
m_kart_properties->getNitroDuration(),
m_kart_properties->getNitroFadeOutTime());
}
else
{
@ -1808,7 +1872,7 @@ void Kart::crashed(const Material *m, const Vec3 &normal)
#endif
const LinearWorld *lw = dynamic_cast<LinearWorld*>(World::getWorld());
if(getKartProperties()->getTerrainImpulseType()
if(m_kart_properties->getTerrainImpulseType()
==KartProperties::IMPULSE_NORMAL &&
m_vehicle->getCentralImpulseTime()<=0 )
{
@ -1835,7 +1899,7 @@ void Kart::crashed(const Material *m, const Vec3 &normal)
// graph node center (we have to use the previous point since the
// kart might have only now reached the new quad, meaning the kart
// would be pushed forward).
else if(getKartProperties()->getTerrainImpulseType()
else if(m_kart_properties->getTerrainImpulseType()
==KartProperties::IMPULSE_TO_DRIVELINE &&
lw && m_vehicle->getCentralImpulseTime()<=0 &&
World::getWorld()->getTrack()->isPushBackEnabled())
@ -2038,8 +2102,7 @@ void Kart::updatePhysics(float dt)
if(!m_has_started && m_controls.m_accel)
{
m_has_started = true;
float f = m_kart_properties->getStartupBoost() *
m_difficulty->getStartupBoost();
float f = getStartupBoost();
m_max_speed->instantSpeedIncrease(MaxSpeed::MS_INCREASE_ZIPPER,
0.9f*f, f,
/*engine_force*/200.0f,
@ -2107,11 +2170,11 @@ void Kart::updatePhysics(float dt)
// Only apply if near ground instead of purely based on speed avoiding
// the "parachute on top" look.
const Vec3 &v = m_body->getLinearVelocity();
if(/*isNearGround() &&*/ v.getY() < - m_kart_properties->getSuspensionTravel()*60)
if(/*isNearGround() &&*/ v.getY() < - m_kart_properties->getSuspensionTravel() * 60)
{
Vec3 v_clamped = v;
// clamp the speed to 99% of the maxium falling speed.
v_clamped.setY(-m_kart_properties->getSuspensionTravel()*60 * 0.99f);
v_clamped.setY(-m_kart_properties->getSuspensionTravel() * 60 * 0.99f);
//m_body->setLinearVelocity(v_clamped);
}
@ -2213,8 +2276,8 @@ void Kart::updateEnginePowerAndBrakes(float dt)
engine_power *= 5.0f;
// Lose some traction when skidding, to balance the advantage
if(m_controls.m_skid &&
m_kart_properties->getSkiddingProperties()->getSkidVisualTime()==0)
if (m_controls.m_skid &&
m_kart_properties->getSkidVisualTime() == 0)
engine_power *= 0.5f;
applyEngineForce(engine_power*m_controls.m_accel);
@ -2237,10 +2300,8 @@ void Kart::updateEnginePowerAndBrakes(float dt)
m_brake_time += dt;
// Apply the brakes - include the time dependent brake increase
float f = 1 + m_brake_time
* m_kart_properties->getBrakeTimeIncrease() *
m_difficulty->getBrakeTimeIncrease();
m_vehicle->setAllBrakes(m_kart_properties->getBrakeFactor() *
m_difficulty->getBrakeFactor() * f);
* m_kart_properties->getEngineBrakeTimeIncrease();
m_vehicle->setAllBrakes(m_kart_properties->getEngineBrakeFactor() * f);
}
else // m_speed < 0
{
@ -2248,8 +2309,7 @@ void Kart::updateEnginePowerAndBrakes(float dt)
// going backward, apply reverse gear ratio (unless he goes
// too fast backwards)
if ( -m_speed < m_max_speed->getCurrentMaxSpeed()
*m_kart_properties->getMaxSpeedReverseRatio() *
m_difficulty->getMaxSpeedReverseRatio())
*m_kart_properties->getEngineMaxSpeedReverseRatio())
{
// The backwards acceleration is artificially increased to
// allow players to get "unstuck" quicker if they hit e.g.
@ -2418,7 +2478,7 @@ void Kart::loadData(RaceManager::KartType type, bool is_animated_model)
m_slipstream = new SlipStream(this);
if(m_kart_properties->getSkiddingProperties()->hasSkidmarks())
if (m_kart_properties->getSkidEnabled())
{
m_skidmarks = new SkidMarks(*this);
m_skidmarks->adjustFog(
@ -2428,7 +2488,7 @@ void Kart::loadData(RaceManager::KartType type, bool is_animated_model)
if (!CVS->supportsShadows())
{
m_shadow = new Shadow(m_kart_properties, m_node,
m_shadow = new Shadow(m_kart_properties.get(), m_node,
-m_kart_model->getLowestPoint());
}
World::getWorld()->kartAdded(this, m_node);
@ -2602,8 +2662,7 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz,
{
// fabs(speed) is important, otherwise the negative number will
// become a huge unsigned number in the particle scene node!
nitro_frac = fabsf(getSpeed())/(m_kart_properties->getMaxSpeed() *
m_difficulty->getMaxSpeed() );
nitro_frac = fabsf(getSpeed()) / (m_kart_properties->getEngineMaxSpeed());
// The speed of the kart can be higher (due to powerups) than
// the normal maximum speed of the kart.
if(nitro_frac>1.0f) nitro_frac = 1.0f;
@ -2617,8 +2676,7 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz,
// leaning might get less if a kart gets a special that increases
// its maximum speed, but not the current speed (by much). On the
// other hand, that ratio can often be greater than 1.
float speed_frac = m_speed / m_kart_properties->getMaxSpeed() *
m_difficulty->getMaxSpeed();
float speed_frac = m_speed / m_kart_properties->getEngineMaxSpeed();
if(speed_frac>1.0f)
speed_frac = 1.0f;
else if (speed_frac < 0.0f) // no leaning when backwards driving
@ -2626,14 +2684,14 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz,
const float steer_frac = m_skidding->getSteeringFraction();
const float roll_speed = m_kart_properties->getLeanSpeed();
const float roll_speed = m_kart_properties->getLeanSpeed() * DEGREE_TO_RAD;
if(speed_frac > 0.8f && fabsf(steer_frac)>0.5f)
{
// Use steering ^ 7, which means less effect at lower
// steering
const float f = m_skidding->getSteeringFraction();
const float f2 = f*f;
const float max_lean = -m_kart_properties->getMaxLean()
const float max_lean = -m_kart_properties->getLeanMax() * DEGREE_TO_RAD
* f2*f2*f2*f
* speed_frac;
if(max_lean>0)

View File

@ -31,7 +31,6 @@
#include "items/powerup.hpp"
#include "karts/abstract_kart.hpp"
#include "karts/kart_properties.hpp"
#include "karts/player_difficulty.hpp"
#include "utils/no_copy.hpp"
class btKart;
@ -110,6 +109,9 @@ private:
/** Offset of the graphical kart chassis from the physical chassis. */
float m_graphical_y_offset;
/** True if the kart wins, false otherwise. */
bool m_race_result;
/** True if the kart is eliminated. */
bool m_eliminated;
@ -228,7 +230,7 @@ private:
public:
Kart(const std::string& ident, unsigned int world_kart_id,
int position, const btTransform& init_transform,
const PlayerDifficulty *difficulty);
PerPlayerDifficulty difficulty);
virtual ~Kart();
virtual void init(RaceManager::KartType type);
virtual void kartIsInRestNow();
@ -236,6 +238,8 @@ public:
const btQuaternion& off_rotation);
virtual void createPhysics ();
virtual void updateWeight ();
virtual float getSpeedForTurnRadius(float radius) const;
virtual float getMaxSteerAngle(float speed) const;
virtual bool isInRest () const;
virtual void applyEngineForce (float force);
@ -251,6 +255,8 @@ public:
float fade_in_time);
virtual float getSpeedIncreaseTimeLeft(unsigned int category) const;
virtual void collectedItem(Item *item, int random_attachment);
virtual float getStartupBoost() const;
virtual const Material *getMaterial() const;
virtual const Material *getLastMaterial() const;
/** Returns the pitch of the terrain depending on the heading. */
@ -331,15 +337,12 @@ public:
/** Returns the time till full steering is reached for this kart.
* \param steer Current steer value (must be >=0), on which the time till
* full steer depends. */
virtual float getTimeFullSteer(float steer) const
{
return m_kart_properties->getTimeFullSteer(steer);
} // getTimeFullSteer
virtual float getTimeFullSteer(float steer) const;
// ------------------------------------------------------------------------
/** Returns the maximum steering angle for this kart, which depends on the
* speed. */
virtual float getMaxSteerAngle () const
{ return m_kart_properties->getMaxSteerAngle(getSpeed()); }
{ return getMaxSteerAngle(getSpeed()); }
// ------------------------------------------------------------------------
/** Returns the skidding object for this kart (which can be used to query
* skidding related values). */
@ -438,6 +441,12 @@ public:
float getWrongwayCounter() { return m_wrongway_counter; }
// ------------------------------------------------------------------------
void setWrongwayCounter(float counter) { m_wrongway_counter = counter; }
// ------------------------------------------------------------------------
/** Returns whether this kart wins or loses. */
virtual bool getRaceResult() const { return m_race_result; }
// ------------------------------------------------------------------------
/** Set this kart race result. */
void setRaceResult();
}; // Kart

View File

@ -326,8 +326,7 @@ void KartGFX::updateTerrain(const ParticleKind *pk)
if (skidding > 1.0f && on_ground)
rate = fabsf(m_kart->getControls().m_steer) > 0.8 ? skidding - 1 : 0;
else if (speed >= 0.5f && on_ground)
rate = speed/m_kart->getKartProperties()->getMaxSpeed() *
m_kart->getPlayerDifficulty()->getMaxSpeed();
rate = speed/m_kart->getKartProperties()->getEngineMaxSpeed();
else
{
pe->setCreationRateAbsolute(0);

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,7 @@
#ifndef HEADER_KART_PROPERTIES_HPP
#define HEADER_KART_PROPERTIES_HPP
#include <memory>
#include <string>
#include <vector>
@ -37,9 +38,11 @@ using namespace irr;
#include "utils/interpolation_array.hpp"
#include "utils/vec3.hpp"
class AbstractCharacteristic;
class AIProperties;
class CachedCharacteristic;
class CombinedCharacteristic;
class Material;
class SkiddingProperties;
class XMLNode;
/**
@ -47,7 +50,7 @@ class XMLNode;
* This includes size, name, identifier, physical properties etc.
* It is atm also the base class for STKConfig, which stores the default values
* for all physics constants.
* Note that KartProperies is copied (when setting the default values from
* Note that KartProperties is copied (when setting the default values from
* stk_config.
*
* \ingroup karts
@ -58,16 +61,11 @@ private:
/** Base directory for this kart. */
std::string m_root;
/** The skididing properties for this kart, as a separate object in order
* to reduce dependencies (and therefore compile time) when changing
* any skidding property. */
SkiddingProperties *m_skidding_properties;
/** AI Properties for this kart, as a separate object in order to
* reduce dependencies (and therefore compile time) when changing
* any AI property. There is one separate object for each
* difficulty. */
AIProperties *m_ai_properties[RaceManager::DIFFICULTY_COUNT];
std::shared_ptr<AIProperties> m_ai_properties[RaceManager::DIFFICULTY_COUNT];
/** The absolute path of the icon texture to use. */
Material *m_icon_material;
@ -82,7 +80,7 @@ private:
/** The kart model and wheels. It is mutable since the wheels of the
* KartModel can rotate and turn, and animations are played, but otherwise
* the kart_properties object is const. */
mutable KartModel *m_kart_model;
mutable std::shared_ptr<KartModel> m_kart_model;
/** List of all groups the kart belongs to. */
std::vector<std::string> m_groups;
@ -119,33 +117,18 @@ private:
* status bar and on the track-view. */
int m_shape; /**< Number of vertices in polygon when
* drawing the dot on the mini map. */
/** The physical, item, etc. characteristics of this kart that are loaded
* from the xml file.
*/
std::shared_ptr<AbstractCharacteristic> m_characteristic;
/** The base characteristics combined with the characteristics of this kart. */
std::shared_ptr<CombinedCharacteristic> m_combined_characteristic;
/** The cached combined characteristics. */
std::shared_ptr<CachedCharacteristic> m_cached_characteristic;
// Physic properties
// -----------------
/** Weight of kart. */
float m_mass;
/** Maximum force from engine for each difficulty. */
std::vector<float> m_engine_power;
/** Braking factor * engine_power braking force. */
float m_brake_factor;
/** Brake_time * m_brake_time_increase will increase the break time
* over time. */
float m_brake_time_increase;
/** Time for player karts to reach full steer angle. */
InterpolationArray m_time_full_steer;
/** Time for steering to go back to zero from full steer. */
float m_time_reset_steer;
/** A torque impulse applied to keep the kart parallel to the ground. */
float m_smooth_flying_impulse;;
/** The turn angle depending on speed. */
InterpolationArray m_turn_angle_at_speed;
/** If != 0 a bevelled box shape is used by using a point cloud as a
* collision shape. */
Vec3 m_bevel_factor;
@ -159,39 +142,6 @@ private:
*/
float m_physical_wheel_position;
/** Time a kart is moved upwards after when it is rescued. */
float m_rescue_time;
/** Distance the kart is raised before dropped. */
float m_rescue_height;
/** Time an animated explosion is shown. Longer = more delay for kart. */
float m_explosion_time;
/** How far away from an explosion karts will still be affected. */
float m_explosion_radius;
/** How long a kart is invulnerable after it is hit by an explosion. */
float m_explosion_invulnerability_time;
/** Duration a zipper is active. */
float m_zipper_time;
/** Fade out time for a zipper. */
float m_zipper_fade_out_time;
/** Additional force added to the acceleration. */
float m_zipper_force;
/** Initial one time speed gain. */
float m_zipper_speed_gain;
/** Absolute increase of the kart's maximum speed (in m/s). */
float m_zipper_max_speed_increase;
/** Vertical offset after rescue. */
float m_rescue_vert_offset;
/** Minimum time during which nitro is consumed when pressing
* the nitro key (to prevent using in very small bursts)
*/
@ -202,64 +152,12 @@ private:
/** Filename of the wheel models. */
std::string m_wheel_filename[4];
/** Radius of the graphical wheels. */
float m_wheel_graphics_radius[4];
/** An additional Y offset added to the y position of the graphical
* chassis. Useful for karts that don't have enough space for suspension
* compression. */
float m_graphical_y_offset;
/** Max. length of plunger rubber band. */
float m_rubber_band_max_length;
/** Force of an attached rubber band. */
/** Duration a rubber band works. */
float m_rubber_band_force;
/** How long the rubber band will fly. */
float m_rubber_band_duration;
/** Increase of maximum speed of the kart when the rubber band pulls. */
float m_rubber_band_speed_increase;
/** Fade out time when the rubber band is removed. */
float m_rubber_band_fade_out_time;
/**Duration of plunger in face depending on difficulty. */
std::vector<float> m_plunger_in_face_duration;
/** Wheel base of the kart. */
float m_wheel_base;
/** Nitro consumption. */
float m_nitro_consumption;
/** Nitro amount for small bottle. */
float m_nitro_small_container;
/** Nitro amount for big bittle. */
float m_nitro_big_container;
/** How much the speed of a kart might exceed its maximum speed (in m/s). */
float m_nitro_max_speed_increase;
/** Additional engine force to affect the kart. */
float m_nitro_engine_force;
/** How long the increased nitro max speed will be valid after
* the kart stops using nitro (and the fade-out-time starts). */
float m_nitro_duration;
/** Duration during which the increased maximum speed
* due to nitro fades out. */
float m_nitro_fade_out_time;
/** Maximum nitro a kart can collect. */
float m_nitro_max;
/** Bubble gum diration. */
float m_bubblegum_time;
/** Torque to add when a bubble gum was hit in order to make the kart go
* sideways a bit. */
float m_bubblegum_torque;
/** Fraction of top speed that can be reached maximum after hitting a
* bubble gum. */
float m_bubblegum_speed_fraction;
/** How long to fade in the slowdown for a bubble gum. */
float m_bubblegum_fade_in_time;
/** Square of the maximum distance a swatter can operate. */
float m_swatter_distance2;
/** 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;
/** The maximum roll a kart graphics should show when driving in a fast
* curve. This is read in as degrees, but stored in radians. */
@ -269,57 +167,19 @@ private:
* (in radians/second). */
float m_lean_speed;
/** How long a jump must be in order to trigger the jump animation. */
float m_jump_animation_time;
/** Engine sound effect. */
std::string m_engine_sfx_type;
// bullet physics data
// -------------------
float m_suspension_stiffness;
float m_wheel_damping_relaxation;
float m_wheel_damping_compression;
float m_max_suspension_force;
float m_friction_slip;
float m_roll_influence;
/** Parameters for the speed-weighted objects */
SpeedWeightedObject::Properties m_speed_weighted_object_properties;
/** An impulse pushing the kart down which is proportional to speed. So
* the actual impulse is speed * m_downward_impulse_factor. Set it to
* 0 to disable completely. Based on
* http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=6059\
* &p=21240&hilit=vehicle#p21240 */
float m_downward_impulse_factor;
/** Artifical acceleration that pulls a kart down onto the track if one
* axis loses contact with the track. */
float m_track_connection_accel;
/** Linear damping of the chassis to prevent it from toppling over. */
float m_chassis_linear_damping;
/** Angular damping to prevent it from turning too easily. */
float m_chassis_angular_damping;
/** The maximum speed at each difficulty. */
std::vector<float> m_max_speed;
float m_max_speed_reverse_ratio;
/** Shift of center of gravity. */
Vec3 m_gravity_center_shift;
/** The suspension reaction is dampened to reach an exponential behaviour.
* See http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=7369\
* &p=25236&hilit=vehicle#p25236 for details. */
bool m_exp_spring_response;
float m_suspension_rest;
float m_suspension_travel;
public:
/** STK can add an impulse to push karts away from the track in case
* of a kart-track collision. This can be done in two ways: either
@ -345,80 +205,33 @@ private:
/** The restitution factor to be used in collsions for this kart. */
float m_restitution;
/** How far behind a kart slipstreaming is effective. */
float m_slipstream_length;
/** How wide the slipstream area is at the end. */
float m_slipstream_width;
/** Time after which sstream gives a bonus. */
float m_slipstream_collect_time;
/** Time slip-stream bonus is effective. */
float m_slipstream_use_time;
/** Additional power due to sstreaming. */
float m_slipstream_add_power;
/** Minimum speed for slipstream to take effect. */
float m_slipstream_min_speed;
/** How much the speed of the kart might exceed its
* normal maximum speed. */
float m_slipstream_max_speed_increase;
/** How long the higher speed lasts after slipstream stopped working. */
float m_slipstream_duration;
/** How long the slip stream speed increase will gradually be reduced. */
float m_slipstream_fade_out_time;
/** Distance of normal camera from kart. */
float m_camera_distance;
/** Up angle of the camera in relation to the pitch of the kart when
* driving forwards. */
float m_camera_forward_up_angle;
/** Up angle of the camera in relation to the pitch of the kart when
* driving backwards. */
float m_camera_backward_up_angle;
/** The following two vectors define at what ratio of the maximum speed what
* gear is selected. E.g. 0.25 means: if speed <=0.25*maxSpeed --> gear 1,
* 0.5 means: if speed <=0.5 *maxSpeed --> gear 2 */
std::vector<float> m_gear_switch_ratio;
/** This vector contains the increase in max power (to simulate different
* gears), e.g. 2.5 as first entry means: 2.5*maxPower in gear 1. See
m_gear_switch_ratio). */
std::vector<float> m_gear_power_increase;
/** If the kart starts within the specified time at index I after 'go',
* it receives the speed boost from m_startup_boost[I]. */
std::vector<float> m_startup_times;
/** The startup boost is the kart starts fast enough. */
std::vector<float> m_startup_boost;
void load (const std::string &filename,
const std::string &node);
void combineCharacteristics();
public:
/** Returns the string representation of a per-player difficulty. */
static std::string getPerPlayerDifficultyAsString(PerPlayerDifficulty d);
KartProperties (const std::string &filename="");
~KartProperties ();
void copyForPlayer (const KartProperties *source);
void copyFrom (const KartProperties *source);
void getAllData (const XMLNode * root);
void checkAllSet (const std::string &filename);
float getStartupBoost () const;
bool isInGroup (const std::string &group) const;
bool operator<(const KartProperties &other) const;
// ------------------------------------------------------------------------
/** Returns the (maximum) speed for a given turn radius.
* \param radius The radius for which the speed needs to be computed. */
float getSpeedForTurnRadius(float radius) const {
float angle = sin(m_wheel_base / radius);
return m_turn_angle_at_speed.getReverse(angle);
} // getSpeedForTurnRadius
/** Returns the characteristics for this kart. */
const AbstractCharacteristic* getCharacteristic() const;
// ------------------------------------------------------------------------
/** Returns the maximum steering angle (depending on speed). */
float getMaxSteerAngle(float speed) const {
return m_turn_angle_at_speed.get(speed);
} // getMaxSteerAngle
/** Returns the characteristics for this kart combined with the base
* characteristic. This value isn't used for the race, because the
* difficulty is missing, but it can be used e.g. for the kart stats widget.
*/
const AbstractCharacteristic* getCombinedCharacteristic() const;
// ------------------------------------------------------------------------
/** Returns the material for the kart icons. */
@ -462,6 +275,10 @@ public:
/** Returns the internal identifier of this kart. */
const std::string& getIdent () const {return m_ident; }
// ------------------------------------------------------------------------
/** Returns the type of this kart. */
const std::string& getKartType () const { return m_kart_type; }
// ------------------------------------------------------------------------
/** Returns the shadow texture to use. */
video::ITexture *getShadowTexture() const {return m_shadow_texture; }
@ -492,47 +309,6 @@ public:
/** Returns the list of groups this kart belongs to. */
const std::vector<std::string>&
getGroups () const {return m_groups; }
// ------------------------------------------------------------------------
/** Returns the mass of this kart. */
float getMass () const {return m_mass; }
// ------------------------------------------------------------------------
/** Returns the maximum engine power depending on difficulty. */
float getMaxPower () const
{return m_engine_power[race_manager->getDifficulty()];}
// ------------------------------------------------------------------------
/** Returns the time the kart needs to fully steer in one direction from
* steering straight depending on the current steering value.
* \param steer Current steering value, must be >=0. */
float getTimeFullSteer(float steer) const
{
assert(steer>=0);
return m_time_full_steer.get(steer);
} // getTimeFullSteer
// ------------------------------------------------------------------------
/** Returns the time the kart needs to go back to steering straight from
* full steer. */
float getTimeResetSteer () const { return m_time_reset_steer; }
// ------------------------------------------------------------------------
/** Get braking information. */
float getBrakeFactor () const {return m_brake_factor; }
// ------------------------------------------------------------------------
/** Returns the additional brake factor which depends on time. */
float getBrakeTimeIncrease() const { return m_brake_time_increase; }
// ------------------------------------------------------------------------
/** Returns the torque scaling factor used to keep the karts parallel to
* the ground when flying. */
float getSmoothFlyingImpulse() const
{
return m_smooth_flying_impulse;
} // getSmoothFlyingImpulse
// ------------------------------------------------------------------------
/** Get maximum reverse speed ratio. */
float getMaxSpeedReverseRatio() const {return m_max_speed_reverse_ratio; }
// ------------------------------------------------------------------------
/** Returns the engine type (used to change sfx depending on kart size). */
@ -540,31 +316,9 @@ public:
// Bullet physics get functions
//-----------------------------
/** Returns the suspension stiffness. */
float getSuspensionStiffness () const {return m_suspension_stiffness; }
// ------------------------------------------------------------------------
/** Returns damping relaxation. */
float getWheelDampingRelaxation () const
{return m_wheel_damping_relaxation; }
// ------------------------------------------------------------------------
/** Returns the wheel damping compression. */
float getWheelDampingCompression() const
{return m_wheel_damping_compression;}
// ------------------------------------------------------------------------
/** Returns maximum suspension force. */
float getMaxSuspensionForce() const {return m_max_suspension_force; }
// ------------------------------------------------------------------------
/** Returns friction slip. */
float getFrictionSlip () const {return m_friction_slip; }
// ------------------------------------------------------------------------
/** Returns roll influence. */
float getRollInfluence () const {return m_roll_influence; }
// ------------------------------------------------------------------------
/** Returns parameters for the speed-weighted objects */
const SpeedWeightedObject::Properties& getSpeedWeightedObjectProperties() const
@ -576,99 +330,11 @@ public:
/** Returns the wheel base (distance front to rear axis). */
float getWheelBase () const {return m_wheel_base; }
// ------------------------------------------------------------------------
/** Returns linear damping of chassis. */
float getChassisLinearDamping () const {return m_chassis_linear_damping;}
// ------------------------------------------------------------------------
/** Returns angular damping of chassis. */
float getChassisAngularDamping () const
{return m_chassis_angular_damping; }
// ------------------------------------------------------------------------
/** Artifical downward impulse every frame. */
float getDownwardImpulseFactor() const { return m_downward_impulse_factor;}
// ------------------------------------------------------------------------
/** Returns artificial acceleration to keep wheels on track. */
float getTrackConnectionAccel () const {return m_track_connection_accel;}
// ------------------------------------------------------------------------
/** Returns the maximum speed dependent on the difficult level. */
float getMaxSpeed () const
{
return m_max_speed[race_manager->getDifficulty()];
}
// ------------------------------------------------------------------------
/** Return the absolute maximum speed, independent on the difficulty. */
float getAbsMaxSpeed () const
{
return m_max_speed[m_max_speed.size()-1];
}
// ------------------------------------------------------------------------
/** Returns the nitro consumption. */
float getNitroConsumption () const {return m_nitro_consumption; }
// ------------------------------------------------------------------------
/** Returns the amount of nitro for a small container. */
float getNitroSmallContainer () const {return m_nitro_small_container; }
// ------------------------------------------------------------------------
/** Returns the amount of nitro for a big container. */
float getNitroBigContainer () const {return m_nitro_big_container; }
// ------------------------------------------------------------------------
/** Returns the increase of maximum speed due to nitro. */
float getNitroMaxSpeedIncrease () const
{return m_nitro_max_speed_increase; }
// ------------------------------------------------------------------------
float getNitroEngineForce () const {return m_nitro_engine_force; }
// ------------------------------------------------------------------------
/** Returns how long the increased nitro max speed will be valid after
* the kart stops using nitro (and the fade-out-time starts). */
float getNitroDuration () const {return m_nitro_duration; }
// ------------------------------------------------------------------------
/** Returns the duration during which the increased maximum speed
* due to nitro fades out. */
float getNitroFadeOutTime () const {return m_nitro_fade_out_time; }
// ------------------------------------------------------------------------
/** Returns the maximum amount of nitro a kart can store. */
float getNitroMax () const {return m_nitro_max; }
// ------------------------------------------------------------------------
/** Returns how long a bubble gum is active. */
float getBubblegumTime() const { return m_bubblegum_time; }
// ------------------------------------------------------------------------
/** Returns the torque to add when a bubble gum was hit . */
float getBubblegumTorque() const { return m_bubblegum_torque; }
// ------------------------------------------------------------------------
/** Returns the fraction of top speed that can be reached maximum after
* hitting a bubble gum. */
float getBubblegumSpeedFraction() const {return m_bubblegum_speed_fraction;}
// ------------------------------------------------------------------------
/** Returns how long to fade in the slowdown for a bubble gum. */
float getBubblegumFadeInTime() const { return m_bubblegum_fade_in_time; }
// ------------------------------------------------------------------------
/** Returns a shift of the center of mass (lowering the center of mass
* makes the karts more stable. */
const Vec3&getGravityCenterShift() const {return m_gravity_center_shift; }
// ------------------------------------------------------------------------
/** Retusn suspension rest length. */
float getSuspensionRest () const {return m_suspension_rest; }
// ------------------------------------------------------------------------
/** Returns the amount the suspension can extend. */
float getSuspensionTravel () const {return m_suspension_travel; }
// ------------------------------------------------------------------------
/** Returns if the spring should be exponentially dampened. */
bool getExpSpringResponse() const {return m_exp_spring_response; }
// ------------------------------------------------------------------------
/** Returns an artificial impulse to push karts away from the terrain
* it hits. */
@ -693,128 +359,6 @@ public:
/** Returns the restitution factor for this kart. */
float getRestitution () const { return m_restitution; }
// ------------------------------------------------------------------------
/** Returns the vertical offset when rescuing karts to avoid karts being
* rescued in (or under) the track. */
float getVertRescueOffset () const {return m_rescue_vert_offset; }
// ------------------------------------------------------------------------
/** Returns the time a kart is rised during a rescue. */
float getRescueTime () const {return m_rescue_time; }
// ------------------------------------------------------------------------
/** Returns the height a kart is moved to during a rescue. */
float getRescueHeight () const {return m_rescue_height; }
// ------------------------------------------------------------------------
/** Returns the time an explosion animation is shown. */
float getExplosionTime () const {return m_explosion_time; }
// ------------------------------------------------------------------------
/** Returns the height of the explosion animation. */
float getExplosionRadius () const {return m_explosion_radius; }
// ------------------------------------------------------------------------
/** Returns how long a kart is invulnerable after being hit by an
explosion. */
float getExplosionInvulnerabilityTime() const
{ return m_explosion_invulnerability_time; }
// ------------------------------------------------------------------------
/** Returns the maximum length of a rubber band before it breaks. */
float getRubberBandMaxLength () const {return m_rubber_band_max_length;}
// ------------------------------------------------------------------------
/** Returns force a rubber band has when attached to a kart. */
float getRubberBandForce () const {return m_rubber_band_force; }
// ------------------------------------------------------------------------
/** Returns the duration a rubber band is active for. */
float getRubberBandDuration () const {return m_rubber_band_duration; }
// ------------------------------------------------------------------------
/** Returns the increase of maximum speed while a rubber band is
* pulling. */
float getRubberBandSpeedIncrease() const
{
return m_rubber_band_speed_increase;
}
// ------------------------------------------------------------------------
/** 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 how far behind a kart slipstreaming works. */
float getSlipstreamLength () const {return m_slipstream_length; }
// ------------------------------------------------------------------------
/** Returns how wide the slipstream area is at the end. */
float getSlipstreamWidth () const {return m_slipstream_width; }
// ------------------------------------------------------------------------
/** Returns time after which slipstream has maximum effect. */
float getSlipstreamCollectTime () const
{return m_slipstream_collect_time; }
// ------------------------------------------------------------------------
/** Returns time after which slipstream has maximum effect. */
float getSlipstreamUseTime () const {return m_slipstream_use_time; }
// ------------------------------------------------------------------------
/** Returns additional power due to slipstreaming. */
float getSlipstreamAddPower () const {return m_slipstream_add_power; }
// ------------------------------------------------------------------------
/** Returns the minimum slipstream speed. */
float getSlipstreamMinSpeed () const {return m_slipstream_min_speed; }
// ------------------------------------------------------------------------
/** Returns the increase of the maximum speed of a kart
* due to slipstream. */
float getSlipstreamMaxSpeedIncrease() const
{ return m_slipstream_max_speed_increase; }
// ------------------------------------------------------------------------
/** Returns how long the higher speed lasts after slipstream
* stopped working. */
float getSlipstreamDuration () const { return m_slipstream_duration; }
// ------------------------------------------------------------------------
/** Returns how long the slip stream speed increase will gradually
* be reduced. */
float getSlipstreamFadeOutTime () const
{ return m_slipstream_fade_out_time; }
// ------------------------------------------------------------------------
/** Returns the scale factor by which the shadow plane
* had to be set. */
@ -830,79 +374,18 @@ public:
* had to be set. */
float getShadowZOffset () const {return m_shadow_z_offset; }
// ------------------------------------------------------------------------
/** Returns a pointer to the skidding properties. */
const SkiddingProperties *getSkiddingProperties() const
{ return m_skidding_properties; }
// ------------------------------------------------------------------------
/** Returns a pointer to the AI properties. */
const AIProperties *getAIPropertiesForDifficulty() const
{
return m_ai_properties[race_manager->getDifficulty()];
return m_ai_properties[race_manager->getDifficulty()].get();
} // getAIProperties
// ------------------------------------------------------------------------
/** Returns ratio of current speed to max speed at which the gear will
* change (for our simualated gears = simple change of engine power). */
const std::vector<float>&
getGearSwitchRatio () const {return m_gear_switch_ratio; }
// ------------------------------------------------------------------------
/** Returns the power increase depending on gear. */
const std::vector<float>&
getGearPowerIncrease () const {return m_gear_power_increase; }
// ------------------------------------------------------------------------
/** Returns the average power of the kart (in all gears). */
const float getAvgPower () const;
// ------------------------------------------------------------------------
/** Returns distance between kart and camera. */
float getCameraDistance () const {return m_camera_distance; }
// ------------------------------------------------------------------------
/** Returns the angle the camera has relative to the pitch of the kart. */
float getCameraForwardUpAngle () const
{return m_camera_forward_up_angle; }
// ------------------------------------------------------------------------
/** Returns the angle the camera has relative to the pitch of the kart. */
float getCameraBackwardUpAngle () const
{return m_camera_backward_up_angle; }
// ------------------------------------------------------------------------
/** Returns the full path where the files for this kart are stored. */
const std::string& getKartDir () const {return m_root; }
// ------------------------------------------------------------------------
/** Returns the square of the maximum distance at which a swatter
* can hit karts. */
float getSwatterDistance2() const { return m_swatter_distance2; }
// ------------------------------------------------------------------------
/** 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; }
// ------------------------------------------------------------------------
/** The maximum leaning a kart should show (In radians). */
float getMaxLean() const { return m_max_lean; }
// ------------------------------------------------------------------------
/** The speed with which a kart should lean (in radians/s). */
float getLeanSpeed() const { return m_lean_speed; }
// ------------------------------------------------------------------------
/** Return show long a jump must last in order to play the jump
* animation. */
float getJumpAnimationTime() const { return m_jump_animation_time; }
// ------------------------------------------------------------------------
/** Returns minimum time during which nitro is consumed when pressing nitro
* key, to prevent using nitro in very short bursts
@ -921,8 +404,142 @@ public:
{
return m_physical_wheel_position;
} // getPhysicalWheelPosition
// ------------------------------------------------------------------------
float getAvgPower() const;
// Script-generated content generated by tools/create_kart_properties.py defs
// Please don't change the following tag. It will be automatically detected
// by the script and replace the contained content.
// To update the code, use tools/update_characteristics.py
/* <characteristics-start kpdefs> */
float getSuspensionStiffness() const;
float getSuspensionRest() const;
float getSuspensionTravel() const;
bool getSuspensionExpSpringResponse() const;
float getSuspensionMaxForce() const;
float getStabilityRollInfluence() const;
float getStabilityChassisLinearDamping() const;
float getStabilityChassisAngularDamping() const;
float getStabilityDownwardImpulseFactor() const;
float getStabilityTrackConnectionAccel() const;
float getStabilitySmoothFlyingImpulse() const;
InterpolationArray getTurnRadius() const;
float getTurnTimeResetSteer() const;
InterpolationArray getTurnTimeFullSteer() const;
float getEnginePower() const;
float getEngineMaxSpeed() const;
float getEngineBrakeFactor() const;
float getEngineBrakeTimeIncrease() const;
float getEngineMaxSpeedReverseRatio() const;
std::vector<float> getGearSwitchRatio() const;
std::vector<float> getGearPowerIncrease() const;
float getMass() const;
float getWheelsDampingRelaxation() const;
float getWheelsDampingCompression() const;
float getCameraDistance() const;
float getCameraForwardUpAngle() const;
float getCameraBackwardUpAngle() const;
float getJumpAnimationTime() const;
float getLeanMax() const;
float getLeanSpeed() const;
float getAnvilDuration() const;
float getAnvilWeight() const;
float getAnvilSpeedFactor() const;
float getParachuteFriction() const;
float getParachuteDuration() const;
float getParachuteDurationOther() const;
float getParachuteLboundFraction() const;
float getParachuteUboundFraction() const;
float getParachuteMaxSpeed() const;
float getBubblegumDuration() const;
float getBubblegumSpeedFraction() const;
float getBubblegumTorque() const;
float getBubblegumFadeInTime() const;
float getBubblegumShieldDuration() const;
float getZipperDuration() const;
float getZipperForce() const;
float getZipperSpeedGain() const;
float getZipperMaxSpeedIncrease() const;
float getZipperFadeOutTime() const;
float getSwatterDuration() const;
float getSwatterDistance() const;
float getSwatterSquashDuration() const;
float getSwatterSquashSlowdown() const;
float getPlungerBandMaxLength() const;
float getPlungerBandForce() const;
float getPlungerBandDuration() const;
float getPlungerBandSpeedIncrease() const;
float getPlungerBandFadeOutTime() const;
float getPlungerInFaceTime() const;
std::vector<float> getStartupTime() const;
std::vector<float> getStartupBoost() const;
float getRescueDuration() const;
float getRescueVertOffset() const;
float getRescueHeight() const;
float getExplosionDuration() const;
float getExplosionRadius() const;
float getExplosionInvulnerabilityTime() const;
float getNitroDuration() const;
float getNitroEngineForce() const;
float getNitroConsumption() const;
float getNitroSmallContainer() const;
float getNitroBigContainer() const;
float getNitroMaxSpeedIncrease() const;
float getNitroFadeOutTime() const;
float getNitroMax() const;
float getSlipstreamDuration() const;
float getSlipstreamLength() const;
float getSlipstreamWidth() const;
float getSlipstreamCollectTime() const;
float getSlipstreamUseTime() const;
float getSlipstreamAddPower() const;
float getSlipstreamMinSpeed() const;
float getSlipstreamMaxSpeedIncrease() const;
float getSlipstreamFadeOutTime() const;
float getSkidIncrease() const;
float getSkidDecrease() const;
float getSkidMax() const;
float getSkidTimeTillMax() const;
float getSkidVisual() const;
float getSkidVisualTime() const;
float getSkidRevertVisualTime() const;
float getSkidMinSpeed() const;
std::vector<float> getSkidTimeTillBonus() const;
std::vector<float> getSkidBonusSpeed() const;
std::vector<float> getSkidBonusTime() const;
std::vector<float> getSkidBonusForce() const;
float getSkidPhysicalJumpTime() const;
float getSkidGraphicalJumpTime() const;
float getSkidPostSkidRotateFactor() const;
float getSkidReduceTurnMin() const;
float getSkidReduceTurnMax() const;
bool getSkidEnabled() const;
/* <characteristics-end kpdefs> */
}; // KartProperties
#endif
/* EOF */

View File

@ -28,6 +28,7 @@
#include "guiengine/engine.hpp"
#include "io/file_manager.hpp"
#include "karts/kart_properties.hpp"
#include "karts/xml_characteristic.hpp"
#include "utils/log.hpp"
#include "utils/string_utils.hpp"
@ -175,12 +176,68 @@ void KartPropertiesManager::loadAllKarts(bool loading_icon)
} // loadAllKarts
//-----------------------------------------------------------------------------
/** Loads a single kart and (if not disabled) the oorresponding 3d model.
/** Loads the characteristics from the characteristics config file.
* \param root The xml node where the characteristics are stored.
*/
void KartPropertiesManager::loadCharacteristics(const XMLNode *root)
{
// Load base characteristics
std::vector<XMLNode*> nodes;
root->getNodes("characteristic", nodes);
bool found = false;
std::string name;
for (const XMLNode *baseNode : nodes)
{
baseNode->get("name", &name);
if (name == "base")
{
found = true;
m_base_characteristic.reset(new XmlCharacteristic(baseNode));
break;
}
}
if (!found)
Log::fatal("KartPropertiesManager", "Base characteristics not found");
// Load difficulties
nodes.clear();
root->getNode("difficulties")->getNodes("characteristic", nodes);
for (const XMLNode *type : nodes)
{
type->get("name", &name);
m_difficulty_characteristics.insert(std::pair<const std::string,
std::unique_ptr<AbstractCharacteristic> >(name,
std::unique_ptr<AbstractCharacteristic>(new XmlCharacteristic(type))));
}
// Load kart type characteristics
nodes.clear();
root->getNode("kart-types")->getNodes("characteristic", nodes);
for (const XMLNode *type : nodes)
{
type->get("name", &name);
m_kart_type_characteristics.insert(std::pair<const std::string,
std::unique_ptr<AbstractCharacteristic> >(name,
std::unique_ptr<AbstractCharacteristic>(new XmlCharacteristic(type))));
}
// Load player difficulties
nodes.clear();
root->getNode("player-characteristics")->getNodes("characteristic", nodes);
for (const XMLNode *type : nodes)
{
type->get("name", &name);
m_player_characteristics.insert(std::pair<const std::string,
std::unique_ptr<AbstractCharacteristic> >(name,
std::unique_ptr<AbstractCharacteristic>(new XmlCharacteristic(type))));
}
}
//-----------------------------------------------------------------------------
/** Loads a single kart and (if not disabled) the corresponding 3d model.
* \param filename Full path to the kart config file.
*/
bool KartPropertiesManager::loadKart(const std::string &dir)
{
std::string config_filename=dir+"/kart.xml";
std::string config_filename = dir + "/kart.xml";
if(!file_manager->fileExists(config_filename))
return false;
@ -191,7 +248,7 @@ bool KartPropertiesManager::loadKart(const std::string &dir)
}
catch (std::runtime_error& err)
{
Log::error("[Kart_Properties_Manager]","Giving up loading '%s': %s",
Log::error("[KartPropertiesManager]", "Giving up loading '%s': %s",
config_filename.c_str(), err.what());
return false;
}
@ -201,7 +258,7 @@ bool KartPropertiesManager::loadKart(const std::string &dir)
if (kart_properties->getVersion() < stk_config->m_min_kart_version ||
kart_properties->getVersion() > stk_config->m_max_kart_version)
{
Log::warn("[Kart_Properties_Manager]", "Warning: kart '%s' is not "
Log::warn("[KartPropertiesManager]", "Warning: kart '%s' is not "
"supported by this binary, ignored.",
kart_properties->getIdent().c_str());
delete kart_properties;
@ -221,7 +278,7 @@ bool KartPropertiesManager::loadKart(const std::string &dir)
}
m_all_kart_dirs.push_back(dir);
return true;
} // loadKartData
} // loadKart
//-----------------------------------------------------------------------------
/** Sets the name of a mesh to use as a hat for all karts.
@ -235,6 +292,36 @@ void KartPropertiesManager::setHatMeshName(const std::string &hat_name)
}
} // setHatMeshName
//-----------------------------------------------------------------------------
const AbstractCharacteristic* KartPropertiesManager::getDifficultyCharacteristic(const std::string &type) const
{
std::map<std::string, std::unique_ptr<AbstractCharacteristic> >::const_iterator
it = m_difficulty_characteristics.find(type);
if (it == m_difficulty_characteristics.cend())
return nullptr;
return it->second.get();
} // getDifficultyCharacteristic
//-----------------------------------------------------------------------------
const AbstractCharacteristic* KartPropertiesManager::getKartTypeCharacteristic(const std::string &type) const
{
std::map<std::string, std::unique_ptr<AbstractCharacteristic> >::const_iterator
it = m_kart_type_characteristics.find(type);
if (it == m_kart_type_characteristics.cend())
return nullptr;
return it->second.get();
} // getKartTypeCharacteristic
//-----------------------------------------------------------------------------
const AbstractCharacteristic* KartPropertiesManager::getPlayerCharacteristic(const std::string &type) const
{
std::map<std::string, std::unique_ptr<AbstractCharacteristic> >::const_iterator
it = m_player_characteristics.find(type);
if (it == m_player_characteristics.cend())
return nullptr;
return it->second.get();
} // getPlayerCharacteristic
//-----------------------------------------------------------------------------
/** Returns index of the kart properties with the given ident.
* \return Index of kart (between 0 and number of karts - 1).

View File

@ -22,13 +22,16 @@
#include "utils/ptr_vector.hpp"
#include <map>
#include <memory>
#include "network/remote_kart_info.hpp"
#include "utils/no_copy.hpp"
#define ALL_KART_GROUPS_ID "all"
class AbstractCharacteristic;
class KartProperties;
class XMLNode;
/**
* \ingroup karts
@ -58,6 +61,11 @@ private:
* all clients or not. */
std::vector<bool> m_kart_available;
std::unique_ptr<AbstractCharacteristic> m_base_characteristic;
std::map<std::string, std::unique_ptr<AbstractCharacteristic> > m_difficulty_characteristics;
std::map<std::string, std::unique_ptr<AbstractCharacteristic> > m_kart_type_characteristics;
std::map<std::string, std::unique_ptr<AbstractCharacteristic> > m_player_characteristics;
protected:
typedef PtrVector<KartProperties> KartPropertiesVector;
@ -74,6 +82,7 @@ public:
int getKartByGroup(const std::string& group,
int i) const;
void loadCharacteristics (const XMLNode *root);
bool loadKart (const std::string &dir);
void loadAllKarts (bool loading_icon = true);
void unloadAllKarts ();
@ -89,6 +98,18 @@ public:
std::vector<std::string> *ai_list);
void setHatMeshName(const std::string &hat_name);
// ------------------------------------------------------------------------
/** Get the characteristic that holds the base values. */
const AbstractCharacteristic* getBaseCharacteristic() const { return m_base_characteristic.get(); }
// ------------------------------------------------------------------------
/** Get a characteristic that holds the values for a certain difficulty. */
const AbstractCharacteristic* getDifficultyCharacteristic(const std::string &type) const;
// ------------------------------------------------------------------------
/** Get a characteristic that holds the values for a kart type. */
const AbstractCharacteristic* getKartTypeCharacteristic(const std::string &type) const;
// ------------------------------------------------------------------------
/** Get a characteristic that holds the values for a player difficulty. */
const AbstractCharacteristic* getPlayerCharacteristic(const std::string &type) const;
// ------------------------------------------------------------------------
/** Returns a list of all groups. */
const std::vector<std::string>& getAllGroups() const {return m_all_groups;}
// ------------------------------------------------------------------------

View File

@ -26,7 +26,7 @@
KartWithStats::KartWithStats(const std::string& ident,
unsigned int world_kart_id,
int position, const btTransform& init_transform,
const PlayerDifficulty *difficulty)
PerPlayerDifficulty difficulty)
: Kart(ident, world_kart_id, position,
init_transform, difficulty)
{

View File

@ -75,7 +75,7 @@ public:
unsigned int world_kart_id,
int position,
const btTransform& init_transform,
const PlayerDifficulty *difficulty);
PerPlayerDifficulty difficulty);
virtual void update(float dt);
virtual void reset();
virtual void collectedItem(Item *item, int add_info);

View File

@ -62,8 +62,7 @@ MaxSpeed::MaxSpeed(AbstractKart *kart)
*/
void MaxSpeed::reset()
{
m_current_max_speed = m_kart->getKartProperties()->getMaxSpeed() *
m_kart->getPlayerDifficulty()->getMaxSpeed();
m_current_max_speed = m_kart->getKartProperties()->getEngineMaxSpeed();
m_min_speed = -1.0f;
for(unsigned int i=MS_DECREASE_MIN; i<MS_DECREASE_MAX; i++)
@ -243,8 +242,7 @@ void MaxSpeed::update(float dt)
}
m_add_engine_force = 0;
m_current_max_speed = m_kart->getKartProperties()->getMaxSpeed() *
m_kart->getPlayerDifficulty()->getMaxSpeed();
m_current_max_speed = m_kart->getKartProperties()->getEngineMaxSpeed();
// Then add the speed increase from each category
// ----------------------------------------------

View File

@ -1,199 +0,0 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "karts/player_difficulty.hpp"
#include "config/stk_config.hpp"
#include "io/xml_node.hpp"
#include "karts/skidding_properties.hpp"
#include "race/race_manager.hpp"
#include "modes/world.hpp"
#include "utils/log.hpp"
/**
* The constructor initialises all values with default values.
*/
PlayerDifficulty::PlayerDifficulty(const std::string &filename)
{
// Set all other values to undefined, so that it can later be tested
// if everything is defined properly.
m_mass = m_brake_factor = m_brake_time_increase = m_rescue_time =
m_explosion_time = m_explosion_invulnerability_time = m_zipper_time =
m_zipper_fade_out_time = m_zipper_force = m_zipper_speed_gain =
m_zipper_max_speed_increase = m_rubber_band_max_length =
m_rubber_band_force = m_rubber_band_duration =
m_rubber_band_speed_increase = m_rubber_band_fade_out_time =
m_nitro_consumption = m_nitro_max_speed_increase =
m_nitro_engine_force = m_nitro_duration = m_nitro_fade_out_time =
m_bubblegum_time = m_bubblegum_torque = m_bubblegum_speed_fraction =
m_bubblegum_fade_in_time = m_swatter_duration = m_squash_duration =
m_squash_slowdown = m_max_speed_reverse_ratio = m_slipstream_length =
m_slipstream_width = m_slipstream_collect_time =
m_slipstream_use_time = m_slipstream_add_power =
m_slipstream_min_speed = m_slipstream_max_speed_increase =
m_slipstream_duration = m_slipstream_fade_out_time = 1;
m_startup_times.resize(RaceManager::DIFFICULTY_COUNT, 1);
m_startup_boost.resize(RaceManager::DIFFICULTY_COUNT, 1);
// The default constructor for stk_config uses filename=""
if (filename != "")
load(filename, "normal");
} // PlayerDifficulty
//-----------------------------------------------------------------------------
/** Destructor, dereferences the kart model. */
PlayerDifficulty::~PlayerDifficulty()
{
} // ~PlayerDifficulty
//-----------------------------------------------------------------------------
/** */
std::string PlayerDifficulty::getIdent() const
{
switch(m_difficulty)
{
case PLAYER_DIFFICULTY_NORMAL: return "normal"; break;
case PLAYER_DIFFICULTY_HANDICAP: return "handicap"; break;
default: assert(false);
}
return "";
}
//-----------------------------------------------------------------------------
/** Loads the difficulty properties from a file.
* \param filename Filename to load.
* \param node Name of the xml node to load the data from
*/
void PlayerDifficulty::load(const std::string &filename, const std::string &node)
{
const XMLNode* root = new XMLNode(filename);
getAllData(root->getNode(node));
if(root)
delete root;
} // load
//-----------------------------------------------------------------------------
/** Actually reads in the data from the xml file.
* \param root Root of the xml tree.
*/
void PlayerDifficulty::getAllData(const XMLNode * root)
{
if(const XMLNode *mass_node = root->getNode("mass"))
mass_node->get("value", &m_mass);
if(const XMLNode *engine_node = root->getNode("engine"))
{
engine_node->get("brake-factor", &m_brake_factor);
engine_node->get("brake-time-increase", &m_brake_time_increase);
engine_node->get("max-speed-reverse-ratio", &m_max_speed_reverse_ratio);
engine_node->get("power", &m_engine_power);
engine_node->get("max-speed", &m_max_speed);
}
if(const XMLNode *nitro_node = root->getNode("nitro"))
{
nitro_node->get("consumption", &m_nitro_consumption );
nitro_node->get("max-speed-increase", &m_nitro_max_speed_increase);
nitro_node->get("engine-force", &m_nitro_engine_force );
nitro_node->get("duration", &m_nitro_duration );
nitro_node->get("fade-out-time", &m_nitro_fade_out_time );
}
if(const XMLNode *bubble_node = root->getNode("bubblegum"))
{
bubble_node->get("time", &m_bubblegum_time );
bubble_node->get("speed-fraction", &m_bubblegum_speed_fraction);
bubble_node->get("torque", &m_bubblegum_torque );
bubble_node->get("fade-in-time", &m_bubblegum_fade_in_time );
}
if(const XMLNode *rescue_node = root->getNode("rescue"))
rescue_node->get("time", &m_rescue_time);
if(const XMLNode *explosion_node = root->getNode("explosion"))
{
explosion_node->get("time", &m_explosion_time);
explosion_node->get("invulnerability-time",
&m_explosion_invulnerability_time);
}
if(const XMLNode *slipstream_node = root->getNode("slipstream"))
{
slipstream_node->get("length", &m_slipstream_length );
slipstream_node->get("width", &m_slipstream_width );
slipstream_node->get("collect-time", &m_slipstream_collect_time );
slipstream_node->get("use-time", &m_slipstream_use_time );
slipstream_node->get("add-power", &m_slipstream_add_power );
slipstream_node->get("min-speed", &m_slipstream_min_speed );
slipstream_node->get("max-speed-increase",
&m_slipstream_max_speed_increase);
slipstream_node->get("duration", &m_slipstream_duration );
slipstream_node->get("fade-out-time", &m_slipstream_fade_out_time );
}
if(const XMLNode *plunger_node= root->getNode("plunger"))
{
plunger_node->get("band-max-length", &m_rubber_band_max_length );
plunger_node->get("band-force", &m_rubber_band_force );
plunger_node->get("band-duration", &m_rubber_band_duration );
plunger_node->get("band-speed-increase",&m_rubber_band_speed_increase);
plunger_node->get("band-fade-out-time", &m_rubber_band_fade_out_time );
plunger_node->get("in-face-time", &m_plunger_in_face_duration);
}
if(const XMLNode *zipper_node= root->getNode("zipper"))
{
zipper_node->get("time", &m_zipper_time );
zipper_node->get("fade-out-time", &m_zipper_fade_out_time );
zipper_node->get("force", &m_zipper_force );
zipper_node->get("speed-gain", &m_zipper_speed_gain );
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("squash-duration", &m_squash_duration );
swatter_node->get("squash-slowdown", &m_squash_slowdown );
}
if(const XMLNode *startup_node= root->getNode("startup"))
{
startup_node->get("time", &m_startup_times);
startup_node->get("boost", &m_startup_boost);
}
} // getAllData
// ----------------------------------------------------------------------------
/** Called the first time a kart accelerates after 'ready-set-go'. It searches
* through m_startup_times to find the appropriate slot, and returns the
* speed-boost from the corresponding entry in m_startup_boost.
* If the kart started too slow (i.e. slower than the longest time in
* m_startup_times, it returns 0.
*/
float PlayerDifficulty::getStartupBoost() const
{
float t = World::getWorld()->getTime();
for(unsigned int i=0; i<m_startup_times.size(); i++)
{
if(t<=m_startup_times[i]) return m_startup_boost[i];
}
return 0;
} // getStartupBoost

View File

@ -1,345 +0,0 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2006-2015 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef HEADER_PLAYER_DIFFICULTY_HPP
#define HEADER_PLAYER_DIFFICULTY_HPP
#include "network/remote_kart_info.hpp"
#include <string>
#include <vector>
class XMLNode;
/**
* \brief This class stores values that modify the properties of a kart.
* This includes physical properties like speed and the effect of items.
* The values stored in this class get multiplied with the current
* properties of the kart. If all values here are set to 1, nothing changes.
*
* \ingroup karts
*/
class PlayerDifficulty
{
private:
/** Actual difficulty */
PerPlayerDifficulty m_difficulty;
// -----------------
/** Weight of kart. */
float m_mass;
/** Maximum force from engine for each difficulty. */
float m_engine_power;
/** Braking factor * engine_power braking force. */
float m_brake_factor;
/** Brake_time * m_brake_time_increase will increase the break time
* over time. */
float m_brake_time_increase;
/** Time a kart is moved upwards after when it is rescued. */
float m_rescue_time;
/** Time an animated explosion is shown. Longer = more delay for kart. */
float m_explosion_time;
/** How long a kart is invulnerable after it is hit by an explosion. */
float m_explosion_invulnerability_time;
/** Duration a zipper is active. */
float m_zipper_time;
/** Fade out time for a zipper. */
float m_zipper_fade_out_time;
/** Additional force added to the acceleration. */
float m_zipper_force;
/** Initial one time speed gain. */
float m_zipper_speed_gain;
/** Absolute increase of the kart's maximum speed (in m/s). */
float m_zipper_max_speed_increase;
/** Max. length of plunger rubber band. */
float m_rubber_band_max_length;
/** Force of an attached rubber band. */
/** Duration a rubber band works. */
float m_rubber_band_force;
/** How long the rubber band will fly. */
float m_rubber_band_duration;
/** Increase of maximum speed of the kart when the rubber band pulls. */
float m_rubber_band_speed_increase;
/** Fade out time when the rubber band is removed. */
float m_rubber_band_fade_out_time;
/**Duration of plunger in face depending on difficulty. */
float m_plunger_in_face_duration;
/** Nitro consumption. */
float m_nitro_consumption;
/* How much the speed of a kart might exceed its maximum speed (in m/s). */
float m_nitro_max_speed_increase;
/** Additional engine force to affect the kart. */
float m_nitro_engine_force;
/** How long the increased nitro max speed will be valid after
* the kart stops using nitro (and the fade-out-time starts). */
float m_nitro_duration;
/** Duration during which the increased maximum speed
* due to nitro fades out. */
float m_nitro_fade_out_time;
/** Bubble gum diration. */
float m_bubblegum_time;
/** Torque to add when a bubble gum was hit in order to make the kart go
* sideways a bit. */
float m_bubblegum_torque;
/** Fraction of top speed that can be reached maximum after hitting a
* bubble gum. */
float m_bubblegum_speed_fraction;
/** How long to fade in the slowdown for a bubble gum. */
float m_bubblegum_fade_in_time;
/** 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;
/** The maximum speed at each difficulty. */
float m_max_speed;
float m_max_speed_reverse_ratio;
/** How far behind a kart slipstreaming is effective. */
float m_slipstream_length;
/** How wide the slipstream area is at the end. */
float m_slipstream_width;
/** Time after which sstream gives a bonus. */
float m_slipstream_collect_time;
/** Time slip-stream bonus is effective. */
float m_slipstream_use_time;
/** Additional power due to sstreaming. */
float m_slipstream_add_power;
/** Minimum speed for slipstream to take effect. */
float m_slipstream_min_speed;
/** How much the speed of the kart might exceed its
* normal maximum speed. */
float m_slipstream_max_speed_increase;
/** How long the higher speed lasts after slipstream stopped working. */
float m_slipstream_duration;
/** How long the slip stream speed increase will gradually be reduced. */
float m_slipstream_fade_out_time;
/** If the kart starts within the specified time at index I after 'go',
* it receives the speed boost from m_startup_boost[I]. */
std::vector<float> m_startup_times;
/** The startup boost is the kart starts fast enough. */
std::vector<float> m_startup_boost;
void load (const std::string &filename,
const std::string &node);
public:
PlayerDifficulty (const std::string &filename="");
~PlayerDifficulty ();
void getAllData (const XMLNode * root);
std::string getIdent() const;
float getStartupBoost () const;
// ------------------------------------------------------------------------
/** Returns the maximum engine power depending on difficulty. */
float getMaxPower () const {return m_engine_power; }
// ------------------------------------------------------------------------
/** Get braking information. */
float getBrakeFactor () const {return m_brake_factor; }
// ------------------------------------------------------------------------
/** Returns the additional brake factor which depends on time. */
float getBrakeTimeIncrease() const { return m_brake_time_increase; }
// ------------------------------------------------------------------------
/** Get maximum reverse speed ratio. */
float getMaxSpeedReverseRatio () const
{return m_max_speed_reverse_ratio; }
// ------------------------------------------------------------------------
/** Returns the maximum speed dependent on the difficult level. */
float getMaxSpeed () const { return m_max_speed; }
// ------------------------------------------------------------------------
/** Returns the nitro consumption. */
float getNitroConsumption () const {return m_nitro_consumption; }
// ------------------------------------------------------------------------
/** Returns the increase of maximum speed due to nitro. */
float getNitroMaxSpeedIncrease () const
{return m_nitro_max_speed_increase; }
// ------------------------------------------------------------------------
float getNitroEngineForce () const {return m_nitro_engine_force; }
// ------------------------------------------------------------------------
/** Returns how long the increased nitro max speed will be valid after
* the kart stops using nitro (and the fade-out-time starts). */
float getNitroDuration () const {return m_nitro_duration; }
// ------------------------------------------------------------------------
/** Returns the duration during which the increased maximum speed
* due to nitro fades out. */
float getNitroFadeOutTime () const {return m_nitro_fade_out_time; }
// ------------------------------------------------------------------------
/** Returns how long a bubble gum is active. */
float getBubblegumTime() const { return m_bubblegum_time; }
// ------------------------------------------------------------------------
/** Returns the torque to add when a bubble gum was hit . */
float getBubblegumTorque() const { return m_bubblegum_torque; }
// ------------------------------------------------------------------------
/** Returns the fraction of top speed that can be reached maximum after
* hitting a bubble gum. */
float getBubblegumSpeedFraction() const {return m_bubblegum_speed_fraction;}
// ------------------------------------------------------------------------
/** Returns how long to fade in the slowdown for a bubble gum. */
float getBubblegumFadeInTime() const { return m_bubblegum_fade_in_time; }
// ------------------------------------------------------------------------
/** Returns the time a kart is rised during a rescue. */
float getRescueTime () const {return m_rescue_time; }
// ------------------------------------------------------------------------
/** Returns the time an explosion animation is shown. */
float getExplosionTime () const {return m_explosion_time; }
// ------------------------------------------------------------------------
/** Returns how long a kart is invulnerable after being hit by an
explosion. */
float getExplosionInvulnerabilityTime() const
{ return m_explosion_invulnerability_time; }
// ------------------------------------------------------------------------
/** Returns the maximum length of a rubber band before it breaks. */
float getRubberBandMaxLength () const {return m_rubber_band_max_length;}
// ------------------------------------------------------------------------
/** Returns force a rubber band has when attached to a kart. */
float getRubberBandForce () const {return m_rubber_band_force; }
// ------------------------------------------------------------------------
/** Returns the duration a rubber band is active for. */
float getRubberBandDuration () const {return m_rubber_band_duration; }
// ------------------------------------------------------------------------
/** Returns the increase of maximum speed while a rubber band is
* pulling. */
float getRubberBandSpeedIncrease() const
{
return m_rubber_band_speed_increase;
}
// ------------------------------------------------------------------------
/** 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;}
// ------------------------------------------------------------------------
/** 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 how far behind a kart slipstreaming works. */
float getSlipstreamLength () const {return m_slipstream_length; }
// ------------------------------------------------------------------------
/** Returns how wide the slipstream area is at the end. */
float getSlipstreamWidth () const {return m_slipstream_width; }
// ------------------------------------------------------------------------
/** Returns time after which slipstream has maximum effect. */
float getSlipstreamCollectTime () const
{return m_slipstream_collect_time; }
// ------------------------------------------------------------------------
/** Returns time after which slipstream has maximum effect. */
float getSlipstreamUseTime () const {return m_slipstream_use_time; }
// ------------------------------------------------------------------------
/** Returns additional power due to slipstreaming. */
float getSlipstreamAddPower () const {return m_slipstream_add_power; }
// ------------------------------------------------------------------------
/** Returns the minimum slipstream speed. */
float getSlipstreamMinSpeed () const {return m_slipstream_min_speed; }
// ------------------------------------------------------------------------
/** Returns the increase of the maximum speed of a kart
* due to slipstream. */
float getSlipstreamMaxSpeedIncrease() const
{ return m_slipstream_max_speed_increase; }
// ------------------------------------------------------------------------
/** Returns how long the higher speed lasts after slipstream
* stopped working. */
float getSlipstreamDuration () const { return m_slipstream_duration; }
// ------------------------------------------------------------------------
/** Returns how long the slip stream speed increase will gradually
* be reduced. */
float getSlipstreamFadeOutTime () const
{ return m_slipstream_fade_out_time; }
// ------------------------------------------------------------------------
/** 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; }
}; // KartProperties
#endif

View File

@ -39,8 +39,7 @@ RescueAnimation::RescueAnimation(AbstractKart *kart, bool is_auto_rescue)
{
m_referee = new Referee(*m_kart);
m_kart->getNode()->addChild(m_referee->getSceneNode());
m_timer = m_kart->getKartProperties()->getRescueTime() *
m_kart->getPlayerDifficulty()->getRescueTime();
m_timer = m_kart->getKartProperties()->getRescueDuration();
m_velocity = m_kart->getKartProperties()->getRescueHeight() / m_timer;
m_xyz = m_kart->getXYZ();

View File

@ -36,7 +36,7 @@
/** Constructor of the skidding object.
*/
Skidding::Skidding(Kart *kart, const SkiddingProperties *sp)
Skidding::Skidding(Kart *kart)
{
#ifdef SKID_DEBUG
m_predicted_curve = new ShowCurve(0.05f, 0.05f,
@ -47,8 +47,8 @@ Skidding::Skidding(Kart *kart, const SkiddingProperties *sp)
m_actual_curve->setVisible(false);
#endif
m_kart = kart;
copyFrom(sp);
m_skid_reduce_turn_delta = m_skid_reduce_turn_max - m_skid_reduce_turn_min;
m_skid_reduce_turn_delta = m_kart->getKartProperties()->getSkidReduceTurnMax()
- m_kart->getKartProperties()->getSkidReduceTurnMin();
reset();
} // Skidding
@ -95,13 +95,15 @@ void Skidding::reset()
*/
void Skidding::updateSteering(float steer, float dt)
{
const KartProperties *kp = m_kart->getKartProperties();
switch(m_skid_state)
{
case SKID_SHOW_GFX_LEFT:
case SKID_SHOW_GFX_RIGHT:
case SKID_NONE:
m_real_steering = steer;
if(m_skid_time<m_skid_visual_time && m_skid_time>0)
if (m_skid_time < kp->getSkidVisualTime() && m_skid_time > 0)
{
float f = m_visual_rotation - m_visual_rotation*dt/m_skid_time;
// Floating point errors when m_skid_time is very close to 0
@ -125,25 +127,27 @@ void Skidding::updateSteering(float steer, float dt)
case SKID_ACCUMULATE_RIGHT:
{
float f = (1.0f+steer)*0.5f; // map [-1,1] --> [0, 1]
m_real_steering = m_skid_reduce_turn_min+
m_skid_reduce_turn_delta*f;
if(m_skid_time < m_skid_visual_time)
m_visual_rotation = m_skid_visual*m_real_steering*m_skid_time
/ m_skid_visual_time;
m_real_steering = kp->getSkidReduceTurnMin()
+ m_skid_reduce_turn_delta * f;
if(m_skid_time < kp->getSkidVisualTime())
m_visual_rotation = kp->getSkidVisual()
* m_real_steering * m_skid_time
/ kp->getSkidVisualTime();
else
m_visual_rotation = m_skid_visual * m_real_steering;
m_visual_rotation = kp->getSkidVisual() * m_real_steering;
break;
}
case SKID_ACCUMULATE_LEFT:
{
float f = (-1.0f+steer)*0.5f; // map [-1,1] --> [-1, 0]
m_real_steering = -m_skid_reduce_turn_min+
m_skid_reduce_turn_delta*f;
if(m_skid_time < m_skid_visual_time)
m_visual_rotation = m_skid_visual*m_real_steering*m_skid_time
/ m_skid_visual_time;
m_real_steering = -kp->getSkidReduceTurnMin()
+ m_skid_reduce_turn_delta * f;
if(m_skid_time < kp->getSkidVisualTime())
m_visual_rotation = kp->getSkidVisual()
* m_real_steering * m_skid_time
/ kp->getSkidVisualTime();
else
m_visual_rotation = m_skid_visual * m_real_steering;
m_visual_rotation = kp->getSkidVisual() * m_real_steering;
break;
}
@ -173,13 +177,13 @@ float Skidding::getSteeringWhenSkidding(float steering) const
break;
case SKID_ACCUMULATE_RIGHT:
{
float f = (steering - m_skid_reduce_turn_min)
float f = (steering - m_kart->getKartProperties()->getSkidReduceTurnMin())
/ m_skid_reduce_turn_delta;
return f *2.0f-1.0f;
}
case SKID_ACCUMULATE_LEFT:
{
float f = (steering + m_skid_reduce_turn_min)
float f = (steering + m_kart->getKartProperties()->getSkidReduceTurnMin())
/ m_skid_reduce_turn_delta;
return 2.0f * f +1.0f;
}
@ -198,6 +202,8 @@ float Skidding::getSteeringWhenSkidding(float steering) const
void Skidding::update(float dt, bool is_on_ground,
float steering, KartControl::SkidControl skidding)
{
const KartProperties *kp = m_kart->getKartProperties();
// If a kart animation is shown, stop all skidding bonuses.
if(m_kart->getKartAnimation())
{
@ -212,7 +218,7 @@ void Skidding::update(float dt, bool is_on_ground,
#endif
// No skidding backwards or while stopped
if(m_kart->getSpeed() < m_min_skid_speed &&
if(m_kart->getSpeed() < kp->getSkidMinSpeed() &&
m_skid_state != SKID_NONE && m_skid_state != SKID_BREAK)
{
m_skid_state = SKID_BREAK;
@ -223,15 +229,16 @@ void Skidding::update(float dt, bool is_on_ground,
m_skid_bonus_ready = false;
if (is_on_ground)
{
if((fabs(steering) > 0.001f) &&
m_kart->getSpeed()>m_min_skid_speed &&
(skidding==KartControl::SC_LEFT||skidding==KartControl::SC_RIGHT))
if ((fabs(steering) > 0.001f) &&
m_kart->getSpeed() > kp->getSkidMinSpeed() &&
(skidding == KartControl::SC_LEFT || skidding == KartControl::SC_RIGHT))
{
m_skid_factor += m_skid_increase *dt/m_time_till_max_skid;
m_skid_factor += kp->getSkidIncrease()
* dt / kp->getSkidTimeTillMax();
}
else if(m_skid_factor>1.0f)
else if (m_skid_factor > 1.0f)
{
m_skid_factor *= m_skid_decrease;
m_skid_factor *= kp->getSkidDecrease();
}
}
else
@ -239,10 +246,10 @@ void Skidding::update(float dt, bool is_on_ground,
m_skid_factor = 1.0f; // Lose any skid factor as soon as we fly
}
if(m_skid_factor>m_skid_max)
m_skid_factor = m_skid_max;
if (m_skid_factor > kp->getSkidMax())
m_skid_factor = kp->getSkidMax();
else
if(m_skid_factor<1.0f) m_skid_factor = 1.0f;
if (m_skid_factor < 1.0f) m_skid_factor = 1.0f;
// If skidding was started and a graphical jump should still
// be displayed, update the data
@ -288,8 +295,9 @@ void Skidding::update(float dt, bool is_on_ground,
break;
// Don't allow skidding while the kart is (apparently)
// still in the air, or when the kart is too slow
if(m_remaining_jump_time>0 ||
m_kart->getSpeed() <m_min_skid_speed) break;
if (m_remaining_jump_time > 0 ||
m_kart->getSpeed() < kp->getSkidMinSpeed())
break;
m_skid_state = skidding==KartControl::SC_RIGHT
? SKID_ACCUMULATE_RIGHT
@ -300,14 +308,14 @@ void Skidding::update(float dt, bool is_on_ground,
// Then use this speed to determine the impulse necessary to
// reach this speed.
float v = World::getWorld()->getTrack()->getGravity()
* 0.5f*m_physical_jump_time;
* 0.5f * kp->getSkidPhysicalJumpTime();
btVector3 imp(0, v / m_kart->getBody()->getInvMass(),0);
m_kart->getVehicle()->getRigidBody()->applyCentralImpulse(imp);
// Some karts might use a graphical-only jump. Set it up:
m_jump_speed = World::getWorld()->getTrack()->getGravity()
* 0.5f*m_graphical_jump_time;
m_remaining_jump_time = m_graphical_jump_time;
* 0.5f * kp->getSkidGraphicalJumpTime();
m_remaining_jump_time = kp->getSkidGraphicalJumpTime();
#ifdef SKID_DEBUG
#define SPEED 20.0f
@ -318,13 +326,13 @@ void Skidding::update(float dt, bool is_on_ground,
m_predicted_curve->setVisible(true);
m_predicted_curve->setPosition(m_kart->getXYZ());
m_predicted_curve->setHeading(m_kart->getHeading());
float angle = m_kart->getKartProperties()
float angle = kp
->getMaxSteerAngle(m_kart->getSpeed())
* fabsf(getSteeringFraction());
angle = m_kart->getKartProperties()
angle = kp
->getMaxSteerAngle(SPEED)
* fabsf(getSteeringFraction());
float r = m_kart->getKartProperties()->getWheelBase()
float r = kp->getWheelBase()
/ asin(angle)*1.0f;
const int num_steps = 50;
@ -335,7 +343,7 @@ void Skidding::update(float dt, bool is_on_ground,
{
float real_x = m_skid_state==SKID_ACCUMULATE_LEFT ? -x : x;
Vec3 xyz(real_x, 0.2f, sqrt(r*r-(r-x)*(r-x))*(1.0f+SPEED/150.0f)
*(1+(angle/m_kart->getKartProperties()->getMaxSteerAngle(SPEED)-0.6f)*0.1f));
*(1+(angle/kp->getMaxSteerAngle(SPEED)-0.6f)*0.1f));
Vec3 xyz1=m_kart->getTrans()(xyz);
Log::debug("Skidding", "predict %f %f %f speed %f angle %f",
xyz1.getX(), xyz1.getY(), xyz1.getZ(),
@ -368,7 +376,7 @@ void Skidding::update(float dt, bool is_on_ground,
Log::debug("Skidding", "actual %f %f %f turn %f speed %f angle %f",
m_kart->getXYZ().getX(),m_kart->getXYZ().getY(),m_kart->getXYZ().getZ(),
m_real_steering, m_kart->getSpeed(),
m_kart->getKartProperties()->getMaxSteerAngle(m_kart->getSpeed()));
kp->getMaxSteerAngle(m_kart->getSpeed()));
#endif
m_skid_time += dt;
float bonus_time, bonus_speed, bonus_force;
@ -388,11 +396,11 @@ void Skidding::update(float dt, bool is_on_ground,
m_skid_state = m_skid_state == SKID_ACCUMULATE_LEFT
? SKID_SHOW_GFX_LEFT
: SKID_SHOW_GFX_RIGHT;
float t = std::min(m_skid_time, m_skid_visual_time);
t = std::min(t, m_skid_revert_visual_time);
float t = std::min(m_skid_time, kp->getSkidVisualTime());
t = std::min(t, kp->getSkidRevertVisualTime());
float vso = getVisualSkidRotation();
btVector3 rot(0, vso*m_post_skid_rotate_factor, 0);
btVector3 rot(0, vso * kp->getSkidPostSkidRotateFactor(), 0);
m_kart->getVehicle()->setTimedRotation(t, rot);
// skid_time is used to count backwards for the GFX
m_skid_time = t;
@ -452,16 +460,19 @@ unsigned int Skidding::getSkidBonus(float *bonus_time,
float *bonus_speed,
float *bonus_force) const
{
const KartProperties *kp = m_kart->getKartProperties();
*bonus_time = 0;
*bonus_speed = 0;
*bonus_force = 0;
for(unsigned int i=0; i<m_skid_bonus_speed.size(); i++)
for (unsigned int i = 0; i < kp->getSkidBonusSpeed().size(); i++)
{
if(m_skid_time<=m_skid_time_till_bonus[i]) return i;
*bonus_speed = m_skid_bonus_speed[i];
*bonus_time = m_skid_bonus_time[i];
*bonus_force = m_skid_bonus_force[i];
if (m_skid_time <= kp->getSkidTimeTillBonus()[i])
return i;
*bonus_speed = kp->getSkidBonusSpeed()[i];
*bonus_time = kp->getSkidBonusTime()[i];
*bonus_force = kp->getSkidBonusForce()[i];
}
return (unsigned int) m_skid_bonus_speed.size();
return (unsigned int) kp->getSkidBonusSpeed().size();
} // getSkidBonusForce

View File

@ -19,7 +19,6 @@
#ifndef HEADER_SKIDDING_HPP
#define HEADER_SKIDDING_HPP
#include "karts/skidding_properties.hpp"
#include "karts/controller/kart_control.hpp"
#include "utils/leak_check.hpp"
#include "utils/no_copy.hpp"
@ -35,7 +34,7 @@ class ShowCurve;
#undef SKID_DEBUG
class Skidding : public SkiddingProperties
class Skidding
{
public:
LEAK_CHECK();
@ -101,7 +100,7 @@ private:
float *bonus_force) const;
void updateSteering(float steer, float dt);
public:
Skidding(Kart *kart, const SkiddingProperties *sp);
Skidding(Kart *kart);
~Skidding();
void reset();
void update(float dt, bool is_on_ground, float steer,

Some files were not shown because too many files have changed in this diff Show More