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