Skip to content

Commit

Permalink
Version 0.1.4 - effect images generator, fixed some issues in PHP out…
Browse files Browse the repository at this point in the history
…fit generator
  • Loading branch information
gesior committed Nov 9, 2021
1 parent 7c8cb47 commit e9d90b8
Show file tree
Hide file tree
Showing 14 changed files with 330 additions and 6,827 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
node_modules
js
init.js
*.map
*.map
tools/colored-outfit-images-generator/cache.generated.txt
tools/colored-outfit-images-generator/outfits_anim
40 changes: 40 additions & 0 deletions effectFramesGenerator.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<html>
<head>
<title>Effect Frames Generator - OpenTibiaLibrary</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
</head>
<body>
<h2>Effect frames generator (for animated effects PHP script)</h2>
<blockquote>
How to use:<br/><br/>
1. Type version of Tibia client.<br/>
2. Select SPR and DAT files.<br/>
3. Click "LOAD FILES", watch "Progress".<br/>
4. Click "GENERATE IMAGES", wait for ZIP file download.<br/>
5. Convert generated PNG images into animations using:<br/>
<a href="https://item-images.ots.me/png-to-gif-converter/">https://item-images.ots.me/png-to-gif-converter/</a><br/>
You can also download <a href="https://github.com/gesior/open-tibia-library">https://github.com/gesior/open-tibia-library</a>
and use command line PHP script <b>"tools/item-image-frames-to-animated-gif-converter/cli_convert.php"</b>
to convert it to animations on your PC<br/>
<br/>
<b>FILES ARE NOT SEND TO SERVER! EVERYTHING IS GENERATED ON YOUR COMPUTER.</b><br/>
<br/>
(Enable Developer console in webbrowser to get more information about problems/progress)
</blockquote>
Client Version: <input type="number" id="clientversion" value="860"/> (format 860, NOT 8.60)<br/>
Sprite file: <input type="file" id="spr"/><br/>
Dat file: <input type="file" id="dat"/><br/>
Force enable extended sprites: <input type="checkbox" id="forceEnableExtendedSprites"/> (for old clients .dat and .spr
with this feature enabled)<br/>
<br/>
<button id="loadFiles">LOAD FILES</button>
<br/>
<br/>
<button id="generateImages">GENERATE IMAGES</button>
<br/>
<br/>
<b>Progress:</b> <span id="progressBar">Not running</span><br/>
<script src="js/vendor.js"></script>
<script src="js/effectFramesGenerator.js"></script>
</body>
</html>
84 changes: 84 additions & 0 deletions effectFramesGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {DatManager} from "./modules/datFile/datManager";
import {OtbManager} from "./modules/otbFile/otbManager";
import {SpriteManager} from "./modules/sprFile/spriteManager";
import {ImageGenerator} from "./modules/imageGenerator/imageGenerator";
import {DatThingCategory, GameFeature} from "./modules/constants/const";
import {WebsiteImageGeneratorBase} from "./websiteImageGeneratorBase";

class EffectFramesGenerator extends WebsiteImageGeneratorBase {
private forceEnableExtendedSpritesCheckbox: HTMLInputElement;

init() {
this.otbRequired = false;
super.init();
this.forceEnableExtendedSpritesCheckbox = <HTMLInputElement>document.getElementById('forceEnableExtendedSprites');
}

afterSetClientVersion() {
if (this.forceEnableExtendedSpritesCheckbox.checked) {
this.client.enableFeature(GameFeature.GameSpritesU32);
}
}

startImageGenerator(imageGenerator: ImageGenerator, otbManager: OtbManager, datManager: DatManager, spriteManager: SpriteManager, zip) {
this.generateEffectImage(imageGenerator, datManager, zip, 0);
}

generateEffectImage(imageGenerator: ImageGenerator, datManager: DatManager, zip, effectId: number) {
const self = this;
this.progressValue(effectId, datManager.getCategory(DatThingCategory.ThingCategoryEffect).length);
if (effectId > datManager.getCategory(DatThingCategory.ThingCategoryEffect).length) {
this.progressText('Packing images to ZIP file, please wait (it may take a while)');
zip.generateAsync({type: "blob"}).then(function (blob: Blob) {
console.log('zip size', blob.size);
self.progressText('ZIP generated, it should start download now.');
self.downloadBlob('effects.zip', blob);
});
return;
}

let effectThingType = this.datManager.getEffect(effectId);
if (!effectThingType) {
console.log('dat ID not found in dat file', effectId);
setTimeout(function () {
self.generateEffectImage(imageGenerator, datManager, zip, effectId + 1);
}, 1);
return;
}

const effectSprites = imageGenerator.generateEffectImagesById(effectId);
if (!effectSprites || effectSprites.length == 0) {
setTimeout(function () {
self.generateEffectImage(imageGenerator, datManager, zip, effectId + 1);
}, 1);
return;
}

const firstEffectSprite = effectSprites[0];
const canvas = <HTMLCanvasElement>document.createElement('canvas');
canvas.width = firstEffectSprite.getWidth() * effectSprites.length;
canvas.height = firstEffectSprite.getHeight();
document.getElementsByTagName('body')[0].appendChild(canvas);
const ctx = canvas.getContext("2d");

for (let animationFrame = 0; animationFrame < effectSprites.length; animationFrame++) {
const palette = ctx.getImageData(firstEffectSprite.getWidth() * animationFrame, 0, firstEffectSprite.getWidth(), firstEffectSprite.getHeight());
const effectSprite = effectSprites[animationFrame];
palette.data.set(new Uint8ClampedArray(effectSprite.getPixels().m_buffer.buffer));
ctx.putImageData(palette, firstEffectSprite.getWidth() * animationFrame, 0);
}

const callback = function (blob) {
canvas.remove();
zip.file('effects/' + effectId + '_' + effectSprites.length + '.png', blob);
setTimeout(function () {
self.generateEffectImage(imageGenerator, datManager, zip, effectId + 1);
}, 1);

};
canvas.toBlob(callback);
}
}

