Skip to content

Commit 2ca6406

Browse files
author
Morgan Christiansson
committed
Palette cycling animation
1 parent 49ab5db commit 2ca6406

8 files changed

Lines changed: 326 additions & 150 deletions

File tree

CGame_Render.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ void CGame::Render()
5353
return;
5454
}
5555

56-
// render the map if active
56+
// update palette cycling animations before rendering the map
5757
if(MapObj && MapObj->isActive())
5858
{
59+
if(MapObj->getMap())
60+
CSurface::UpdatePaletteAnimations(MapObj->getMap()->type);
5961
CSurface::Draw(Surf_Display, MapObj->getSurface(), 0, 0);
6062
std::array<char, 100> textBuffer;
6163
// text for x and y of vertex (shown in upper left corner)

CIO/CFile.cpp

Lines changed: 174 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -350,143 +350,208 @@ bool CFile::read_lbm(FILE* fp, const boost::filesystem::path& filepath)
350350
/* READ SECOND CHUNK "CMAP" */
351351

352352
// chunk-identifier (4 Bytes)
353-
// search for the "CMAP" and skip other chunk-types
354-
while(!feof(fp))
353+
// search for the "CMAP" and skip/parse other chunk-types
355354
{
356-
CHECK_READ(libendian::read(chunk_identifier.data(), 4, fp));
355+
// Local collection of CRNG (palette animation) chunks
356+
std::vector<PaletteAnimData> crngData;
357357

358-
if(strcmp(chunk_identifier.data(), "CMAP") == 0)
359-
break;
360-
else
361-
{
358+
// Helper to parse a CRNG chunk (caller already read chunk ID, we read length+data)
359+
auto readCRNG = [&]() -> bool {
362360
Uint32 chunkLen;
363-
CHECK_READ(libendian::be_read_ui(&chunkLen, fp));
364-
if(chunkLen & 1)
365-
chunkLen++;
366-
fseek(fp, chunkLen, SEEK_CUR);
367-
}
368-
}
369-
if(feof(fp))
370-
return false;
371-
// length of data block
372-
CHECK_READ(libendian::be_read_ui(&length, fp));
373-
// must be 768 (RGB = 3 Byte x 256 Colors)
374-
if(length != 3u * 256u)
375-
return false;
376-
// palette
377-
for(auto& color : colors)
378-
{
379-
CHECK_READ(libendian::read(&color.r, 1, fp));
380-
CHECK_READ(libendian::read(&color.g, 1, fp));
381-
CHECK_READ(libendian::read(&color.b, 1, fp));
382-
}
383-
384-
/* READ THIRD CHUNK "BODY" */
361+
if(!libendian::be_read_ui(&chunkLen, fp))
362+
return false;
363+
if(chunkLen >= 8)
364+
{
365+
PaletteAnimData anim;
366+
uint16_t padding, rate, flags;
367+
if(!libendian::be_read_us(&padding, fp))
368+
return false;
369+
if(!libendian::be_read_us(&rate, fp))
370+
return false;
371+
if(!libendian::be_read_us(&flags, fp))
372+
return false;
373+
uint8_t firstClr, lastClr;
374+
if(!libendian::read(&firstClr, 1, fp))
375+
return false;
376+
if(!libendian::read(&lastClr, 1, fp))
377+
return false;
378+
anim.isActive = (flags & 1) != 0;
379+
anim.moveUp = (flags & 2) != 0;
380+
if(rate)
381+
anim.isActive = anim.moveUp = true;
382+
anim.rate = rate;
383+
anim.firstClr = firstClr;
384+
anim.lastClr = lastClr;
385+
anim.currentOffset = 0;
386+
anim.lastUpdateTime = SDL_GetTicks();
387+
crngData.push_back(anim);
388+
if(chunkLen > 8)
389+
{
390+
uint32_t remaining = chunkLen - 8;
391+
if(remaining & 1)
392+
remaining++;
393+
fseek(fp, remaining, SEEK_CUR);
394+
} else if(chunkLen & 1)
395+
fseek(fp, 1, SEEK_CUR);
396+
} else
397+
{
398+
if(chunkLen & 1)
399+
chunkLen++;
400+
fseek(fp, chunkLen, SEEK_CUR);
401+
}
402+
return true;
403+
};
385404

386-
// chunk-identifier (4 Bytes)
387-
// search for the "BODY" and skip other chunk-types
388-
while(!feof(fp))
389-
{
390-
CHECK_READ(libendian::read(chunk_identifier.data(), 4, fp));
405+
while(!feof(fp))
406+
{
407+
CHECK_READ(libendian::read(chunk_identifier.data(), 4, fp));
391408

392-
if(strcmp(chunk_identifier.data(), "BODY") == 0)
393-
break;
394-
else
409+
if(strcmp(chunk_identifier.data(), "CMAP") == 0)
410+
break;
411+
else if(strcmp(chunk_identifier.data(), "CRNG") == 0)
412+
{
413+
CHECK_READ(readCRNG());
414+
} else
415+
{
416+
Uint32 chunkLen;
417+
CHECK_READ(libendian::be_read_ui(&chunkLen, fp));
418+
if(chunkLen & 1)
419+
chunkLen++;
420+
fseek(fp, chunkLen, SEEK_CUR);
421+
}
422+
}
423+
if(feof(fp))
424+
return false;
425+
// length of data block
426+
CHECK_READ(libendian::be_read_ui(&length, fp));
427+
// must be 768 (RGB = 3 Byte x 256 Colors)
428+
if(length != 3u * 256u)
429+
return false;
430+
// palette
431+
for(auto& color : colors)
395432
{
396-
Uint32 chunkLen;
397-
CHECK_READ(libendian::be_read_ui(&chunkLen, fp));
398-
if(chunkLen & 1)
399-
chunkLen++;
400-
fseek(fp, chunkLen, SEEK_CUR);
433+
CHECK_READ(libendian::read(&color.r, 1, fp));
434+
CHECK_READ(libendian::read(&color.g, 1, fp));
435+
CHECK_READ(libendian::read(&color.b, 1, fp));
401436
}
402-
}
403-
if(feof(fp))
404-
return false;
405-
// length of data block
406-
CHECK_READ(libendian::be_read_ui(&length, fp));
407437

408-
// now we are ready to read the picture lines and fill the surface, so lets create one
409-
if(!(bmpArray->surface = makePalSurface(bmpArray->w, bmpArray->h, colors)))
410-
{
411-
std::cerr << "Failed to create surface: " << SDL_GetError() << std::endl;
412-
return false;
413-
}
438+
/* READ THIRD CHUNK "BODY" */
414439

415-
if(compression_flag == 0)
416-
{
417-
// picture uncompressed
418-
// main loop for reading picture lines
419-
for(Position pos{0, 0}; pos.y < bmpArray->h; pos.y++)
440+
// chunk-identifier (4 Bytes)
441+
// search for the "BODY" and skip/parse other chunk-types
442+
while(!feof(fp))
420443
{
421-
// loop for reading pixels of the actual picture line
422-
for(pos.x = 0; pos.x < bmpArray->w; pos.x++)
444+
CHECK_READ(libendian::read(chunk_identifier.data(), 4, fp));
445+
446+
if(strcmp(chunk_identifier.data(), "BODY") == 0)
447+
break;
448+
else if(strcmp(chunk_identifier.data(), "CRNG") == 0)
423449
{
424-
// read color value (1 Byte)
425-
CHECK_READ(libendian::read(&color_value, 1, fp));
426-
// draw
427-
CSurface::DrawPixel_Color(bmpArray->surface.get(), pos, (Uint32)color_value);
450+
CHECK_READ(readCRNG());
451+
} else
452+
{
453+
Uint32 chunkLen;
454+
CHECK_READ(libendian::be_read_ui(&chunkLen, fp));
455+
if(chunkLen & 1)
456+
chunkLen++;
457+
fseek(fp, chunkLen, SEEK_CUR);
428458
}
429459
}
460+
if(feof(fp))
461+
return false;
462+
// length of data block
463+
CHECK_READ(libendian::be_read_ui(&length, fp));
430464

431-
} else if(compression_flag == 1)
432-
{
433-
// picture compressed
465+
// now we are ready to read the picture lines and fill the surface, so lets create one
466+
if(!(bmpArray->surface = makePalSurface(bmpArray->w, bmpArray->h, colors)))
467+
{
468+
std::cerr << "Failed to create surface: " << SDL_GetError() << std::endl;
469+
return false;
470+
}
434471

435-
// main loop for reading picture lines
436-
for(Position pos{0, 0}; pos.y < bmpArray->h; pos.y++)
472+
if(compression_flag == 0)
437473
{
438-
// loop for reading pixels of the actual picture line
439-
//(cause of a kind of RLE-Compression we cannot read the pixels sequentially)
440-
//'x' will be incremented WITHIN the loop
441-
for(pos.x = 0; pos.x < bmpArray->w;)
474+
// picture uncompressed
475+
// main loop for reading picture lines
476+
for(Position pos{0, 0}; pos.y < bmpArray->h; pos.y++)
442477
{
443-
// read compression type
444-
CHECK_READ(libendian::read(&ctype, 1, fp));
478+
// loop for reading pixels of the actual picture line
479+
for(pos.x = 0; pos.x < bmpArray->w; pos.x++)
480+
{
481+
// read color value (1 Byte)
482+
CHECK_READ(libendian::read(&color_value, 1, fp));
483+
// draw
484+
CSurface::DrawPixel_Color(bmpArray->surface.get(), pos, (Uint32)color_value);
485+
}
486+
}
445487

446-
if(ctype >= 0)
488+
} else if(compression_flag == 1)
489+
{
490+
// picture compressed
491+
492+
// main loop for reading picture lines
493+
for(Position pos{0, 0}; pos.y < bmpArray->h; pos.y++)
494+
{
495+
// loop for reading pixels of the actual picture line
496+
//(cause of a kind of RLE-Compression we cannot read the pixels sequentially)
497+
//'x' will be incremented WITHIN the loop
498+
for(pos.x = 0; pos.x < bmpArray->w;)
447499
{
448-
// following 'ctype + 1' pixels are uncompressed
449-
for(int k = 0; k < ctype + 1; k++, pos.x++)
500+
// read compression type
501+
CHECK_READ(libendian::read(&ctype, 1, fp));
502+
503+
if(ctype >= 0)
450504
{
451-
// read color value (1 Byte)
505+
// following 'ctype + 1' pixels are uncompressed
506+
for(int k = 0; k < ctype + 1; k++, pos.x++)
507+
{
508+
// read color value (1 Byte)
509+
CHECK_READ(libendian::read(&color_value, 1, fp));
510+
// draw
511+
CSurface::DrawPixel_Color(bmpArray->surface.get(), pos, (Uint32)color_value);
512+
}
513+
} else if(ctype >= -127)
514+
{
515+
// draw the following byte '-ctype + 1' times to the surface
452516
CHECK_READ(libendian::read(&color_value, 1, fp));
453-
// draw
454-
CSurface::DrawPixel_Color(bmpArray->surface.get(), pos, (Uint32)color_value);
455-
}
456-
} else if(ctype >= -127)
457-
{
458-
// draw the following byte '-ctype + 1' times to the surface
459-
CHECK_READ(libendian::read(&color_value, 1, fp));
460517

461-
for(int k = 0; k < -ctype + 1; k++, pos.x++)
462-
CSurface::DrawPixel_Color(bmpArray->surface.get(), pos, (Uint32)color_value);
463-
} else // if (ctype == -128)
464-
{
465-
// ignore
518+
for(int k = 0; k < -ctype + 1; k++, pos.x++)
519+
CSurface::DrawPixel_Color(bmpArray->surface.get(), pos, (Uint32)color_value);
520+
} else // if (ctype == -128)
521+
{
522+
// ignore
523+
}
466524
}
467525
}
468-
}
469-
} else
470-
return false;
471-
472-
// if this is a texture file, we need a secondary 32-bit surface for SGE and we set a color key at both surfaces
473-
if(filepath.filename() == "TEX5.LBM" || filepath.filename() == "TEX6.LBM" || filepath.filename() == "TEX7.LBM"
474-
|| filepath.filename() == "TEXTUR_0.LBM" || filepath.filename() == "TEXTUR_3.LBM")
475-
{
476-
SDL_SetColorKey(bmpArray->surface.get(), SDL_TRUE, SDL_MapRGB(bmpArray->surface->format, 0, 0, 0));
526+
} else
527+
return false;
477528

