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 "ags/shared/ac/character_info.h"
24 #include "ags/shared/ac/common.h"
25 #include "ags/shared/ac/game_setup_struct.h"
26 #include "ags/engine/ac/character.h"
27 #include "ags/engine/ac/character_extras.h"
28 #include "ags/engine/ac/game_state.h"
29 #include "ags/engine/ac/global_character.h"
30 #include "ags/engine/ac/math.h"
31 #include "ags/engine/ac/view_frame.h"
32 #include "ags/engine/debugging/debug_log.h"
33 #include "ags/shared/game/room_struct.h"
34 #include "ags/engine/main/update.h"
35 #include "ags/engine/media/audio/audio_system.h"
36 #include "ags/ags.h"
37 #include "ags/globals.h"
38 
39 namespace AGS3 {
40 
41 using namespace AGS::Shared;
42 
43 #define Random __Rand
44 
get_effective_y()45 int CharacterInfo::get_effective_y() {
46 	return y - z;
47 }
get_baseline()48 int CharacterInfo::get_baseline() {
49 	if (baseline < 1)
50 		return y;
51 	return baseline;
52 }
get_blocking_top()53 int CharacterInfo::get_blocking_top() {
54 	if (blocking_height > 0)
55 		return y - blocking_height / 2;
56 	return y - 2;
57 }
get_blocking_bottom()58 int CharacterInfo::get_blocking_bottom() {
59 	// the blocking_bottom should be 1 less than the top + height
60 	// since the code does <= checks on it rather than < checks
61 	if (blocking_height > 0)
62 		return (y + (blocking_height + 1) / 2) - 1;
63 	return y + 3;
64 }
65 
UpdateMoveAndAnim(int & char_index,CharacterExtras * chex,int & numSheep,int * followingAsSheep)66 void CharacterInfo::UpdateMoveAndAnim(int &char_index, CharacterExtras *chex, int &numSheep, int *followingAsSheep) {
67 	int res;
68 
69 	if (on != 1) return;
70 
71 	// walking
72 	res = update_character_walking(chex);
73 	// [IKM] Yes, it should return! upon getting RETURN_CONTINUE here
74 	if (res == RETURN_CONTINUE) { // [IKM] now, this is one of those places...
75 		return;                   //  must be careful not to screw things up
76 	}
77 
78 	// Fixup character's view when possible
79 	if (view >= 0 &&
80 		(loop >= _G(views)[view].numLoops || frame >= _G(views)[view].loops[loop].numFrames)) {
81 		for (loop = 0;
82 			(loop < _G(views)[view].numLoops) && (_G(views)[view].loops[loop].numFrames == 0); ++loop) {
83 		}
84 		if (loop == _G(views)[view].numLoops) {
85 			// view has no frames?!
86 			// amazingly enough there are old games that allow this to happen...
87 			if (_G(loaded_game_file_version) >= kGameVersion_300)
88 				quitprintf("!Character %s is assigned view %d that has no frames!", name, view);
89 			loop = 0;
90 		}
91 	}
92 
93 	int doing_nothing = 1;
94 
95 	update_character_moving(char_index, chex, doing_nothing);
96 
97 	// [IKM] 2012-06-28:
98 	// Character index value is used to set up some variables in there, so I cannot just cease using it
99 	res = update_character_animating(char_index, doing_nothing);
100 	// [IKM] Yes, it should return! upon getting RETURN_CONTINUE here
101 	if (res == RETURN_CONTINUE) { // [IKM] now, this is one of those places...
102 		return;                   //  must be careful not to screw things up
103 	}
104 
105 	update_character_follower(char_index, numSheep, followingAsSheep, doing_nothing);
106 
107 	update_character_idle(chex, doing_nothing);
108 
109 	chex->process_idle_this_time = 0;
110 }
111 
UpdateFollowingExactlyCharacter()112 void CharacterInfo::UpdateFollowingExactlyCharacter() {
113 	x = _GP(game).chars[following].x;
114 	y = _GP(game).chars[following].y;
115 	z = _GP(game).chars[following].z;
116 	room = _GP(game).chars[following].room;
117 	prevroom = _GP(game).chars[following].prevroom;
118 
119 	int usebase = _GP(game).chars[following].get_baseline();
120 
121 	if (flags & CHF_BEHINDSHEPHERD)
122 		baseline = usebase - 1;
123 	else
124 		baseline = usebase + 1;
125 }
126 
update_character_walking(CharacterExtras * chex)127 int CharacterInfo::update_character_walking(CharacterExtras *chex) {
128 	if (walking >= TURNING_AROUND) {
129 		// Currently rotating to correct direction
130 		if (walkwait > 0) walkwait--;
131 		else {
132 			// Work out which direction is next
133 			int wantloop = find_looporder_index(loop) + 1;
134 			// going anti-clockwise, take one before instead
135 			if (walking >= TURNING_BACKWARDS)
136 				wantloop -= 2;
137 			while (1) {
138 				if (wantloop >= 8)
139 					wantloop = 0;
140 				if (wantloop < 0)
141 					wantloop = 7;
142 				if ((turnlooporder[wantloop] >= _G(views)[view].numLoops) ||
143 				        (_G(views)[view].loops[turnlooporder[wantloop]].numFrames < 1) ||
144 				        ((turnlooporder[wantloop] >= 4) && ((flags & CHF_NODIAGONAL) != 0))) {
145 					if (walking >= TURNING_BACKWARDS)
146 						wantloop--;
147 					else
148 						wantloop++;
149 				} else break;
150 			}
151 			loop = turnlooporder[wantloop];
152 			walking -= TURNING_AROUND;
153 			// if still turning, wait for next frame
154 			if (walking % TURNING_BACKWARDS >= TURNING_AROUND)
155 				walkwait = animspeed;
156 			else
157 				walking = walking % TURNING_BACKWARDS;
158 			chex->animwait = 0;
159 		}
160 		return RETURN_CONTINUE;
161 		//continue;
162 	}
163 
164 	return 0;
165 }
166 
update_character_moving(int & char_index,CharacterExtras * chex,int & doing_nothing)167 void CharacterInfo::update_character_moving(int &char_index, CharacterExtras *chex, int &doing_nothing) {
168 	if ((walking > 0) && (room == _G(displayed_room))) {
169 		if (walkwait > 0) walkwait--;
170 		else {
171 			flags &= ~CHF_AWAITINGMOVE;
172 
173 			// Move the character
174 			int numSteps = wantMoveNow(this, chex);
175 
176 			if ((numSteps) && (chex->xwas != INVALID_X)) {
177 				// if the zoom level changed mid-move, the walkcounter
178 				// might not have come round properly - so sort it out
179 				x = chex->xwas;
180 				y = chex->ywas;
181 				chex->xwas = INVALID_X;
182 			}
183 
184 			int oldxp = x, oldyp = y;
185 
186 			for (int ff = 0; ff < abs(numSteps); ff++) {
187 				if (doNextCharMoveStep(this, char_index, chex))
188 					break;
189 				if ((walking == 0) || (walking >= TURNING_AROUND))
190 					break;
191 			}
192 
193 			if (numSteps < 0) {
194 				// very small scaling, intersperse the movement
195 				// to stop it being jumpy
196 				chex->xwas = x;
197 				chex->ywas = y;
198 				x = ((x) - oldxp) / 2 + oldxp;
199 				y = ((y) - oldyp) / 2 + oldyp;
200 			} else if (numSteps > 0)
201 				chex->xwas = INVALID_X;
202 
203 			if ((flags & CHF_ANTIGLIDE) == 0)
204 				walkwaitcounter++;
205 		}
206 
207 		if (loop >= _G(views)[view].numLoops)
208 			quitprintf("Unable to render character %d (%s) because loop %d does not exist in view %d", index_id, name, loop, view + 1);
209 
210 		// check don't overflow loop
211 		int framesInLoop = _G(views)[view].loops[loop].numFrames;
212 		if (frame > framesInLoop) {
213 			frame = 1;
214 
215 			if (framesInLoop < 2)
216 				frame = 0;
217 
218 			if (framesInLoop < 1)
219 				quitprintf("Unable to render character %d (%s) because there are no frames in loop %d", index_id, name, loop);
220 		}
221 
222 		if (walking < 1) {
223 			chex->process_idle_this_time = 1;
224 			doing_nothing = 1;
225 			walkwait = 0;
226 			chex->animwait = 0;
227 			// use standing pic
228 			Character_StopMoving(this);
229 			frame = 0;
230 			CheckViewFrameForCharacter(this);
231 		} else if (chex->animwait > 0) chex->animwait--;
232 		else {
233 			if (flags & CHF_ANTIGLIDE)
234 				walkwaitcounter++;
235 
236 			if ((flags & CHF_MOVENOTWALK) == 0) {
237 				frame++;
238 				if (frame >= _G(views)[view].loops[loop].numFrames) {
239 					// end of loop, so loop back round skipping the standing frame
240 					frame = 1;
241 
242 					if (_G(views)[view].loops[loop].numFrames < 2)
243 						frame = 0;
244 				}
245 
246 				chex->animwait = _G(views)[view].loops[loop].frames[frame].speed + animspeed;
247 
248 				if (flags & CHF_ANTIGLIDE)
249 					walkwait = chex->animwait;
250 				else
251 					walkwait = 0;
252 
253 				CheckViewFrameForCharacter(this);
254 			}
255 		}
256 		doing_nothing = 0;
257 	}
258 }
259 
update_character_animating(int & aa,int & doing_nothing)260 int CharacterInfo::update_character_animating(int &aa, int &doing_nothing) {
261 	// not moving, but animating
262 	// idleleft is <0 while idle view is playing (.animating is 0)
263 	if (((animating != 0) || (idleleft < 0)) &&
264 	        ((walking == 0) || ((flags & CHF_MOVENOTWALK) != 0)) &&
265 	        (room == _G(displayed_room))) {
266 		doing_nothing = 0;
267 		// idle anim doesn't count as doing something
268 		if (idleleft < 0)
269 			doing_nothing = 1;
270 
271 		if (wait > 0) wait--;
272 		else if ((_G(char_speaking) == aa) && (_GP(game).options[OPT_LIPSYNCTEXT] != 0)) {
273 			// currently talking with lip-sync speech
274 			int fraa = frame;
275 			wait = update_lip_sync(view, loop, &fraa) - 1;
276 			// closed mouth at end of sentence
277 			// NOTE: standard lip-sync is synchronized with text timer, not voice file
278 			if (_GP(play).speech_in_post_state ||
279 			        ((_GP(play).messagetime >= 0) && (_GP(play).messagetime < _GP(play).close_mouth_speech_time)))
280 				frame = 0;
281 
282 			if (frame != fraa) {
283 				frame = fraa;
284 				CheckViewFrameForCharacter(this);
285 			}
286 
287 			//continue;
288 			return RETURN_CONTINUE;
289 		} else {
290 			int oldframe = frame;
291 			if (animating & CHANIM_BACKWARDS) {
292 				frame--;
293 				if (frame < 0) {
294 					// if the previous loop is a Run Next Loop one, go back to it
295 					if ((loop > 0) &&
296 					        (_G(views)[view].loops[loop - 1].RunNextLoop())) {
297 
298 						loop--;
299 						frame = _G(views)[view].loops[loop].numFrames - 1;
300 					} else if (animating & CHANIM_REPEAT) {
301 
302 						frame = _G(views)[view].loops[loop].numFrames - 1;
303 
304 						while (_G(views)[view].loops[loop].RunNextLoop()) {
305 							loop++;
306 							frame = _G(views)[view].loops[loop].numFrames - 1;
307 						}
308 					} else {
309 						frame++;
310 						animating = 0;
311 					}
312 				}
313 			} else
314 				frame++;
315 
316 			if ((aa == _G(char_speaking)) &&
317 			        (_GP(play).speech_in_post_state ||
318 			         ((!_GP(play).speech_has_voice) &&
319 			          (_GP(play).close_mouth_speech_time > 0) &&
320 			          (_GP(play).messagetime < _GP(play).close_mouth_speech_time)))) {
321 				// finished talking - stop animation
322 				animating = 0;
323 				frame = 0;
324 			}
325 
326 			if (frame >= _G(views)[view].loops[loop].numFrames) {
327 
328 				if (_G(views)[view].loops[loop].RunNextLoop()) {
329 					if (loop + 1 >= _G(views)[view].numLoops)
330 						quit("!Animating character tried to overrun last loop in view");
331 					loop++;
332 					frame = 0;
333 				} else if ((animating & CHANIM_REPEAT) == 0) {
334 					animating = 0;
335 					frame--;
336 					// end of idle anim
337 					if (idleleft < 0) {
338 						// constant anim, reset (need this cos animating==0)
339 						if (idletime == 0)
340 							frame = 0;
341 						// one-off anim, stop
342 						else {
343 							ReleaseCharacterView(aa);
344 							idleleft = idletime;
345 						}
346 					}
347 				} else {
348 					frame = 0;
349 					// if it's a multi-loop animation, go back to start
350 					if (_GP(play).no_multiloop_repeat == 0) {
351 						while ((loop > 0) &&
352 						        (_G(views)[view].loops[loop - 1].RunNextLoop()))
353 							loop--;
354 					}
355 				}
356 			}
357 			wait = _G(views)[view].loops[loop].frames[frame].speed;
358 			// idle anim doesn't have speed stored cos animating==0
359 			if (idleleft < 0)
360 				wait += animspeed + 5;
361 			else
362 				wait += (animating >> 8) & 0x00ff;
363 
364 			if (frame != oldframe)
365 				CheckViewFrameForCharacter(this);
366 		}
367 	}
368 
369 	return 0;
370 }
371 
update_character_follower(int & aa,int & numSheep,int * followingAsSheep,int & doing_nothing)372 void CharacterInfo::update_character_follower(int &aa, int &numSheep, int *followingAsSheep, int &doing_nothing) {
373 	if ((following >= 0) && (followinfo == FOLLOW_ALWAYSONTOP)) {
374 		// an always-on-top follow
375 		if (numSheep >= MAX_SHEEP)
376 			quit("too many sheep");
377 		followingAsSheep[numSheep] = aa;
378 		numSheep++;
379 	}
380 	// not moving, but should be following another character
381 	else if ((following >= 0) && (doing_nothing == 1)) {
382 		short distaway = (followinfo >> 8) & 0x00ff;
383 		// no character in this room
384 		if ((_GP(game).chars[following].on == 0) || (on == 0));
385 		else if (room < 0) {
386 			room++;
387 			if (room == 0) {
388 				// appear in the new room
389 				room = _GP(game).chars[following].room;
390 				x = _GP(play).entered_at_x;
391 				y = _GP(play).entered_at_y;
392 			}
393 		}
394 		// wait a bit, so we're not constantly walking
395 		else if (Random(100) < (followinfo & 0x00ff));
396 		// the followed character has changed room
397 		else if ((room != _GP(game).chars[following].room)
398 		         && (_GP(game).chars[following].on == 0))
399 			;  // do nothing if the player isn't visible
400 		else if (room != _GP(game).chars[following].room) {
401 			prevroom = room;
402 			room = _GP(game).chars[following].room;
403 
404 			if (room == _G(displayed_room)) {
405 				// only move to the room-entered position if coming into
406 				// the current room
407 				if (_GP(play).entered_at_x > (_GP(thisroom).Width - 8)) {
408 					x = _GP(thisroom).Width + 8;
409 					y = _GP(play).entered_at_y;
410 				} else if (_GP(play).entered_at_x < 8) {
411 					x = -8;
412 					y = _GP(play).entered_at_y;
413 				} else if (_GP(play).entered_at_y > (_GP(thisroom).Height - 8)) {
414 					y = _GP(thisroom).Height + 8;
415 					x = _GP(play).entered_at_x;
416 				} else if (_GP(play).entered_at_y < _GP(thisroom).Edges.Top + 8) {
417 					y = _GP(thisroom).Edges.Top + 1;
418 					x = _GP(play).entered_at_x;
419 				} else {
420 					// not at one of the edges
421 					// delay for a few seconds to let the player move
422 					room = -_GP(play).follow_change_room_timer;
423 				}
424 				if (room >= 0) {
425 					walk_character(aa, _GP(play).entered_at_x, _GP(play).entered_at_y, 1, true);
426 					doing_nothing = 0;
427 				}
428 			}
429 		} else if (room != _G(displayed_room)) {
430 			// if the characetr is following another character and
431 			// neither is in the current room, don't try to move
432 		} else if ((abs(_GP(game).chars[following].x - x) > distaway + 30) |
433 		           (abs(_GP(game).chars[following].y - y) > distaway + 30) |
434 		           ((followinfo & 0x00ff) == 0)) {
435 			// in same room
436 			int goxoffs = (Random(50) - 25);
437 			// make sure he's not standing on top of the other man
438 			if (goxoffs < 0) goxoffs -= distaway;
439 			else goxoffs += distaway;
440 			walk_character(aa, _GP(game).chars[following].x + goxoffs,
441 			               _GP(game).chars[following].y + (Random(50) - 25), 0, true);
442 			doing_nothing = 0;
443 		}
444 	}
445 }
446 
update_character_idle(CharacterExtras * chex,int & doing_nothing)447 void CharacterInfo::update_character_idle(CharacterExtras *chex, int &doing_nothing) {
448 	// no idle animation, so skip this bit
449 	if (idleview < 1);
450 	// currently playing idle anim
451 	else if (idleleft < 0);
452 	// not in the current room
453 	else if (room != _G(displayed_room));
454 	// they are moving or animating (or the view is locked), so
455 	// reset idle timeout
456 	else if ((doing_nothing == 0) || ((flags & CHF_FIXVIEW) != 0))
457 		idleleft = idletime;
458 	// count idle time
459 	else if ((_G(loopcounter) % 40 == 0) || (chex->process_idle_this_time == 1)) {
460 		idleleft--;
461 		if (idleleft == -1) {
462 			int useloop = loop;
463 			debug_script_log("%s: Now idle (view %d)", scrname, idleview + 1);
464 			Character_LockView(this, idleview + 1);
465 			// SetCharView resets it to 0
466 			idleleft = -2;
467 			int maxLoops = _G(views)[idleview].numLoops;
468 			// if the char is set to "no diagonal loops", don't try
469 			// to use diagonal idle loops either
470 			if ((maxLoops > 4) && (useDiagonal(this)))
471 				maxLoops = 4;
472 			// If it's not a "swimming"-type idleanim, choose a random loop
473 			// if there arent enough loops to do the current one.
474 			if ((idletime > 0) && (useloop >= maxLoops)) {
475 				do {
476 					useloop = ::AGS::g_vm->getRandomNumber(maxLoops - 1);
477 					// don't select a loop which is a continuation of a previous one
478 				} while ((useloop > 0) && (_G(views)[idleview].loops[useloop - 1].RunNextLoop()));
479 			}
480 			// Normal idle anim - just reset to loop 0 if not enough to
481 			// use the current one
482 			else if (useloop >= maxLoops)
483 				useloop = 0;
484 
485 			animate_character(this, useloop,
486 			                  animspeed + 5, (idletime == 0) ? 1 : 0, 1);
487 
488 			// don't set Animating while the idle anim plays
489 			animating = 0;
490 		}
491 	}  // end do idle animation
492 }
493 
494 } // namespace AGS3
495