1 ////////////////////////////////////////////////////////////////////////////////
2 //    Scorched3D (c) 2000-2011
3 //
4 //    This file is part of Scorched3D.
5 //
6 //    Scorched3D is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Scorched3D is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License along
17 //    with this program; if not, write to the Free Software Foundation, Inc.,
18 //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include <movement/Boid2.h>
22 #include <movement/TargetMovementEntryBoids.h>
23 #include <common/OptionsScorched.h>
24 #include <landscapemap/LandscapeMaps.h>
25 #include <engine/ScorchedContext.h>
26 #include <engine/Simulator.h>
27 #include <target/Target.h>
28 #include <target/TargetLife.h>
29 
Boid2(ScorchedContext & context,Target * target,TargetMovementEntryBoids * world)30 Boid2::Boid2(ScorchedContext &context, Target *target, TargetMovementEntryBoids *world) :
31 	context_(context), target_(target), world_(world)
32 {
33 }
34 
~Boid2()35 Boid2::~Boid2()
36 {
37 }
38 
getPosition()39 FixedVector &Boid2::getPosition()
40 {
41 	static FixedVector result;
42 	if (target_) return target_->getLife().getTargetPosition();
43 	return result;
44 }
45 
getVelocity()46 FixedVector &Boid2::getVelocity()
47 {
48 	static FixedVector result;
49 	if (target_) return target_->getLife().getVelocity();
50 	return result;
51 }
52 
clearTarget()53 void Boid2::clearTarget()
54 {
55 	target_ = 0;
56 }
57 
update(fixed frameTime,std::vector<Boid2 * > & boidSet,bool complexUpdate)58 void Boid2::update(fixed frameTime, std::vector<Boid2*> &boidSet, bool complexUpdate)
59 {
60 	// Limit Velocity
61 	directionMag_ = getVelocity().Magnitude();
62 	direction_ = getVelocity() / directionMag_;
63 	if (directionMag_ > world_->getMaxVelocity())
64 	{
65 		getVelocity() = direction_ * world_->getMaxVelocity();
66 		directionMag_ = world_->getMaxVelocity();
67 	}
68 
69 	// Update position and rotation
70 	if (target_)
71 	{
72 		fixed yaw = atan2x(getVelocity()[0], getVelocity()[1]);
73 		fixed newRotation = -fixed(yaw) / fixed::XPI * 180;
74 
75 		FixedVector newPosition = getPosition() + getVelocity();
76 		target_->getLife().setTargetPositionAndRotation(newPosition, newRotation);
77 	}
78 
79 	// Calculate new velocity
80 	FixedVector collisionNormal;
81 	FixedVector newVelocity;
82 	if (checkCollision(collisionNormal))
83 	{
84 		// Collision
85 		newVelocity = collisionNormal / 10;
86 	}
87 	else
88 	{
89 		if (complexUpdate)
90 		{
91 			newVelocity = checkGrouping(boidSet);
92 		}
93 
94 		// Wander
95 		{
96 			fixed cruiseDiff = world_->getCruiseVelocity() - directionMag_;
97 			fixed cruiseChange = cruiseDiff / 10;
98 			newVelocity += direction_ * cruiseDiff;
99 		}
100 
101 		// Level out the flight
102 		{
103 			newVelocity[2] -= getVelocity()[2] / 100;
104 		}
105 	}
106 
107 	// Limit acceleration
108 	fixed velocityMag = newVelocity.Magnitude();
109 	if (velocityMag > world_->getMaxAcceleration())
110 	{
111 		newVelocity.StoreNormalize();
112 		newVelocity *= world_->getMaxAcceleration();
113 	}
114 
115 	if (target_)
116 	{
117 		newVelocity += getVelocity();
118 		target_->getLife().setVelocity(newVelocity);
119 
120 		if (complexUpdate)
121 		{
122 			if (context_.getOptionsGame().getActionSyncCheck() &&
123 				context_.getOptionsGame().getActionMovementSyncCheck())
124 			{
125 				context_.getSimulator().addSyncCheck(
126 					S3D::formatStringBuffer("TargetMovement: %u %s %s",
127 						target_->getPlayerId(),
128 						target_->getLife().getTargetPosition().asQuickString(),
129 						target_->getLife().getVelocity().asQuickString()));
130 			}
131 		}
132 	}
133 }
134 
checkGrouping(std::vector<Boid2 * > & boidSet)135 FixedVector Boid2::checkGrouping(std::vector<Boid2*> &boidSet)
136 {
137 	FixedVector posMatch, velMatch, velDist;
138 	FixedVector normalizedVelocity = direction_;
139 
140 	int count = 0, countDist = 0;
141 	std::vector<Boid2*>::iterator itor;
142 	for (itor = boidSet.begin(); itor != boidSet.end(); ++itor)
143 	{
144 		Boid2 *other = (Boid2 *) *itor;
145 		if (!other || other == this) continue;
146 
147 		FixedVector direction = getPosition() - other->getPosition();
148 		fixed distance = direction.Magnitude();
149 		FixedVector normalizedDirection = direction / distance;
150 		fixed dp = normalizedDirection.dotP(normalizedVelocity);
151 		if (dp.abs() < fixed(true, 3000))
152 		{
153 			count++;
154 
155 			posMatch += other->getPosition();
156 			velMatch += other->getVelocity();
157 
158 			if (distance < world_->getCruiseDistance())
159 			{
160 				velDist += direction;
161 				countDist++;
162 			}
163 		}
164 	}
165 
166 	if (countDist > 0)
167 	{
168 		velDist /= countDist;
169 		velDist /= 10;
170 	}
171 
172 	if (count > 0)
173 	{
174 		posMatch /= count;
175 		posMatch = (posMatch - getPosition()) / 10;
176 
177 		velMatch /= count;
178 		velMatch = (velMatch - getVelocity()) / 10;
179 	}
180 
181 	FixedVector velocity;
182 	velocity += posMatch;
183 	velocity += velMatch;
184 	velocity += velDist;
185 	return velocity;
186 }
187 
checkCollision(FixedVector & normal)188 bool Boid2::checkCollision(FixedVector &normal)
189 {
190 	int collision = 0;
191 	FixedVector &position = getPosition();
192 	if (position[0] < world_->getMinBounds()[0])
193 	{
194 		collision++;
195 		normal += FixedVector(1, 0, 0);
196 	}
197 	else if (position[0] > world_->getMaxBounds()[0])
198 	{
199 		collision++;
200 		normal += FixedVector(-1, 0, 0);
201 	}
202 
203 	if (position[1] < world_->getMinBounds()[1])
204 	{
205 		collision++;
206 		normal += FixedVector(0, 1, 0);
207 	}
208 	else if (position[1] > world_->getMaxBounds()[1])
209 	{
210 		collision++;
211 		normal += FixedVector(0, -1, 0);
212 	}
213 
214 	if (position[2] < world_->getMinBounds()[2])
215 	{
216 		collision++;
217 		normal += FixedVector(0, 0, 1);
218 	}
219 	else if (position[2] > world_->getMaxBounds()[2])
220 	{
221 		collision++;
222 		normal += FixedVector(0, 0, -1);
223 	}
224 
225 	for (int i=0; i<4; i++)
226 	{
227 		fixed dist = fixed(i) * 3;
228 		FixedVector newPosition = position + direction_ * dist;
229 
230 		if (context_.getLandscapeMaps().getGroundMaps().getHeight(
231 			newPosition[0].asInt(), newPosition[1].asInt()) > newPosition[2] - fixed(5))
232 		{
233 			collision++;
234 			normal += context_.getLandscapeMaps().getGroundMaps().getNormal(
235 				newPosition[0].asInt(), newPosition[1].asInt());
236 			break;
237 		}
238 	}
239 
240 	if (collision > 0)
241 	{
242 		normal.StoreNormalize();
243 		return true;
244 	}
245 
246 	return false;
247 }
248