478-
bmpArray++;
479-
if((bmpArray->surface = makeRGBSurface((bmpArray - 1)->w, (bmpArray - 1)->h)))
529+
// if this is a texture file, we need a secondary 32-bit surface for SGE and we set a color key at both surfaces
530+
if(filepath.filename() == "TEX5.LBM" || filepath.filename() == "TEX6.LBM" || filepath.filename() == "TEX7.LBM"
531+
|| filepath.filename() == "TEXTUR_0.LBM" || filepath.filename() == "TEXTUR_3.LBM")
480532
{
533+
// Store palette animation data for the 8-bit tileset
534+
// bmpArray currently points to the 8-bit slot
535+
const Uint16 bmpIdx8 = static_cast<Uint16>(bmpArray - global::bmpArray.data());
536+
if(global::tilesetAnimData.size() <= bmpIdx8)
537+
global::tilesetAnimData.resize(bmpIdx8 + 1);
538+
global::tilesetAnimData[bmpIdx8] = std::move(crngData);
539+
481540
SDL_SetColorKey(bmpArray->surface.get(), SDL_TRUE, SDL_MapRGB(bmpArray->surface->format, 0, 0, 0));
482-
CSurface::Draw(bmpArray->surface, (bmpArray - 1)->surface);
483-
} else
484-
bmpArray--;
485-
}
486541

