1 /* ScummVM - Graphic Adventure Engine
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.
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.
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.
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.
26 #include "innocent/actor.h"
30 #include "common/rect.h"
32 #include "innocent/graphics.h"
33 #include "innocent/innocent.h"
34 #include "innocent/inter.h"
35 #include "innocent/logic.h"
36 #include "innocent/resources.h"
37 #include "innocent/room.h"
38 #include "innocent/util.h"
43 Actor::Actor(const CodePointer
&code
) : Animation(code
, Common::Point()) {
44 byte
*header
= code
.code();
45 _base
= header
- code
.offset();
47 snprintf(_debugInfo
, 50, "actor at %s", +code
);
48 _direction
= kDirNone
;
51 _attentionNeeded
= false;
52 _nextDirection
= kDirNone
;
54 Engine::instance().logic()->addAnimation(this);
59 bool Actor::isFine() const {
60 return _room
== Log
.currentRoom() && _base
&& !_attentionNeeded
&&
61 (!_framequeue
.empty() || !_confused
|| _nextDirection
);
64 void Actor::setAnimation(uint16 offset
) {
65 _base
= _base
- _baseOffset
+ offset
;
68 _debugInvalid
= false;
69 _confused
= _attentionNeeded
= false;
72 _counter
= _ticksLeft
= 0;
73 _nextDirection
= kDirNone
;
76 void Actor::setAnimation(const CodePointer
&anim
) {
77 debugC(3, kDebugLevelScript
, "setting animation code of %s to %s", _debugInfo
, +anim
);
79 _baseOffset
= anim
.offset();
81 _debugInvalid
= false;
82 _confused
= _attentionNeeded
= false;
85 _counter
= _ticksLeft
= 0;
86 _nextDirection
= kDirNone
;
91 _baseOffset
= _offset
= 0;
94 void Actor::callMe(const CodePointer
&code
) {
95 debugC(3, kDebugLevelScript
, "actor will call %s when needed", +code
);
96 _callBacks
.push(code
);
99 void Actor::tellMe(const CodePointer
&code
, uint16 timeout
) {
100 _roomCallbacks
.push_back(RoomCallback(timeout
, code
));
103 bool Actor::isSpeaking() const {
104 return _speech
.active();
107 void Actor::callMeWhenSilent(const CodePointer
&cp
) {
108 _speech
.callWhenDone(cp
);
111 void Actor::say(const Common::String
&text
) {
112 _speech
= Speech(this, text
);
115 Actor::Speech::~Speech() { while (!_cb
.empty()) Log
.runLater(_cb
.pop()); }
117 bool Actor::isMoving() const {
122 void Actor::callMeWhenStill(const CodePointer
&cp
) {
126 void Actor::setFrame(uint16 frame
) {
128 Frame
f(Log
.room()->getFrame(frame
));
129 if (f
.position().x
== 999)
131 _position
= f
.position();
132 debugC(5, kDebugLevelActor
, "actor set to frame %d, position %d:%d", f
.index(), _position
.x
, _position
.y
);
135 Common::List
<Actor::Frame
> Actor::findPath(Actor::Frame from
, uint16 to
) {
136 Common::List
<Common::List
<Frame
> > reachable
;
138 Common::List
<Frame
> zero
;
139 zero
.push_back(from
);
140 reachable
.push_back(zero
);
144 Common::List
<Common::List
<Frame
> >::iterator back
= reachable
.end();
146 Common::List
<Frame
>::iterator current
= back
->begin();
147 Common::List
<Frame
> next
;
148 std::ostringstream s
;
149 while (!found
&& current
!= back
->end()) {
150 std::vector
<byte
> nexts
= current
->nexts();
151 for (int i
= 0; i
< 8; i
++)
153 s
<< ", " << int(nexts
[i
]);
154 next
.push_back(Log
.room()->getFrame(nexts
[i
]));
155 if (nexts
[i
] == to
) {
162 reachable
.push_back(next
);
163 debugC(4, kDebugLevelActor
, "reachable on this level:%s", s
.str().c_str());
166 Common::List
<Frame
> path
;
168 Common::List
<Common::List
<Frame
> >::iterator level
= reachable
.end();
170 Common::List
<Frame
>::iterator current
= level
->end();
172 uint16 index
= level
->size() - 1;
175 path
.push_front(*current
);
176 if (*current
== from
)
179 current
= level
->begin();
180 uint16 new_index
= 0;
181 while (index
>= current
->nextCount()) {
182 index
-= current
->nextCount();
192 void Actor::moveTo(uint16 frame
) {
193 Frame cur
= Log
.room()->getFrame(_frame
);
195 Common::List
<Frame
> path
= findPath(cur
, frame
);
197 Common::List
<Frame
>::iterator it
= path
.end();
199 if (it
->index() != frame
) {
200 Common::List
<Frame
> p
;
201 p
.push_back(Log
.room()->getFrame(frame
));
205 std::ostringstream s
;
208 while (it
!= path
.end()) {
209 _framequeue
.push(*it
);
210 s
<< " " << int(it
->index());
214 debugC(3, kDebugLevelActor
, "found path: %s", s
.str().c_str());
219 void Actor::setRoom(uint16 r
, uint16 frame
, uint16 next_frame
) {
223 _nextFrame
= next_frame
;
226 setAnimation(CodePointer(_puppeteer
.mainCodeOffset(), Log
.mainInterpreter()));
229 bool Actor::nextFrame() {
230 if (_framequeue
.empty())
233 Frame next
= _framequeue
.front();
234 Frame current
= Log
.room()->getFrame(_frame
);
236 Direction direction
= next
- current
;
238 debugC(4, kDebugLevelActor
, "switching frames %d -> %d", current
.index(), next
.index());
239 if (turnTo(direction
))
242 // setFrame(_framequeue.front().index());
243 setAnimation(_puppeteer
.moveAnimator(direction
));
244 _frame
= next
.index();
249 bool Actor::turnTo(Direction dir
) {
250 if (dir
== _direction
)
253 Direction d
= _direction
>>dir
;
254 debugC(4, kDebugLevelActor
, "turning %d -> %d >> %d", _direction
, d
, dir
);
255 setAnimation(_puppeteer
.turnAnimator(d
));
259 void Actor::animate() {
260 unless (_puppeteer
.valid())
263 unless (_attentionNeeded
|| _confused
/* || _timedOut*/)
266 debugC(4, kDebugLevelActor
, "attention needed");
273 setAnimation(_nextAnimator
);
278 if (_nextDirection
) {
279 if (turnTo(_nextDirection
))
281 _direction
= _nextDirection
;
282 _nextDirection
= kDirNone
;
283 /* ax = _nextPuppeteer;
287 setAnimation(_puppeteer
.offset());
288 /* } else if (_nextPuppeteer) {
296 if (turnTo(kDirDown
))
298 _direction
= kDirDown
;
304 setAnimation(_puppeteer.offset());
308 Animation::Status
Actor::tick() {
313 if (_room
== Log
.currentRoom()) {
315 if (_debug
) gDebugLevel
+= 3;
316 s
= Animation::tick();
317 if (_debug
) gDebugLevel
-= 3;
323 void Actor::toggleDebug() {
327 void Actor::readHeader(const byte
*code
) {
328 // uint16 segment = READ_LE_UINT16(code + kOffsetCode);
330 _base = Log.mainInterpreter()->rawCode(0);
331 else if (segment == 1)
332 _base = Log.blockInterpreter()->rawCode(0);
333 else error("segment %x", segment);*/
334 _interval
= code
[kOffsetInterval
];
335 _ticksLeft
= READ_LE_UINT16(code
+ kOffsetTicksLeft
);
337 _position
= Common::Point(READ_LE_UINT16(code
+ kOffsetLeft
), READ_LE_UINT16(code
+ kOffsetTop
));
338 uint16 baseOff
= READ_LE_UINT16(code
+ kOffsetCode
);
339 _offset
= READ_LE_UINT16(code
+ kOffsetOffset
);
340 if (_offset
|| baseOff
) {
342 _baseOffset
= baseOff
;
345 _baseOffset
= _offset
= 0;
347 uint16 sprite
= READ_LE_UINT16(code
+ kOffsetMainSprite
);
348 _room
= READ_LE_UINT16(code
+ kOffsetRoom
);
350 debugC(3, kDebugLevelFiles
, "loading %s: interv %d ticks %d z%d pos%d:%d code %d offset %d sprite %d room %d", _debugInfo
, _interval
, _ticksLeft
, _zIndex
, _position
.x
, _position
.y
, baseOff
, _offset
, sprite
, _room
);
352 if (sprite
!= 0xffff)
353 setMainSprite(sprite
);
356 void Actor::callBacks() {
358 Common::Queue
<CodePointer
> cb
= _callBacks
;
364 foreach (RoomCallback
, _roomCallbacks
) {
365 if (_room
== Log
.currentRoom() || !it
->timeout
) {
367 Common::List
<RoomCallback
>::iterator done
= it
;
369 _roomCallbacks
.erase(done
);
375 void Puppeteer::parse(const byte
*data
) {
376 _actorId
= READ_LE_UINT16(data
+ kActorId
);
377 _offset
= READ_LE_UINT16(data
+ kMainCode
);
379 assert (kTurnAnimators
== kMoveAnimators
+ 16);
380 const byte
*d
= data
+ kMoveAnimators
;
381 for (int i
= 0; i
< 16; i
++) {
382 _animators
[i
] = READ_LE_UINT16(d
);
387 static const Direction mixeddirs
[] = {kDirUp
, kDirRight
, kDirDown
, kDirLeft
,
388 kDirUpRight
, kDirDownRight
, kDirDownLeft
, kDirUpLeft
};
390 CodePointer
Puppeteer::moveAnimator(Direction d
) {
391 uint16 off
= mainCodeOffset();
392 for (int i
= 0; i
< 8; i
++) {
393 if (mixeddirs
[i
] == d
)
397 return CodePointer(off
, Log
.mainInterpreter());
400 CodePointer
Puppeteer::turnAnimator(Direction d
) {
401 uint16 off
= mainCodeOffset();
402 for (int i
= 0; i
< 8; i
++) {
403 if (mixeddirs
[i
] == d
)
404 off
= _animators
[i
+ 8];
407 return CodePointer(off
, Log
.mainInterpreter());
410 Actor::Speech::Speech(Actor
*parent
, const Common::String
&text
) : _text(text
), _ticksLeft(20), _actor(parent
) {
411 const Common::Point
&position
= parent
->getSpeechPosition();
412 debugC(1, kDebugLevelActor
, "adding speech \"%s\" for %s at %d:%d", text
.c_str(), parent
->_debugInfo
, position
.x
, position
.y
);
413 _image
= new Innocent::Sprite
;
414 _rect
= Graf
.paintSpeechInBubble(position
, 235, reinterpret_cast<const byte
*>(text
.c_str()), _image
);
417 void Actor::Speech::tick() {
418 unless (_ticksLeft
--) {
421 Log
.runLater(_cb
.pop());
425 Common::Point
Actor::getSpeechPosition() const {
426 Common::Point
speechPosition(_position
);
427 if (_mainSprite
.get()) {
428 speechPosition
.y
-= _mainSprite
.get()->h
- _mainSprite
.get()->_hotPoint
.y
;
429 speechPosition
.x
-= _mainSprite
.get()->_hotPoint
.x
;
431 return speechPosition
;
434 void Actor::paint(Graphics
*g
) {
439 void Actor::Speech::paint(Graphics
*g
) {
443 g
->paint(_image
, Common::Point(_rect
.left
, _rect
.top
), Graphics::kPaintSemiTransparent
| Graphics::kPaintPositionIsTop
);
446 Direction
Actor::Frame::operator-(const Actor::Frame
&other
) const {
447 for (int i
= 0; i
< 8; i
++)
448 if (other
._nexts
[i
] == _index
)
454 Direction
operator>>(Direction _a
, Direction _b
) {
455 assert(sizeof(Direction
) == sizeof(int32
));
474 return *reinterpret_cast<Direction
*>(&a
);
477 template <int opcode
>
478 Animation::Status
Actor::opcodeHandler(){
479 return Animation::opcodeHandler
<opcode
>();
483 void Actor::init_opcodes() {
484 _handlers
[N
] = &Innocent::Actor::opcodeHandler
<N
>;
489 void Actor::init_opcodes
<-1>() {}
491 Animation::Status
Actor::op(byte opcode
) {
492 return (this->*_handlers
[opcode
])();
495 #define OPCODE(n) template<> Animation::Status Actor::opcodeHandler<n>()
498 debugC(1, kDebugLevelActor
, "actor opcode 0x01: I don't know what to do");
507 uint16 off
= shift();
509 debugC(1, kDebugLevelAnimation
, "actor opcode 0x14: jump to 0x%04x if I'm speaking STUB", off
);
511 // also, some check for non-protagonists
517 debugC(1, kDebugLevelAnimation
, "actor opcode 0x15: look at cursor direction if its mode is 'See' STUB");
523 byte val
= embeddedByte();
524 uint16 off
= shift();
526 debugC(1, kDebugLevelAnimation
, "actor opcode 0x16: if look direction is %d then jump to 0x%04x STUB", val
, off
);
532 byte val
= embeddedByte();
533 uint16 off
= shift();
535 debugC(3, kDebugLevelAnimation
, "actor opcode 0x17: if facing (currently %d) is %d then change code to 0x%04x", _direction
, val
, off
);
537 if (val
== _direction
) {
545 uint16 val
= shift();
547 debugC(3, kDebugLevelAnimation
, "actor opcode 0x18: set next animator to %x", val
);
554 byte dir
= embeddedByte();
556 debugC(3, kDebugLevelAnimation
, "actor opcode 0x23: face %d", dir
);
563 _direction
= kDirUpRight
;
566 _direction
= kDirRight
;
569 _direction
= kDirDownRight
;
572 _direction
= kDirDown
;
575 _direction
= kDirDownLeft
;
578 _direction
= kDirLeft
;
581 _direction
= kDirUpLeft
;
591 byte dir
= embeddedByte();
593 debugC(3, kDebugLevelAnimation
, "actor opcode 0x24: set attention needed flag to %d", dir
);
594 _attentionNeeded
= true;
599 } // end of namespace