const effectFramesGenerator = new EffectFramesGenerator();
effectFramesGenerator.init();
1 change: 0 additions & 1 deletion itemImageFramesGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ class ItemImageGenerator extends WebsiteImageGeneratorBase {
private forceEnableExtendedSpritesCheckbox: HTMLInputElement;

private onlyPickupable = true;
private forceEnableExtendedSprites = false;

init() {
super.init();
Expand Down
84 changes: 84 additions & 0 deletions modules/imageGenerator/imageGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,90 @@ export class ImageGenerator {
return itemSprites;
}

generateEffectImageById(clientItemId: number, animationFrame = 0, xPattern = 0, yPattern = 0, zPattern = 0,): Sprite {
if (this.datManager === null) {
throw new Error("datManager is not set");
}
if (this.sprManager === null) {
throw new Error("sprManager is not set");
}
let itemThingType = this.datManager.getEffect(clientItemId);
if (!itemThingType) {
console.log('missing dat effect', clientItemId);
return null;
}

const frameGroup = itemThingType.getFrameGroup(FrameGroupType.FrameGroupDefault);
if (!frameGroup) {
console.log('missing default frameGroup effect', clientItemId);
return null;
}

const itemSprite = new Sprite(new Size(SpriteManager.SPRITE_SIZE * frameGroup.m_size.width(), SpriteManager.SPRITE_SIZE * frameGroup.m_size.height()));

for (let l = 0; l < frameGroup.m_layers; ++l) {
for (let w = 0; w < frameGroup.m_size.width(); ++w) {
for (let h = 0; h < frameGroup.m_size.height(); ++h) {
const spriteId = frameGroup.m_spritesIndex[
frameGroup.getSpriteIndex(w, h, l, xPattern, yPattern, zPattern, animationFrame)
];
const sprite = this.sprManager.getSprite(spriteId);
if (!sprite) {
if (spriteId != 0) {
console.log('missing sprite', spriteId);
}
continue;
}
itemSprite.blit(
new Point(
SpriteManager.SPRITE_SIZE * (frameGroup.m_size.width() - w - 1),
SpriteManager.SPRITE_SIZE * (frameGroup.m_size.height() - h - 1)
),
sprite
);
}
}
}

return itemSprite;
}

/**
* Generates array of effect images.
* Array contains animation frames of effect.
* If item is stackable, array contains first animation frame of each stack stage.
* @param effectId
*/
generateEffectImagesById(effectId: number): Sprite[] {
if (this.datManager === null) {
throw new Error("datManager is not set");
}
if (this.sprManager === null) {
throw new Error("sprManager is not set");
}
let effectThingType = this.datManager.getEffect(effectId);
if (!effectThingType) {
console.log('missing dat effect', effectId);
return null;
}

const frameGroup = effectThingType.getFrameGroup(FrameGroupType.FrameGroupIdle);
if (!frameGroup) {
console.log('missing idle frameGroup item', effectId);
return null;
}

const effectSprites = [];
for (let animationPhase = 0; animationPhase < frameGroup.m_animationPhases; ++animationPhase) {
const effectSprite = this.generateEffectImageById(effectId, animationPhase);
if (effectSprite) {
effectSprites.push(effectSprite);
}
}

return effectSprites;
}

generateOutfitAnimationImages(outfitId: number, frameGroupType: FrameGroupType = FrameGroupType.FrameGroupMoving) {
if (this.datManager === null) {
throw new Error("datManager is not set");
Expand Down
2 changes: 1 addition & 1 deletion outfitImageGenerator.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
</head>
<body>
<h2>Outfit images generator (for outfits PHP script)</h2>
<h2>Outfit images generator</h2>
<blockquote>
How to use:<br/><br/>
1. Type version of Tibia client.<br/>
Expand Down
5 changes: 5 additions & 0 deletions outfitImageGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {SpriteManager} from "./modules/sprFile/spriteManager";
import {ImageGenerator} from "./modules/imageGenerator/imageGenerator";
import {DatThingCategory, FrameGroupType, GameFeature} from "./modules/constants/const";
import {WebsiteImageGeneratorBase} from "./websiteImageGeneratorBase";
import {OutfitImagePhpGeneratorCode} from "./outfitImagePhpGeneratorCode";

class OutfitImageGenerator extends WebsiteImageGeneratorBase {
private idleAnimationCheckbox: HTMLInputElement;
Expand Down Expand Up @@ -34,6 +35,10 @@ class OutfitImageGenerator extends WebsiteImageGeneratorBase {
this.progressValue(outfitId, datManager.getCategory(DatThingCategory.ThingCategoryCreature).length);
if (outfitId > datManager.getCategory(DatThingCategory.ThingCategoryCreature).length) {
this.progressText('Packing images to ZIP file, please wait (it may take a while)');

const outfitImagePhpGeneratorCode = new OutfitImagePhpGeneratorCode();
outfitImagePhpGeneratorCode.addFilesToZip(zip);

zip.generateAsync({type: "blob"}).then(function (blob: Blob) {
console.log('zip size', blob.size);
self.progressText('ZIP generated, it should start download now.');
Expand Down
Loading

0 comments on commit e9d90b8

Please sign in to comment.