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