1 #include "ATask.h"
2
3 #include "CGroup.h"
4 #include "CEconomy.h"
5 #include "CUnit.h"
6
7 int ATask::counter = 0;
8
ATask(AIClasses * _ai)9 ATask::ATask(AIClasses *_ai):ARegistrar(++counter) {
10 t = TASK_UNDEFINED;
11 ai = _ai;
12 active = false;
13 suspended = false;
14 isMoving = true;
15 pos = ZEROVECTOR;
16 initFrame = ai->cb->GetCurrentFrame();
17 validateInterval = 5 * 30; // 5 sec by default
18 nextValidateFrame = validateInterval;
19 priority = NORMAL;
20 queueID = 0;
21 }
22
remove()23 void ATask::remove() {
24 LOG_II("ATask::remove " << (*this))
25
26 // NOTE: removal order below is VERY important
27
28 // remove current task from CTaskHandler, so it will mark this task
29 // to be killed on next update
30 std::list<ARegistrar*>::iterator j = records.begin();
31 while (j != records.end()) {
32 ARegistrar *regobj = *j; ++j;
33 regobj->remove(*this);
34 }
35
36 // remove all assisting tasks...
37 std::list<ATask*>::iterator i = assisters.begin();
38 while (i != assisters.end()) {
39 ATask *task = *i; ++i;
40 task->remove();
41 }
42 assert(assisters.size() == 0);
43
44 // detach task from groups...
45 std::list<CGroup*>::iterator itGroup = groups.begin();
46 while(itGroup != groups.end()) {
47 CGroup *g = *itGroup; ++itGroup;
48 removeGroup(*g);
49 }
50
51 active = false;
52 }
53
54 // called on Group removing
remove(ARegistrar & group)55 void ATask::remove(ARegistrar &group) {
56 CGroup *g = dynamic_cast<CGroup*>(&group);
57
58 assert(g != NULL);
59
60 removeGroup(*g);
61
62 if (groups.empty()) {
63 LOG_II("ATask::remove " << (*g))
64
65 remove();
66 }
67 }
68
firstGroup() const69 CGroup* ATask::firstGroup() const {
70 if (groups.empty())
71 return NULL;
72 return groups.front();
73 }
74
addGroup(CGroup & g)75 void ATask::addGroup(CGroup &g) {
76 // FIXME: remove this when task queue will be supported
77 assert(!g.busy);
78 /*
79 if (g->busy) {
80 ATask *task = ai->tasks->getTask(g);
81 assert(task != NULL && task != this);
82 task->suspended = true;
83 // TODO: nextTask = task;
84 }
85 */
86 g.reg(*this);
87 g.busy = true;
88 g.micro(false);
89 //g.abilities(true);
90
91 if ((g.cats&STATIC).any())
92 isMoving = false;
93
94 groups.push_back(&g);
95 }
96
removeGroup(CGroup & g)97 void ATask::removeGroup(CGroup &g) {
98 g.unreg(*this);
99
100 if (!suspended) {
101 g.busy = false;
102 g.unwait();
103 g.micro(false);
104 //g.abilities(false);
105 if (isMoving) g.stop();
106 }
107
108 groups.remove(&g);
109 }
110
enemyScan(int & target)111 bool ATask::enemyScan(int& target) {
112 CGroup *group = firstGroup();
113 bool scout = (group->cats&SCOUTER).any();
114 bool aircraft = (group->cats&AIR).any();
115 TargetsFilter tf;
116
117 if (scout) {
118 tf.threatCeiling = 1.1f;
119 tf.threatRadius = 300.0f;
120 }
121 else {
122 if (aircraft) {
123 if ((group->cats&ASSAULT).any()) {
124 tf.exclude = AIR;
125 tf.threatCeiling = group->strength;
126 }
127 else {
128 tf.threatCeiling = 1.1f;
129 if((group->cats&ANTIAIR).any()) {
130 tf.exclude = LAND|SEA|SUB;
131 }
132 }
133 // TODO: replace with maneuvering radius?
134 tf.threatRadius = 300.0f;
135 }
136 else {
137 tf.exclude = SCOUTER;
138 tf.threatFactor = 0.001f;
139 tf.threatCeiling = group->strength;
140 tf.threatRadius = 0.0f;
141 }
142 }
143
144 // do not chase after aircraft with non-aircraft groups...
145 if (!aircraft)
146 tf.exclude |= AIR;
147
148 target = group->selectTarget(group->getScanRange(), tf);
149
150 if (target >= 0) {
151 group->attack(target);
152 group->micro(true);
153 if (scout)
154 LOG_II("ATask::enemyScan scout " << (*group) << " is microing enemy target Unit(" << target << ") (threat = " << tf.threatValue << ")")
155 else
156 LOG_II("ATask::enemyScan engage " << (*group) << " is microing enemy target Unit(" << target << ") (threat = " << tf.threatValue << ")")
157 return true;
158 }
159
160 return false;
161 }
162
resourceScan()163 bool ATask::resourceScan() {
164 bool isFeature = true;
165 int bestFeature = -1;
166 float bestDist = std::numeric_limits<float>::max();
167 CGroup *group = firstGroup();
168 // NOTE: do not use group->los because it is too small and does not
169 // correspond to real map units
170 float radius = group->buildRange;
171 float3 gpos = group->pos();
172
173 assert(radius > EPS);
174
175 // reclaim features when we can store metal only...
176 if (!ai->economy->mexceeding) {
177 const int numFeatures = ai->cb->GetFeatures(&ai->unitIDs[0], MAX_FEATURES, gpos, 1.5f * radius);
178 for (int i = 0; i < numFeatures; i++) {
179 const int uid = ai->unitIDs[i];
180 const FeatureDef *fd = ai->cb->GetFeatureDef(uid);
181 if (fd->metal > 0.0f) {
182 float3 fpos = ai->cb->GetFeaturePos(uid);
183 float dist = gpos.distance2D(fpos);
184 if (dist < bestDist) {
185 bestFeature = uid;
186 bestDist = dist;
187 }
188 }
189 }
190 }
191
192 // if there is no feature available then reclaim enemy unarmed building,
193 // hehe :)
194 if (bestFeature == -1) {
195 std::map<int, bool> occupied;
196 TargetsFilter tf;
197 tf.include = STATIC;
198 tf.exclude = ATTACKER;
199 tf.threatCeiling = 1.1f;
200 tf.threatRadius = radius;
201
202 bestFeature = group->selectTarget(radius, tf);
203
204 isFeature = false;
205 }
206
207 if (bestFeature != -1) {
208 group->reclaim(bestFeature, isFeature);
209 group->micro(true);
210 LOG_II("ATask::resourceScan group " << (*group) << " is reclaiming")
211 return true;
212 }
213
214 return false;
215 }
216
repairScan()217 bool ATask::repairScan() {
218 if (ai->economy->mstall || ai->economy->estall)
219 return false;
220
221 int bestUnit = -1;
222 float bestScore = 0.0f;
223 CGroup *group = firstGroup();
224 float radius = group->buildRange;
225 float3 gpos = group->pos();
226
227 const int numUnits = ai->cb->GetFriendlyUnits(&ai->unitIDs[0], gpos, 2.0f * radius, MAX_FEATURES);
228 for (int i = 0; i < numUnits; i++) {
229 const int uid = ai->unitIDs[i];
230
231 if (ai->cb->UnitBeingBuilt(uid) || group->firstUnit()->key == uid)
232 continue;
233
234 const float healthDamage = ai->cb->GetUnitMaxHealth(uid) - ai->cb->GetUnitHealth(uid);
235 if (healthDamage > EPS) {
236 // TODO: somehow limit number of repairing builders per unit
237 const UnitDef *ud = ai->cb->GetUnitDef(uid);
238 const unitCategory cats = UC(ud->id);
239
240 if ((cats&AIR).any())
241 continue;
242
243 const float score = healthDamage + (CUnit::isDefense(ud) ? 10000.0f: 0.0f) + (CUnit::isStatic(ud) ? 5000.0f: 0.0f);
244 if (score > bestScore) {
245 bestUnit = uid;
246 bestScore = score;
247 }
248 }
249 }
250
251 if (bestUnit != -1) {
252 group->repair(bestUnit);
253 group->micro(true);
254 LOG_II("ATask::repairScan group " << (*group) << " is repairing")
255 return true;
256 }
257
258 return false;
259 }
260
lifeFrames() const261 int ATask::lifeFrames() const {
262 return ai->cb->GetCurrentFrame() - initFrame;
263 }
264
lifeTime() const265 float ATask::lifeTime() const {
266 return (float)(ai->cb->GetCurrentFrame() - initFrame) / 30.0f;
267 }
268
update()269 void ATask::update() {
270 if (!active) return;
271
272 if (validateInterval > 0) {
273 int lifetime = lifeFrames();
274 if (lifetime >= nextValidateFrame) {
275 if (!onValidate()) {
276 remove();
277 return;
278 }
279 else
280 nextValidateFrame = lifetime + validateInterval;
281 }
282 }
283
284 if (suspended) return;
285
286 onUpdate();
287 }
288
operator <<(std::ostream & out,const ATask & atask)289 std::ostream& operator<<(std::ostream &out, const ATask &atask) {
290 atask.toStream(out);
291
292 if (atask.assisters.size() > 0) {
293 out << " Assisters: amount(" << atask.assisters.size() << ") [";
294 std::list<ATask*>::const_iterator i;
295 for (i = atask.assisters.begin(); i != atask.assisters.end(); ++i) {
296 CGroup *group = (*i)->firstGroup();
297 if (group)
298 out << (*group);
299 }
300 out << "]";
301 }
302
303 return out;
304 }
305