/
IPathController.cpp
119 lines (95 loc) · 4.28 KB
/
IPathController.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
#include "IPathController.hpp"
#include "Sim/Units/Unit.h"
#include "System/myMath.h"
IPathController* IPathController::GetInstance(CUnit* owner) {
return (new GMTDefaultPathController(owner));
}
void IPathController::FreeInstance(IPathController* instance) {
delete instance;
}
float GMTDefaultPathController::GetDeltaSpeed(
unsigned int pathID,
float targetSpeed, // unsigned
float currentSpeed, // unsigned
float maxAccRate, // unsigned
float maxDecRate, // unsigned
bool wantReverse,
bool isReversing
) const {
// Sign(0) is negative which we do not want
const int targetSpeedSign = Sign(int(!wantReverse) * 2 - 1);
const int currentSpeedSign = Sign(int(!isReversing) * 2 - 1);
// if reversing, target and current are swapped
const float speeds[] = {targetSpeed * targetSpeedSign, currentSpeed * currentSpeedSign};
// all possible cases
// tar=[6,+1] && cur=[4,+1] --> dif=+2, delta= accRate*+1 (fwd-acc from +4 to +6)
// tar=[4,+1] && cur=[6,+1] --> dif=-2, delta=-decRate*+1 (fwd-dec from +6 to +4)
// tar=[6,-1] && cur=[4,-1] --> dif=+2, delta= accRate*-1 (rev-acc from -4 to -6)
// tar=[4,-1] && cur=[6,-1] --> dif=-2, delta=-decRate*-1 (rev-dec from -6 to -4)
// tar=[4,-1] && cur=[4,+1] --> dif=-8, delta=-decRate*+1 (fwd-dec from +5 to 0, rev-acc from 0 to -5)
// tar=[4,+1] && cur=[4,-1] --> dif=-8, delta=-decRate*-1 (rev-dec from -5 to 0, fwd-acc from 0 to +5)
//
const float rawSpeedDiff = speeds[isReversing] - speeds[1 - isReversing];
const float absSpeedDiff = std::max(rawSpeedDiff, -rawSpeedDiff);
// need to clamp, rates can be much larger than |speedDiff|
const float modAccRate = std::min(absSpeedDiff, maxAccRate);
const float modDecRate = std::min(absSpeedDiff, maxDecRate);
const float deltaSpeed = mix(modAccRate, -modDecRate, (rawSpeedDiff < 0.0f));
// no acceleration changes if not on ground
//
// note: not 100% correct, depends on MoveDef::speedModClass
// ships should always use (1 - IsInWater()), hovercraft
// should use (1 - IsInWater()) but only when over water,
// tanks should also test if IsInWater() && !IsOnGround()
return ((deltaSpeed * currentSpeedSign) * (1 - owner->IsInAir()));
}
#if 1
short GMTDefaultPathController::GetDeltaHeading(
unsigned int pathID,
short newHeading,
short oldHeading,
float maxTurnRate
) const {
short deltaHeading = newHeading - oldHeading;
if (deltaHeading > 0) {
deltaHeading = std::min(deltaHeading, short( maxTurnRate));
} else {
deltaHeading = std::max(deltaHeading, short(-maxTurnRate));
}
// no orientation changes if not on ground
return (deltaHeading * (1 - owner->IsInAir()));
}
#endif
static float TurnAccelerationSign(float turnBrakeDist, short curDeltaHeading, short newDeltaHeading) {
const bool b0 = (turnBrakeDist >= std::abs(curDeltaHeading));
const bool b1 = (std::abs(newDeltaHeading) <= std::abs(curDeltaHeading));
const bool b2 = (Sign(curDeltaHeading) != Sign(newDeltaHeading));
return (mix(1.0f, -1.0f, b0 && (b1 || b2)));
}
short GMTDefaultPathController::GetDeltaHeading(
unsigned int pathID,
short newHeading,
short oldHeading,
float maxTurnSpeed,
float maxTurnAccel,
float turnBrakeDist,
float* curTurnSpeed
) const {
// negative --> RH turn, positive --> LH turn
// add lookahead term to avoid micro-overshoots
const short curDeltaHeading = newHeading - short(oldHeading + (*curTurnSpeed) * (maxTurnAccel / maxTurnSpeed));
const float minTurnAccel = std::min(float(std::abs(curDeltaHeading)), maxTurnAccel);
const float rawTurnAccel = Clamp(Sign(curDeltaHeading) * maxTurnAccel, -minTurnAccel, minTurnAccel);
const float newTurnSpeed = Clamp((*curTurnSpeed) + rawTurnAccel * (1 - owner->IsInAir()), -maxTurnSpeed, maxTurnSpeed);
// predict the new angular difference
const short newDeltaHeading = newHeading - short(oldHeading + newTurnSpeed);
// flip acceleration sign when overshooting
const float modTurnAccel = rawTurnAccel * TurnAccelerationSign(turnBrakeDist, curDeltaHeading, newDeltaHeading);
(*curTurnSpeed) += (modTurnAccel * (1 - owner->IsInAir()));
(*curTurnSpeed) = Clamp((*curTurnSpeed) * 0.99f, -maxTurnSpeed, maxTurnSpeed);
return (*curTurnSpeed);
}
bool GMTDefaultPathController::IgnoreTerrain(const MoveDef& md, const float3& pos) const {
return (owner->IsInAir());
}