SetupWishCore: fixed table lookup being out of bounds for the wisest pcs
[gemrb.git] / gemrb / core / ScriptedAnimation.cpp
blobe372481a4898d827354c046d0567dec3d7cec60a
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"
26 #include "win32def.h"
28 #include "Audio.h"
29 #include "Game.h"
30 #include "GameData.h"
31 #include "Interface.h"
32 #include "Video.h"
34 #define ILLEGAL 0 //
35 #define ONE 1 //hold
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()
62 Init();
65 void ScriptedAnimation::Init()
67 cover = NULL;
68 memset(anims,0,sizeof(anims));
69 palette = NULL;
70 sounds[0][0] = 0;
71 sounds[1][0] = 0;
72 sounds[2][0] = 0;
73 memset(&Tint,0,sizeof(Tint));
74 Transparency = 0;
75 Fade = 0;
76 SequenceFlags = 0;
77 XPos = YPos = ZPos = 0;
78 FrameRate = 15;
79 FaceTarget = 0;
80 Orientation = 0;
81 Dither = 0;
82 Duration = 0xffffffff;
83 justCreated = true;
84 PaletteName[0]=0;
85 twin = NULL;
86 Phase = P_NOTINITED;
87 effect_owned = false;
88 active = true;
91 void ScriptedAnimation::Override(ScriptedAnimation *templ)
93 Transparency = templ->Transparency;
94 SequenceFlags = templ->SequenceFlags;
95 XPos = templ->XPos;
96 YPos = templ->YPos;
97 ZPos = templ->ZPos;
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) {
131 cCount=1;
134 int type = ctypes[cCount];
135 switch(gettwin) {
136 case 2:
137 if (type==TWO) {
138 type=ONE|DOUBLE;
140 gettwin=0;
141 break;
142 case 1:
143 type=ONE|DOUBLE;
144 break;
147 if (type==ILLEGAL) {
148 cCount=1;
149 type=ONE;
151 else if (type&DOUBLE) {
152 cCount/=2;
155 //these fields mark that the anim cycles should 'follow' the target's orientation
156 if (type&FIVE) {
157 FaceTarget = 5;
158 cCount = MAX_ORIENT*(type&3);
159 } else if (type&NINE) {
160 FaceTarget = 9;
161 cCount = MAX_ORIENT*(type&3);
162 } else {
163 FaceTarget = 0;
166 for(unsigned int i=0;i<cCount;i++) {
167 bool mirror = false;
168 int c = i;
169 int p = i;
170 if (type&DOUBLE) {
171 c<<=1;
172 if (gettwin) c++;
173 } else if (type&FIVE) {
174 c=SixteenToFive[c];
175 if ((i&15)>=5) mirror = true;
176 } else if (type&NINE) {
177 c=SixteenToNine[c];
178 if ((i&15)>=9) mirror = true;
179 } else if (!(type&SEVENEYES)) {
180 p*=MAX_ORIENT;
183 anims[p] = af->GetCycle( (ieByte) c );
184 if (anims[p]) {
185 anims[p]->pos=0;
186 if (mirror) {
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];
200 anims[p_onset]=NULL;
202 //onset and release phases are to be played only once
203 if (anims[p_onset])
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
211 if (gettwin) {
212 return;
214 if (type&DOUBLE) {
215 twin = new ScriptedAnimation();
216 twin->LoadAnimationFactory(af, 1);
218 SetPhase(P_ONSET);
221 /* Creating animation from VVC */
222 ScriptedAnimation::ScriptedAnimation(DataStream* stream, bool autoFree)
224 Init();
225 if (!stream) {
226 return;
229 char Signature[8];
231 stream->Read( Signature, 8);
232 if (strncmp( Signature, "VVC V1.0", 8 ) != 0) {
233 printf( "Not a valid VVC File\n" );
234 if (autoFree)
235 delete( stream );
236 return;
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 );
248 ieDword tmp;
249 stream->ReadDword( &tmp );
250 XPos = (signed) tmp;
251 stream->ReadDword( &tmp ); //this affects visibility
252 ZPos = (signed) tmp;
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
258 YPos = (signed) tmp;
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)
277 if (seq2>0) seq2--;
278 if (seq3>0) seq3--;
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;
291 int c = seq1;
292 if (phases) {
293 switch (FaceTarget) {
294 case 5:
295 c=SixteenToFive[i];
296 break;
297 case 9:
298 c=SixteenToNine[i];
299 break;
300 case 16:
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;
304 break;
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;
318 if (c || !phases) {
319 switch (FaceTarget) {
320 case 5:
321 c=SixteenToFive[i];
322 break;
323 case 9:
324 c=SixteenToNine[i];
325 break;
326 case 16:
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;
330 break;
332 anims[p_hold] = af->GetCycle( c );
333 if (anims[p_hold]) {
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;
344 c = seq3;
345 if (c) {
346 switch (FaceTarget) {
347 case 5:
348 c=SixteenToFive[i];
349 break;
350 case 9:
351 c=SixteenToNine[i];
352 break;
353 case 16:
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;
357 break;
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;
369 PreparePalette();
372 SetPhase(P_ONSET);
374 if (autoFree) {
375 delete( stream );
379 ScriptedAnimation::~ScriptedAnimation(void)
381 for(unsigned int i=0;i<3*MAX_ORIENT;i++) {
382 if (anims[i]) {
383 delete( anims[i] );
386 gamedata->FreePalette(palette, PaletteName);
388 if (cover) {
389 SetSpriteCover(NULL);
391 if (twin) {
392 delete twin;
396 void ScriptedAnimation::SetPhase(int arg)
398 if (arg>=P_ONSET && arg<=P_RELEASE) {
399 Phase = arg;
401 SetSpriteCover(NULL);
402 if (twin) {
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++) {
419 if (anims[i]) {
420 anims[i]->Flags |= S_ANI_PLAYONCE;
423 if (twin) {
424 twin->PlayOnce();
428 void ScriptedAnimation::SetFullPalette(const ieResRef PaletteResRef)
430 gamedata->FreePalette(palette, PaletteName);
431 palette=gamedata->GetPalette(PaletteResRef);
432 memcpy(PaletteName, PaletteResRef, sizeof(PaletteName) );
433 if (twin) {
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
449 #define PALSIZE 12
450 static Color NewPal[PALSIZE];
452 void ScriptedAnimation::SetPalette(int gradient, int start)
454 //get a palette
455 GetPaletteCopy();
456 if (!palette)
457 return;
458 //default start
459 if (start==-1) {
460 start=4;
462 core->GetPalette( gradient&255, PALSIZE, NewPal );
464 memcpy( &palette->col[start], NewPal, PALSIZE*sizeof( Color ) );
465 if (twin) {
466 twin->SetPalette(gradient, start);
470 int ScriptedAnimation::GetCurrentFrame()
472 Animation *anim = anims[P_HOLD*MAX_ORIENT];
473 if (anim) {
474 return anim->GetCurrentFrame();
476 return 0;
479 ieDword ScriptedAnimation::GetSequenceDuration(ieDword multiplier)
481 //P_HOLD * MAX_ORIENT == MAX_ORIENT
482 Animation *anim = anims[P_HOLD*MAX_ORIENT];
483 if (anim) {
484 return anim->GetFrameCount()*multiplier/FrameRate;
486 return 0;
489 void ScriptedAnimation::SetDefaultDuration(ieDword duration)
491 if (Duration==0xffffffff) {
492 Duration = duration;
494 if (twin) {
495 twin->Duration=Duration;
499 void ScriptedAnimation::SetOrientation(int orientation)
501 if (orientation==-1) {
502 return;
504 if(FaceTarget) {
505 Orientation=(ieByte) orientation;
506 } else {
507 Orientation = 0;
509 if (twin) {
510 twin->Orientation=Orientation;
514 bool ScriptedAnimation::HandlePhase(Sprite2D *&frame)
516 if (justCreated) {
517 if (Phase == P_NOTINITED) {
518 printMessage("ScriptedAnimation", "Not fully initialised VVC!\n", LIGHT_RED);
519 return true;
521 justCreated = false;
522 if (Duration!=0xffffffff) {
523 Duration += core->GetGame()->GameTime;
526 if (!anims[P_ONSET*MAX_ORIENT+Orientation]) {
527 Phase = P_HOLD;
529 retry:
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) {
537 PlayOnce();
540 if (!anims[Phase*MAX_ORIENT+Orientation]) {
541 if (Phase>=P_RELEASE) {
542 return true;
544 Phase++;
545 goto retry;
547 frame = anims[Phase*MAX_ORIENT+Orientation]->NextFrame();
549 //explicit duration
550 if (Phase==P_HOLD) {
551 if (core->GetGame()->GameTime>Duration) {
552 Phase++;
553 goto retry;
556 //automatically slip from onset to hold to release
557 if (!frame || anims[Phase*MAX_ORIENT+Orientation]->endReached) {
558 if (Phase>=P_RELEASE) {
559 return true;
561 //this section implements the freeze fading effect (see ice dagger)
562 if (frame && Fade && Tint.a && (Phase==P_HOLD) ) {
563 if (Tint.a<=Fade) {
564 return true;
566 Tint.a-=Fade;
567 return false;
569 Phase++;
570 goto retry;
572 return false;
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)
578 if (FaceTarget) {
579 SetOrientation(orientation);
582 // not sure
583 if (twin) {
584 twin->Draw(screen, Pos, p_tint, area, dither, -1);
587 Video *video = core->GetVideoDriver();
589 Sprite2D* frame;
591 if (HandlePhase(frame)) {
592 return true;
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;
602 Color tint = Tint;
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
606 //on the other hand
608 if (Transparency & IE_VVC_GREYSCALE) {
609 flag |= BLIT_GREY;
612 if (Transparency & IE_VVC_RED_TINT) {
613 flag |= BLIT_RED;
616 if (Transparency & BLIT_TINTED) {
617 flag |= BLIT_TINTED;
620 if ((Transparency & IE_VVC_TINT)==IE_VVC_TINT) {
621 tint = p_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);
629 } else {
630 if (!cover || (Dither!=dither) || (!cover->Covers(cx, cy, frame->XPos, frame->YPos, frame->Width, frame->Height)) ) {
631 Dither = dither;
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);
640 return false;
643 void ScriptedAnimation::PreparePalette()
645 if (Transparency&IE_VVC_BLENDED) {
646 GetPaletteCopy();
647 if (!palette)
648 return;
649 if (!palette->alpha) {
650 palette->CreateShadedAlphaChannel();
655 void ScriptedAnimation::SetBlend()
657 Transparency |= IE_VVC_BLENDED;
658 PreparePalette();
659 if (twin)
660 twin->SetBlend();
663 void ScriptedAnimation::SetFade(ieByte initial, int speed)
665 Tint.r=255;
666 Tint.g=255;
667 Tint.b=255;
668 Tint.a=initial;
669 Fade=speed;
670 Transparency|=BLIT_TINTED;
673 void ScriptedAnimation::GetPaletteCopy()
675 if (palette)
676 return;
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++) {
680 if (anims[i]) {
681 Sprite2D* spr = anims[i]->GetFrame(0);
682 if (spr) {
683 palette = spr->GetPalette()->Copy();
684 //we need only one palette, so break here
685 break;
691 void ScriptedAnimation::AlterPalette(const RGBModifier& mod)
693 GetPaletteCopy();
694 if (!palette)
695 return;
696 palette->SetupGlobalRGBModification(palette,mod);
697 if (twin) {
698 twin->AlterPalette(mod);
702 ScriptedAnimation *ScriptedAnimation::DetachTwin()
704 if (!twin) {
705 return NULL;
707 ScriptedAnimation * ret = twin;
708 //ret->YPos+=ret->ZPos+1;
709 if (ret->ZPos>=0) {
710 ret->ZPos=-1;
712 twin=NULL;
713 return ret;