1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "bladerunner/actor_clues.h"
24 #include "bladerunner/actor.h"
25 #include "bladerunner/script/ai_script.h"
26
27 #include "bladerunner/bladerunner.h"
28 #include "bladerunner/game_info.h"
29 #include "bladerunner/crimes_database.h"
30 #include "bladerunner/savefile.h"
31
32 #include "common/debug.h"
33
34 namespace BladeRunner {
35
ActorClues(BladeRunnerEngine * vm,int cluesLimit)36 ActorClues::ActorClues(BladeRunnerEngine *vm, int cluesLimit) {
37 _vm = vm;
38 _count = 0;
39 _maxCount = 0;
40 switch (cluesLimit) {
41 case 4:
42 _maxCount = kClueCount;
43 break;
44 case 3:
45 _maxCount = 100;
46 break;
47 case 2:
48 _maxCount = 50;
49 break;
50 case 1:
51 _maxCount = 25;
52 break;
53 case 0:
54 _maxCount = 0;
55 break;
56 default:
57 return;
58 }
59
60 if (_maxCount > 0) {
61 _clues.resize(_maxCount);
62 }
63
64 removeAll();
65 }
66
acquire(int clueId,bool flag2,int fromActorId)67 void ActorClues::acquire(int clueId, bool flag2, int fromActorId) {
68 int clueIndex = findClueIndex(clueId);
69 if (clueIndex == -1) { // prevent assertion fault
70 // debug("Actor could not acquire clue: \"%s\" from %d", _vm->_crimesDatabase->getClueText(clueId), fromActorId);
71 return;
72 } else {
73 _clues[clueIndex].flags |= 0x01;
74 _clues[clueIndex].flags = (_clues[clueIndex].flags & ~0x02) | (((flag2? 1:0) << 1) & 0x02);
75 _clues[clueIndex].fromActorId = fromActorId;
76 // debug("Actor acquired clue: \"%s\" from %d", _vm->_crimesDatabase->getClueText(clueId), fromActorId);
77 }
78 }
79
lose(int clueId)80 void ActorClues::lose(int clueId) {
81 int clueIndex = findClueIndex(clueId);
82 if (clueIndex == -1) { // prevent assertion fault
83 // debug("Actor could not lose clue: \"%s\"", _vm->_crimesDatabase->getClueText(clueId));
84 return;
85 } else {
86 _clues[clueIndex].flags = 0;
87 }
88 }
89
isAcquired(int clueId) const90 bool ActorClues::isAcquired(int clueId) const {
91 int clueIndex = findClueIndex(clueId);
92 if (clueIndex == -1) {
93 return false;
94 }
95 return _clues[clueIndex].flags & 0x01;
96 }
97
getWeight(int clueId) const98 int ActorClues::getWeight(int clueId) const {
99 int clueIndex = findClueIndex(clueId);
100 if (clueIndex == -1) {
101 return 0;
102 }
103 return _clues[clueIndex].weight;
104 }
105
getModifier(int actorId,int otherActorId,int clueId)106 int ActorClues::getModifier(int actorId, int otherActorId, int clueId) {
107 Actor *actor = _vm->_actors[actorId];
108 Actor *otherActor = _vm->_actors[otherActorId];
109 int modifier1, modifier2, modifier3, modifier4;
110
111 int friendliness = actor->getFriendlinessToOther(otherActorId);
112 int clueWeight = otherActor->_clues->getWeight(clueId);
113
114 if (actor->_clues->isFlag2(clueId)) {
115 modifier1 = 100 - actor->getHonesty() - friendliness;
116 } else {
117 modifier1 = 0;
118 }
119
120 modifier2 = 0;
121 for (int i = 0; i < (int)_vm->_gameInfo->getActorCount(); ++i) {
122 if (i != actorId && i != otherActorId) {
123 modifier2 += (friendliness - 50) * _vm->_aiScripts->callGetFriendlinessModifierIfGetsClue(i, otherActorId, clueId) / 100;
124 }
125 }
126
127 modifier3 = _vm->_aiScripts->callGetFriendlinessModifierIfGetsClue(otherActorId, actorId, clueId);
128
129 modifier4 = _vm->_rnd.getRandomNumberRng(0, (100 - actor->getIntelligence()) / 10);
130
131 if (_vm->_rnd.getRandomNumberRng(0, 1) == 1) {
132 modifier4 = -modifier4;
133 }
134
135 return modifier1 + modifier2 + modifier3 + modifier4 + clueWeight;
136 }
137
cluesCompare(const void * p1,const void * p2)138 static int cluesCompare(const void *p1, const void *p2) {
139 const ActorClues::CluesUS *clue1 = (const ActorClues::CluesUS *)p1;
140 const ActorClues::CluesUS *clue2 = (const ActorClues::CluesUS *)p2;
141
142 if (clue1->modifier > clue2->modifier)
143 return -1;
144
145 return (clue1->modifier < clue2->modifier);
146 }
147
acquireCluesByRelations(int actorId,int otherActorId)148 void ActorClues::acquireCluesByRelations(int actorId, int otherActorId) {
149 CluesUS clues1[kClueCount], clues2[kClueCount];
150
151 int count1 = findAcquirableCluesFromActor(actorId, otherActorId, clues1, kClueCount);
152 int count2 = findAcquirableCluesFromActor(otherActorId, actorId, clues2, kClueCount);
153
154 if (count1 || count2) {
155 for (int i = 0; i < count1; ++i) {
156 clues1[i].modifier = getModifier(actorId, otherActorId, clues1[i].clueId);
157 }
158 qsort(clues1, count1, sizeof(CluesUS), cluesCompare);
159
160 for (int i = 0; i < count2; ++i) {
161 clues2[i].modifier = getModifier(otherActorId, actorId, clues2[i].clueId);
162 }
163 qsort(clues2, count2, sizeof(CluesUS), cluesCompare);
164
165 Actor *actor = _vm->_actors[actorId];
166 Actor *otherActor = _vm->_actors[otherActorId];
167
168 uint avgParameters = (otherActor->getHonesty() + otherActor->getIntelligence() + actor->getFriendlinessToOther(otherActorId)) / 3;
169 int clue1count = avgParameters * count1 / 100;
170
171 if (avgParameters >= 50 && clue1count == 0 && count1 == 1) {
172 clue1count = 1;
173 }
174
175 avgParameters = (actor->getHonesty() + actor->getIntelligence() + otherActor->getFriendlinessToOther(actorId)) / 3;
176 int clue2count = avgParameters * count2 / 100;
177
178 if (avgParameters >= 50 && clue2count == 0 && count2 == 1) {
179 clue2count = 1;
180 }
181
182 for (int i = 0; i < clue2count; ++i) {
183 bool flag = false;
184 if (otherActor->_clues->isFlag2(clues2[i].clueId)) {
185 avgParameters = (2 * otherActor->getFriendlinessToOther(actorId) + otherActor->getHonesty()) / 3;
186
187 if (avgParameters > 70) {
188 avgParameters = 100;
189 } else if (avgParameters < 30) {
190 avgParameters = 0;
191 }
192 if (_vm->_rnd.getRandomNumberRng(1, 100) <= avgParameters) {
193 flag = true;
194 }
195 }
196
197 actor->_clues->acquire(clues2[i].clueId, flag, otherActorId);
198 }
199
200 for (int i = 0; i < clue1count; ++i) {
201 bool flag = false;
202 if (actor->_clues->isFlag2(clues1[i].clueId)) {
203 avgParameters = (2 * actor->getFriendlinessToOther(otherActorId) + actor->getHonesty()) / 3;
204
205 if (avgParameters > 70) {
206 avgParameters = 100;
207 } else if (avgParameters < 30) {
208 avgParameters = 0;
209 }
210 if (_vm->_rnd.getRandomNumberRng(1, 100) <= avgParameters) {
211 flag = true;
212 }
213 }
214
215 otherActor->_clues->acquire(clues1[i].clueId, flag, actorId);
216 }
217 }
218 }
219
findAcquirableCluesFromActor(int actorId,int targetActorId,ActorClues::CluesUS * list,int size)220 int ActorClues::findAcquirableCluesFromActor(int actorId, int targetActorId, ActorClues::CluesUS *list, int size) {
221 Actor *actor = _vm->_actors[actorId];
222 Actor *otherActor = _vm->_actors[targetActorId];
223 int count = 0;
224 int cluesCount = actor->_clues->getCount();
225
226 for (int i = 0; i < cluesCount; ++i) {
227 int clueId = actor->_clues->getClueIdByIndex(i);
228
229 if (actor->_clues->isAcquired(clueId)
230 && otherActor->_clues->getWeight(clueId) > 0
231 && !otherActor->_clues->isAcquired(clueId)) {
232 list[count].clueId = clueId;
233 list[count].modifier = 0;
234
235 ++count;
236 }
237 }
238
239 return count;
240 }
241
getFromActorId(int clueId) const242 int ActorClues::getFromActorId(int clueId) const {
243 int clueIndex = findClueIndex(clueId);
244 if (clueIndex == -1) {
245 return -1;
246 }
247
248 return _clues[clueIndex].fromActorId;
249 }
250
251 /**
252 * @brief returns if flag2 for specified clue is set.
253 *
254 * Bit flag "flag2" seems to affect one modifier when sharing / spreading clues
255 * (based on Honesty, friendliness and some seemingly *complicated* algorithm).
256 * It seems that it increases the overall modifier value for a clue, making it more likely to be shared with another actor.
257 *
258 * @param clueId
259 * @return true if this bit flag is set, false otherwise
260 */
isFlag2(int clueId) const261 bool ActorClues::isFlag2(int clueId) const {
262 int clueIndex = findClueIndex(clueId);
263 if (clueIndex == -1) {
264 return false;
265 }
266
267 return _clues[clueIndex].flags & 0x02;
268 }
269
isViewed(int clueId) const270 bool ActorClues::isViewed(int clueId) const {
271 int clueIndex = findClueIndex(clueId);
272 if (clueIndex == -1) {
273 return false;
274 }
275
276 return _clues[clueIndex].flags & 0x04;
277 }
278
setViewed(int clueId,bool viewed)279 void ActorClues::setViewed(int clueId, bool viewed) {
280 int clueIndex = findClueIndex(clueId);
281 if (clueIndex == -1) {
282 return;
283 }
284
285 if (viewed) {
286 _clues[clueIndex].flags |= 0x04;
287 } else {
288 _clues[clueIndex].flags &= ~0x04;
289 }
290 }
291
292 // Method for Restored Content
293 // Checks whether a clue, which McCoy has, was shared between McCoy and Mainframe
isSharedWithMainframe(int clueId) const294 bool ActorClues::isSharedWithMainframe(int clueId) const {
295 int clueIndex = findClueIndex(clueId);
296 if (clueIndex == -1) {
297 return false;
298 }
299
300 return _clues[clueIndex].field4 & 0x01;
301 }
302
303 // Method for Restored Content
304 // Marks a clue, which McCoy has, as shared between McCoy and Mainframe
setSharedWithMainframe(int clueId,bool value)305 void ActorClues::setSharedWithMainframe(int clueId, bool value) {
306 int clueIndex = findClueIndex(clueId);
307 if (clueIndex == -1) {
308 return;
309 }
310
311 if (value) {
312 _clues[clueIndex].field4 |= 0x01;
313 } else {
314 _clues[clueIndex].field4 &= ~0x01;
315 }
316 }
317
isPrivate(int clueId) const318 bool ActorClues::isPrivate(int clueId) const {
319 int clueIndex = findClueIndex(clueId);
320 if (clueIndex == -1) {
321 return false;
322 }
323
324 return _clues[clueIndex].flags & 0x08;
325 }
326
setPrivate(int clueId,bool value)327 void ActorClues::setPrivate(int clueId, bool value) {
328 int clueIndex = findClueIndex(clueId);
329 if (clueIndex == -1) {
330 return;
331 }
332
333 if (value) {
334 _clues[clueIndex].flags |= 0x08;
335 } else {
336 _clues[clueIndex].flags &= ~0x08;
337 }
338 }
339
getCount() const340 int ActorClues::getCount() const {
341 return _count;
342 }
343
getClueIdByIndex(int index) const344 int ActorClues::getClueIdByIndex(int index) const {
345 assert(index < _count);
346
347 if (index < 0 || index >= _count) {
348 return -1;
349 }
350 return _clues[index].clueId;
351 }
352
removeAll()353 void ActorClues::removeAll() {
354 _count = 0;
355 for (int i = 0; i < _maxCount; ++i) {
356 remove(i);
357 }
358 }
359
findClueIndex(int clueId) const360 int ActorClues::findClueIndex(int clueId) const {
361 for (int i = 0; i < _count; ++i) {
362 if (clueId == _clues[i].clueId) {
363 return i;
364 }
365 }
366 return -1;
367 }
368
add(int actorId,int clueId,int weight,bool acquired,bool unknownFlag,int fromActorId)369 void ActorClues::add(int actorId, int clueId, int weight, bool acquired, bool unknownFlag, int fromActorId) {
370 assert(_count < _maxCount);
371
372 _clues[_count].clueId = clueId;
373 _clues[_count].weight = weight;
374
375 _clues[_count].flags = 0;
376 _clues[_count].flags = (_clues[_count].flags & ~0x01) | ((acquired? 1:0) & 0x01);
377 _clues[_count].flags = (_clues[_count].flags & ~0x02) | (((unknownFlag? 1:0) << 1) & 0x02);
378
379 _clues[_count].fromActorId = fromActorId;
380 ++_count;
381 }
382
exists(int clueId) const383 bool ActorClues::exists(int clueId) const {
384 return findClueIndex(clueId) != -1;
385 }
386
remove(int index)387 void ActorClues::remove(int index) {
388 // if (_vm->_crimesDatabase) {
389 // debug("Actor removed clue: \"%s\"", _vm->_crimesDatabase->getClueText(_clues[index].clueId));
390 // }
391
392 _clues[index].clueId = -1;
393 _clues[index].weight = 0;
394 _clues[index].flags = 0;
395 _clues[index].fromActorId = -1;
396
397 _clues[index].field3 = -1; // unused (but stored/restored)
398 _clues[index].field4 = 0; // Restored Content: Use to mark if McCoy's clue was shared with Mainframe. original: unused (but stored/restored)
399 _clues[index].field5 = -1; // unused (but stored/restored)
400 _clues[index].field6 = 0; // unused (but stored/restored)
401 _clues[index].field7 = -1; // unused (but stored/restored)
402 _clues[index].field8 = 0; // unused (but stored/restored)
403 }
404
save(SaveFileWriteStream & f)405 void ActorClues::save(SaveFileWriteStream &f) {
406 f.writeInt(_count);
407 f.writeInt(_maxCount);
408 for (int i = 0; i < _maxCount; ++i) {
409 Clue &c = _clues[i];
410 f.writeInt(c.clueId);
411 f.writeInt(c.weight);
412 f.writeInt(c.fromActorId);
413 f.writeInt(c.field3);
414 f.writeInt(c.field4);
415 f.writeInt(c.field5);
416 f.writeInt(c.field6);
417 f.writeInt(c.field7);
418 f.writeInt(c.field8);
419 f.writeByte(c.flags);
420 }
421 }
422
load(SaveFileReadStream & f)423 void ActorClues::load(SaveFileReadStream &f) {
424 _count = f.readInt();
425 _maxCount = f.readInt();
426 _clues.clear();
427 _clues.resize(_maxCount);
428 for (int i = 0; i < _maxCount; ++i) {
429 Clue &c = _clues[i];
430 c.clueId = f.readInt();
431 c.weight = f.readInt();
432 c.fromActorId = f.readInt();
433 c.field3 = f.readInt();
434 c.field4 = f.readInt();
435 c.field5 = f.readInt();
436 c.field6 = f.readInt();
437 c.field7 = f.readInt();
438 c.field8 = f.readInt();
439 c.flags = f.readByte();
440 }
441 }
442
443 } // End of namespace BladeRunner
444