487-
// we are finished, the surface is filled
488-
// increment bmpArray for the next picture
489-
bmpArray++;
542+
bmpArray++;
543+
if((bmpArray->surface = makeRGBSurface((bmpArray - 1)->w, (bmpArray - 1)->h)))
544+
{
545+
SDL_SetColorKey(bmpArray->surface.get(), SDL_TRUE, SDL_MapRGB(bmpArray->surface->format, 0, 0, 0));
546+
CSurface::Draw(bmpArray->surface, (bmpArray - 1)->surface);
547+
} else
548+
bmpArray--;
549+
}
550+
551+
// we are finished, the surface is filled
552+
// increment bmpArray for the next picture
553+
bmpArray++;
554+
} // end of scope for crngData
490555

491556
return true;
492557
}

CMap.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,13 @@ class CMap
150150
render();
151151
return Surf_Map.get();
152152
}
153+
/// Get map surface palette without rendering (nullptr if not 8-bit or not created yet)
154+
SDL_Palette* getSurfacePalette() const
155+
{
156+
if(!Surf_Map || Surf_Map->format->BytesPerPixel != 1)
157+
return nullptr;
158+
return Surf_Map->format->palette;
159+
}
153160
DisplayRectangle getDisplayRect() { return displayRect; }
154161
void setDisplayRect(const DisplayRectangle& displayRect) { this->displayRect = displayRect; }
155162
auto& getPlayerHQx() { return PlayerHQx; }

0 commit comments

Comments
 (0)