1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003-2005 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 // This class handles VVC files of BG2/ToB and converts BAM files to the
22 // common internal animation format on the fly.
24 #include "ScriptedAnimation.h"
31 #include "Interface.h"
36 #define TWO 2 //onset + hold
37 #define THREE 3 //onset + hold + release
38 #define DOUBLE 4 //has twin (pst)
39 #define FIVE 8 //five faces (orientation)
40 #define NINE 16 //nine faces (orientation)
41 #define SEVENEYES 32 //special hack for seven eyes
43 #define MAX_CYCLE_TYPE 16
44 static const ieByte ctypes
[MAX_CYCLE_TYPE
]={
45 ILLEGAL
, ONE
, TWO
, THREE
, TWO
|DOUBLE
, ONE
|FIVE
, THREE
|DOUBLE
, ILLEGAL
,
46 SEVENEYES
, ONE
|NINE
, TWO
|FIVE
, ILLEGAL
, ILLEGAL
, ILLEGAL
, ILLEGAL
,THREE
|FIVE
,
49 static const ieByte SixteenToNine
[3*MAX_ORIENT
]={
50 0, 1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1,
51 9,10,11,12,13,14,15,16,17,16,15,14,13,12,11,10,
52 18,19,20,21,22,23,24,25,26,25,24,23,22,21,20,19
54 static const ieByte SixteenToFive
[3*MAX_ORIENT
]={
55 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 3, 3, 2, 2, 1, 1,
56 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 8, 8, 7, 7, 6, 6,
57 10,10,11,11,12,12,13,13,14,14,13,13,12,12,11,11
60 ScriptedAnimation::ScriptedAnimation()
65 void ScriptedAnimation::Init()
68 memset(anims
,0,sizeof(anims
));
73 memset(&Tint
,0,sizeof(Tint
));
77 XPos
= YPos
= ZPos
= 0;
82 Duration
= 0xffffffff;
91 void ScriptedAnimation::Override(ScriptedAnimation
*templ
)
93 Transparency
= templ
->Transparency
;
94 SequenceFlags
= templ
->SequenceFlags
;
98 FrameRate
= templ
->FrameRate
;
99 FaceTarget
= templ
->FaceTarget
;
100 for (unsigned int i
=0;i
<3;i
++) {
101 memcpy(sounds
[i
],templ
->sounds
[i
],sizeof(ieResRef
));
103 if (templ
->Duration
!=0xffffffff) {
104 SetDefaultDuration(templ
->Duration
);
106 if (templ
->PaletteName
[0]) {
107 SetFullPalette(templ
->PaletteName
);
111 //prepare the animation before doing anything
112 void ScriptedAnimation::PrepareAnimation(Animation
*anim
, ieDword Transparency
)
114 if (Transparency
&IE_VVC_MIRRORX
) {
115 anim
->MirrorAnimation();
117 if (Transparency
&IE_VVC_MIRRORY
) {
118 anim
->MirrorAnimationVert();
122 /* Creating animation from BAM */
123 void ScriptedAnimation::LoadAnimationFactory(AnimationFactory
*af
, int gettwin
)
125 //getcycle returns NULL if there is no such cycle
126 //special case, PST double animations
128 memcpy(ResName
, af
->ResRef
, sizeof(ResName
) );
129 unsigned int cCount
=af
->GetCycleCount();
130 if (cCount
>=MAX_CYCLE_TYPE
) {
134 int type
= ctypes
[cCount
];
151 else if (type
&DOUBLE
) {
155 //these fields mark that the anim cycles should 'follow' the target's orientation
158 cCount
= MAX_ORIENT
*(type
&3);
159 } else if (type
&NINE
) {
161 cCount
= MAX_ORIENT
*(type
&3);
166 for(unsigned int i
=0;i
<cCount
;i
++) {
173 } else if (type
&FIVE
) {
175 if ((i
&15)>=5) mirror
= true;
176 } else if (type
&NINE
) {
178 if ((i
&15)>=9) mirror
= true;
179 } else if (!(type
&SEVENEYES
)) {
183 anims
[p
] = af
->GetCycle( (ieByte
) c
);
187 anims
[p
]->MirrorAnimation();
189 anims
[p
]->gameAnimation
=true;
193 for (unsigned int o
= 0; o
<MAX_ORIENT
; o
++) {
194 unsigned int p_hold
= P_HOLD
*MAX_ORIENT
+o
;
195 unsigned int p_onset
= P_ONSET
*MAX_ORIENT
+o
;
196 unsigned int p_release
= P_RELEASE
*MAX_ORIENT
+o
;
197 //if there is no hold anim, move the onset anim there
198 if (!anims
[p_hold
]) {
199 anims
[p_hold
]=anims
[p_onset
];
202 //onset and release phases are to be played only once
204 anims
[p_onset
]->Flags
|= S_ANI_PLAYONCE
;
205 if (anims
[p_release
])
206 anims
[p_release
]->Flags
|= S_ANI_PLAYONCE
;
208 //we are getting a twin, no need of going further,
209 //if there is any more common initialisation, it should
210 //go above this point
215 twin
= new ScriptedAnimation();
216 twin
->LoadAnimationFactory(af
, 1);
221 /* Creating animation from VVC */
222 ScriptedAnimation::ScriptedAnimation(DataStream
* stream
, bool autoFree
)
231 stream
->Read( Signature
, 8);
232 if (strncmp( Signature
, "VVC V1.0", 8 ) != 0) {
233 printf( "Not a valid VVC File\n" );
238 ieResRef Anim1ResRef
;
239 ieDword seq1
, seq2
, seq3
;
240 stream
->ReadResRef( Anim1ResRef
);
241 //there is no proof it is a second resref
242 //stream->ReadResRef( Anim2ResRef );
243 stream
->Seek( 8, GEM_CURRENT_POS
);
244 stream
->ReadDword( &Transparency
);
245 stream
->Seek( 4, GEM_CURRENT_POS
);
246 stream
->ReadDword( &SequenceFlags
);
247 stream
->Seek( 4, GEM_CURRENT_POS
);
249 stream
->ReadDword( &tmp
);
251 stream
->ReadDword( &tmp
); //this affects visibility
253 stream
->Seek( 4, GEM_CURRENT_POS
);
254 stream
->ReadDword( &FrameRate
);
255 stream
->ReadDword( &FaceTarget
);
256 stream
->Seek( 16, GEM_CURRENT_POS
);
257 stream
->ReadDword( &tmp
); //this doesn't affect visibility
259 stream
->Seek( 12, GEM_CURRENT_POS
);
260 stream
->ReadDword( &Duration
);
261 stream
->Seek( 8, GEM_CURRENT_POS
);
262 stream
->ReadDword( &seq1
);
263 if (seq1
>0) seq1
--; //hack but apparently it works this way
264 stream
->ReadDword( &seq2
);
265 stream
->Seek( 8, GEM_CURRENT_POS
);
266 stream
->ReadResRef( sounds
[P_ONSET
] );
267 stream
->ReadResRef( sounds
[P_HOLD
] );
268 stream
->Seek( 8, GEM_CURRENT_POS
);
269 stream
->ReadDword( &seq3
);
270 stream
->ReadResRef( sounds
[P_RELEASE
] );
272 //if there are no separate phases, then fill the p_hold fields
273 bool phases
= (seq2
|| seq3
);
275 // hacks for seq2/seq3, same as for seq1 above
276 // (not sure if seq3 is needed)
280 if (SequenceFlags
&IE_VVC_BAM
) {
281 AnimationFactory
* af
= ( AnimationFactory
* )
282 gamedata
->GetFactoryResource( Anim1ResRef
, IE_BAM_CLASS_ID
);
283 //no idea about vvc phases, i think they got no endphase?
284 //they certainly got onset and hold phases
285 //the face target flag should be handled too
286 for (int i
=0;i
<MAX_ORIENT
;i
++) {
287 unsigned int p_hold
= P_HOLD
*MAX_ORIENT
+i
;
288 unsigned int p_onset
= P_ONSET
*MAX_ORIENT
+i
;
289 unsigned int p_release
= P_RELEASE
*MAX_ORIENT
+i
;
293 switch (FaceTarget
) {
301 //this is an uglybugly hack, i still have to
302 //figure out what 'FaceTarget' really is
303 if ( (int) af
->GetCycleCount()>i
) c
=i
;
306 anims
[p_onset
] = af
->GetCycle( c
);
307 if (anims
[p_onset
]) {
308 PrepareAnimation(anims
[p_onset
], Transparency
);
309 //creature anims may start at random position, vvcs always start on 0
310 anims
[p_onset
]->pos
=0;
311 //vvcs are always paused
312 anims
[p_onset
]->gameAnimation
=true;
313 anims
[p_onset
]->Flags
|= S_ANI_PLAYONCE
;
317 c
= phases
? seq2
: seq1
;
319 switch (FaceTarget
) {
327 //this is an uglybugly hack, i still have to
328 //figure out what 'FaceTarget' really is
329 if ((int) af
->GetCycleCount()>i
) c
=i
;
332 anims
[p_hold
] = af
->GetCycle( c
);
334 PrepareAnimation(anims
[p_hold
], Transparency
);
336 anims
[p_hold
]->pos
=0;
337 anims
[p_hold
]->gameAnimation
=true;
338 if (!(SequenceFlags
&IE_VVC_LOOP
) ) {
339 anims
[p_hold
]->Flags
|= S_ANI_PLAYONCE
;
346 switch (FaceTarget
) {
354 //this is an uglybugly hack, i still have to
355 //figure out what 'FaceTarget' really is
356 if ( (int) af
->GetCycleCount()>i
) c
=i
;
359 anims
[p_release
] = af
->GetCycle( ( unsigned char ) c
);
360 if (anims
[p_release
]) {
361 PrepareAnimation(anims
[p_release
], Transparency
);
363 anims
[p_release
]->pos
=0;
364 anims
[p_release
]->gameAnimation
=true;
365 anims
[p_release
]->Flags
|= S_ANI_PLAYONCE
;
379 ScriptedAnimation::~ScriptedAnimation(void)
381 for(unsigned int i
=0;i
<3*MAX_ORIENT
;i
++) {
386 gamedata
->FreePalette(palette
, PaletteName
);
389 SetSpriteCover(NULL
);
396 void ScriptedAnimation::SetPhase(int arg
)
398 if (arg
>=P_ONSET
&& arg
<=P_RELEASE
) {
401 SetSpriteCover(NULL
);
403 twin
->SetPhase(Phase
);
407 void ScriptedAnimation::SetSound(int arg
, const ieResRef sound
)
409 if (arg
>=P_ONSET
&& arg
<=P_RELEASE
) {
410 memcpy(sounds
[arg
],sound
,sizeof(sound
));
412 //no need to call the twin
415 void ScriptedAnimation::PlayOnce()
417 SequenceFlags
&=~IE_VVC_LOOP
;
418 for (unsigned int i
=0;i
<3*MAX_ORIENT
;i
++) {
420 anims
[i
]->Flags
|= S_ANI_PLAYONCE
;
428 void ScriptedAnimation::SetFullPalette(const ieResRef PaletteResRef
)
430 gamedata
->FreePalette(palette
, PaletteName
);
431 palette
=gamedata
->GetPalette(PaletteResRef
);
432 memcpy(PaletteName
, PaletteResRef
, sizeof(PaletteName
) );
434 twin
->SetFullPalette(PaletteResRef
);
438 void ScriptedAnimation::SetFullPalette(int idx
)
440 ieResRef PaletteResRef
;
442 //make sure this field is zero terminated, or strlwr will run rampant!!!
443 snprintf(PaletteResRef
,sizeof(PaletteResRef
),"%.7s%d",ResName
, idx
);
444 strnlwrcpy(PaletteResRef
,PaletteResRef
,8);
445 SetFullPalette(PaletteResRef
);
446 //no need to call twin
450 static Color NewPal
[PALSIZE
];
452 void ScriptedAnimation::SetPalette(int gradient
, int start
)
462 core
->GetPalette( gradient
&255, PALSIZE
, NewPal
);
464 memcpy( &palette
->col
[start
], NewPal
, PALSIZE
*sizeof( Color
) );
466 twin
->SetPalette(gradient
, start
);
470 int ScriptedAnimation::GetCurrentFrame()
472 Animation
*anim
= anims
[P_HOLD
*MAX_ORIENT
];
474 return anim
->GetCurrentFrame();
479 ieDword
ScriptedAnimation::GetSequenceDuration(ieDword multiplier
)
481 //P_HOLD * MAX_ORIENT == MAX_ORIENT
482 Animation
*anim
= anims
[P_HOLD
*MAX_ORIENT
];
484 return anim
->GetFrameCount()*multiplier
/FrameRate
;
489 void ScriptedAnimation::SetDefaultDuration(ieDword duration
)
491 if (Duration
==0xffffffff) {
495 twin
->Duration
=Duration
;
499 void ScriptedAnimation::SetOrientation(int orientation
)
501 if (orientation
==-1) {
505 Orientation
=(ieByte
) orientation
;
510 twin
->Orientation
=Orientation
;
514 bool ScriptedAnimation::HandlePhase(Sprite2D
*&frame
)
517 if (Phase
== P_NOTINITED
) {
518 printMessage("ScriptedAnimation", "Not fully initialised VVC!\n", LIGHT_RED
);
522 if (Duration
!=0xffffffff) {
523 Duration
+= core
->GetGame()->GameTime
;
526 if (!anims
[P_ONSET
*MAX_ORIENT
+Orientation
]) {
530 if (sounds
[Phase
][0] != 0) {
531 core
->GetAudioDrv()->Play( sounds
[Phase
] );
535 // if we're looping forever and we didn't get 'bumped' by an effect
536 if (effect_owned
&& (SequenceFlags
&IE_VVC_LOOP
) && Duration
==0xffffffff && !active
) {
540 if (!anims
[Phase
*MAX_ORIENT
+Orientation
]) {
541 if (Phase
>=P_RELEASE
) {
547 frame
= anims
[Phase
*MAX_ORIENT
+Orientation
]->NextFrame();
551 if (core
->GetGame()->GameTime
>Duration
) {
556 //automatically slip from onset to hold to release
557 if (!frame
|| anims
[Phase
*MAX_ORIENT
+Orientation
]->endReached
) {
558 if (Phase
>=P_RELEASE
) {
561 //this section implements the freeze fading effect (see ice dagger)
562 if (frame
&& Fade
&& Tint
.a
&& (Phase
==P_HOLD
) ) {
575 //it is not sure if we need tint at all
576 bool ScriptedAnimation::Draw(const Region
&screen
, const Point
&Pos
, const Color
&p_tint
, Map
*area
, int dither
, int orientation
)
579 SetOrientation(orientation
);
584 twin
->Draw(screen
, Pos
, p_tint
, area
, dither
, -1);
587 Video
*video
= core
->GetVideoDriver();
591 if (HandlePhase(frame
)) {
595 ieDword flag
= BLIT_TRANSSHADOW
;
596 //transferring flags to SDLdriver, this will have to be consolidated later
598 if (Transparency
& IE_VVC_TRANSPARENT
) {
599 flag
|= BLIT_HALFTRANS
;
604 //darken, greyscale, red tint are probably not needed if the global tint works
605 //these are used in the original engine to implement weather/daylight effects
608 if (Transparency
& IE_VVC_GREYSCALE
) {
612 if (Transparency
& IE_VVC_RED_TINT
) {
616 if (Transparency
& BLIT_TINTED
) {
620 if ((Transparency
& IE_VVC_TINT
)==IE_VVC_TINT
) {
624 int cx
= Pos
.x
+ XPos
;
625 int cy
= Pos
.y
- ZPos
+ YPos
;
627 if( SequenceFlags
&IE_VVC_NOCOVER
) {
628 if (cover
) SetSpriteCover(NULL
);
630 if (!cover
|| (Dither
!=dither
) || (!cover
->Covers(cx
, cy
, frame
->XPos
, frame
->YPos
, frame
->Width
, frame
->Height
)) ) {
632 Animation
*anim
= anims
[Phase
*MAX_ORIENT
+Orientation
];
633 SetSpriteCover(area
->BuildSpriteCover(cx
, cy
, -anim
->animArea
.x
,
634 -anim
->animArea
.y
, anim
->animArea
.w
, anim
->animArea
.h
, dither
) );
636 assert(cover
->Covers(cx
, cy
, frame
->XPos
, frame
->YPos
, frame
->Width
, frame
->Height
));
639 video
->BlitGameSprite( frame
, cx
+ screen
.x
, cy
+ screen
.y
, flag
, tint
, cover
, palette
, &screen
);
643 void ScriptedAnimation::PreparePalette()
645 if (Transparency
&IE_VVC_BLENDED
) {
649 if (!palette
->alpha
) {
650 palette
->CreateShadedAlphaChannel();
655 void ScriptedAnimation::SetBlend()
657 Transparency
|= IE_VVC_BLENDED
;
663 void ScriptedAnimation::SetFade(ieByte initial
, int speed
)
670 Transparency
|=BLIT_TINTED
;
673 void ScriptedAnimation::GetPaletteCopy()
677 //it is not sure that the first position will have a resource in it
678 //therefore the cycle
679 for (unsigned int i
=0;i
<3*MAX_ORIENT
;i
++) {
681 Sprite2D
* spr
= anims
[i
]->GetFrame(0);
683 palette
= spr
->GetPalette()->Copy();
684 //we need only one palette, so break here
691 void ScriptedAnimation::AlterPalette(const RGBModifier
& mod
)
696 palette
->SetupGlobalRGBModification(palette
,mod
);
698 twin
->AlterPalette(mod
);
702 ScriptedAnimation
*ScriptedAnimation::DetachTwin()
707 ScriptedAnimation
* ret
= twin
;
708 //ret->YPos+=ret->ZPos+1;