diff --git a/itemImageGenerator.html b/itemImageGenerator.html index 85ab492..bf655be 100644 --- a/itemImageGenerator.html +++ b/itemImageGenerator.html @@ -26,6 +26,7 @@ Sprite file:
Dat file:
Otb file:
+Only pickable items:



diff --git a/itemImageGenerator.ts b/itemImageGenerator.ts index b8d9fef..b8e966c 100755 --- a/itemImageGenerator.ts +++ b/itemImageGenerator.ts @@ -13,6 +13,7 @@ class ItemImageGenerator { private sprPicker: HTMLInputElement; private datPicker: HTMLInputElement; private otbPicker: HTMLInputElement; + private onlyPickupableCheckbox: HTMLInputElement; private loadFilesButton: HTMLButtonElement; private generateImagesButton: HTMLButtonElement; @@ -22,17 +23,20 @@ class ItemImageGenerator { private spriteManager: SpriteManager; private datManager: DatManager; private otbManager: OtbManager; + private onlyPickupable = true; init() { this.clientVersionInput = document.getElementById('clientversion'); this.sprPicker = document.getElementById('spr'); this.datPicker = document.getElementById('dat'); this.otbPicker = document.getElementById('otb'); + this.onlyPickupableCheckbox = document.getElementById('onlyPickupable'); this.loadFilesButton = document.getElementById('loadFiles'); this.generateImagesButton = document.getElementById('generateImages'); const self = this; this.loadFilesButton.onclick = function () { + self.onlyPickupable = self.onlyPickupableCheckbox.checked; self.loadFiles(); }; this.generateImagesButton.onclick = function () { @@ -42,7 +46,7 @@ class ItemImageGenerator { } const imageGenerator = new ImageGenerator(self.datManager, self.spriteManager, self.otbManager); const zip = new JSZip(); - self.generateItemImage(imageGenerator, self.otbManager, self.datManager, zip, 0); + self.generateItemImage(imageGenerator, zip, 0); }; } @@ -127,10 +131,10 @@ class ItemImageGenerator { } } - generateItemImage(imageGenerator: ImageGenerator, otbManager: OtbManager, datManager: DatManager, zip, serverId: number) { + generateItemImage(imageGenerator: ImageGenerator, zip, serverId: number) { const self = this; - this.progressValue(serverId, otbManager.getLastId()); - if (serverId > otbManager.getLastId()) { + this.progressValue(serverId, this.otbManager.getLastId()); + if (serverId > this.otbManager.getLastId()) { 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); @@ -140,33 +144,33 @@ class ItemImageGenerator { return; } - if (!otbManager.isValidOtbId(serverId)) { + if (!this.otbManager.isValidOtbId(serverId)) { setTimeout(function () { - self.generateItemImage(imageGenerator, otbManager, datManager, zip, serverId + 1); + self.generateItemImage(imageGenerator, zip, serverId + 1); }, 1); return; } - const clientItemId = otbManager.getItem(serverId).getClientId(); + const clientItemId = this.otbManager.getItem(serverId).getClientId(); if (!clientItemId) { console.log('otb ID not mapped to any dat ID', serverId); setTimeout(function () { - self.generateItemImage(imageGenerator, otbManager, datManager, zip, serverId + 1); + self.generateItemImage(imageGenerator, zip, serverId + 1); }, 1); return; } - let itemThingType = datManager.getItem(clientItemId); + let itemThingType = this.datManager.getItem(clientItemId); if (!itemThingType) { console.log('dat ID not found in dat file', serverId, clientItemId); setTimeout(function () { - self.generateItemImage(imageGenerator, otbManager, datManager, zip, serverId + 1); + self.generateItemImage(imageGenerator, zip, serverId + 1); }, 1); return; } - if (!itemThingType.isPickupable()) { + if (this.onlyPickupable && !itemThingType.isPickupable()) { console.log('skip not pickupable', serverId); setTimeout(function () { - self.generateItemImage(imageGenerator, otbManager, datManager, zip, serverId + 1); + self.generateItemImage(imageGenerator, zip, serverId + 1); }, 1); return; } @@ -174,7 +178,7 @@ class ItemImageGenerator { const itemSprite = imageGenerator.generateItemImageByServerId(serverId); if (!itemSprite) { setTimeout(function () { - self.generateItemImage(imageGenerator, otbManager, datManager, zip, serverId + 1); + self.generateItemImage(imageGenerator, zip, serverId + 1); }, 1); return; } @@ -193,7 +197,7 @@ class ItemImageGenerator { canvas.remove(); zip.file(serverId + '.png', blob); setTimeout(function () { - self.generateItemImage(imageGenerator, otbManager, datManager, zip, serverId + 1); + self.generateItemImage(imageGenerator, zip, serverId + 1); }, 1); }; @@ -214,7 +218,7 @@ class ItemImageGenerator { gif.on('finished', function (blob) { zip.file(serverId + '.gif', blob); setTimeout(function () { - self.generateItemImage(imageGenerator, otbManager, datManager, zip, serverId + 1); + self.generateItemImage(imageGenerator, zip, serverId + 1); }, 1); }); diff --git a/modules/datFile/animator.ts b/modules/datFile/animator.ts index d47918d..075cd4b 100644 --- a/modules/datFile/animator.ts +++ b/modules/datFile/animator.ts @@ -6,7 +6,7 @@ export class Animator { m_startPhase = 0; m_loopCount = 0; m_async = false; - m_phaseDurations = []; + m_phaseDurations: number[][] = []; unserialize(animationPhases: number, fin: InputFile) { this.m_animationPhases = animationPhases; diff --git a/modules/datFile/datManager.ts b/modules/datFile/datManager.ts index 9712917..4118bc9 100644 --- a/modules/datFile/datManager.ts +++ b/modules/datFile/datManager.ts @@ -122,7 +122,7 @@ export class DatManager { firstId = 100; for (let id = firstId; id < this.m_thingTypes[category].length; ++id) - this.m_thingTypes[category][id].serialize(fin, this.m_client, clientTranslationArray); + this.m_thingTypes[category][id].serialize(fin, category, this.m_client, clientTranslationArray); } return fin; } diff --git a/modules/datFile/datThingType.ts b/modules/datFile/datThingType.ts index 3d76f4f..f7642bf 100644 --- a/modules/datFile/datThingType.ts +++ b/modules/datFile/datThingType.ts @@ -10,30 +10,22 @@ import {Point} from "../structures/point"; import {MarketData} from "../structures/marketData"; import {Light} from "../structures/light"; import {OutputFile} from "../fileHandlers/outputFile"; +import {FrameGroup} from "./frameGroup"; export class DatThingType { static maskColors = [Color.red, Color.green, Color.blue, Color.yellow]; - m_category: DatThingCategory; - m_id: number = 0; - m_null: boolean = true; - m_attribs: DatThingTypeAttributes = new DatThingTypeAttributes(); - - m_size: Size = new Size(); - m_displacement: Point = new Point(); - m_animator: Animator = null; - m_animationPhases: number = 0; - m_exactSize: number = 0; - m_realSize: number = 0; - m_numPatternX: number = 0; - m_numPatternY: number = 0; - m_numPatternZ: number = 0; - m_layers: number = 0; - m_elevation: number = 0; - - m_spritesIndex: number[] = []; - - serialize(fin: OutputFile, client: Client, clientAttributeTranslator) { + private m_category: DatThingCategory; + private m_id: number = 0; + private m_null: boolean = true; + private m_attribs: DatThingTypeAttributes = new DatThingTypeAttributes(); + + private m_displacement: Point = new Point(); + private m_elevation: number = 0; + + private m_frameGroups: FrameGroup[] = []; + + serialize(fin: OutputFile, category: DatThingCategory, client: Client, clientAttributeTranslator) { for (let clientAttrString in clientAttributeTranslator) { if (clientAttributeTranslator.hasOwnProperty(clientAttrString)) { let clientDatAttr = parseInt(clientAttrString); @@ -44,9 +36,10 @@ export class DatThingType { fin.addU8(clientDatAttr); switch (thingAttr) { case DatThingAttr.ThingAttrDisplacement: { - - fin.addU16(this.m_displacement.x); - fin.addU16(this.m_displacement.y); + if (client.getClientVersion() >= 755) { + fin.addU16(this.m_displacement.x); + fin.addU16(this.m_displacement.y); + } break; } case DatThingAttr.ThingAttrLight: { @@ -82,29 +75,40 @@ export class DatThingType { } fin.addU8(DatThingAttr.ThingLastAttr); - fin.addU8(this.m_size.width()); - fin.addU8(this.m_size.height()); + let hasFrameGroups = (category == DatThingCategory.ThingCategoryCreature && client.getFeature(GameFeature.GameIdleAnimations)); + if (hasFrameGroups) + fin.addU8(this.m_frameGroups.length); + + for (let frameGroupType in this.m_frameGroups) { + if (hasFrameGroups) + fin.addU8(Number(frameGroupType)); - if (this.m_size.width() > 1 || this.m_size.height() > 1) - fin.addU8(this.m_realSize); + const frameGroup = this.m_frameGroups[frameGroupType]; + fin.addU8(frameGroup.m_size.width()); + fin.addU8(frameGroup.m_size.height()); - fin.addU8(this.m_layers); - fin.addU8(this.m_numPatternX); - fin.addU8(this.m_numPatternY); - fin.addU8(this.m_numPatternZ); - fin.addU8(this.m_animationPhases); + if (frameGroup.m_size.width() > 1 || frameGroup.m_size.height() > 1) + fin.addU8(frameGroup.m_realSize); - if (client.getFeature(GameFeature.GameEnhancedAnimations)) { - if (this.m_animationPhases > 1 && this.m_animator != null) { - this.m_animator.serialize(fin); + fin.addU8(frameGroup.m_layers); + fin.addU8(frameGroup.m_numPatternX); + fin.addU8(frameGroup.m_numPatternY); + if (client.getClientVersion() >= 755) + fin.addU8(frameGroup.m_numPatternZ); + fin.addU8(frameGroup.m_animationPhases); + + if (client.getFeature(GameFeature.GameEnhancedAnimations)) { + if (frameGroup.m_animationPhases > 1 && frameGroup.m_animator != null) { + frameGroup.m_animator.serialize(fin); + } } - } - for (let i2 = 0; i2 < this.m_spritesIndex.length; i2++) { - if (client.getFeature(GameFeature.GameSpritesU32)) - fin.addU32(this.m_spritesIndex[i2]); - else - fin.addU16(this.m_spritesIndex[i2]); + for (let i2 = 0; i2 < frameGroup.m_spritesIndex.length; i2++) { + if (client.getFeature(GameFeature.GameSpritesU32)) + fin.addU32(frameGroup.m_spritesIndex[i2]); + else + fin.addU16(frameGroup.m_spritesIndex[i2]); + } } } @@ -182,89 +186,51 @@ export class DatThingType { let hasFrameGroups = (category == DatThingCategory.ThingCategoryCreature && client.getFeature(GameFeature.GameIdleAnimations)); let groupCount = hasFrameGroups ? fin.getU8() : 1; - this.m_animationPhases = 0; - let totalSpritesCount = 0; - for (let i = 0; i < groupCount; ++i) { - let frameGroupType = FrameGroupType.FrameGroupDefault; + let frameGroupType = (category == DatThingCategory.ThingCategoryCreature) ? FrameGroupType.FrameGroupMoving : FrameGroupType.FrameGroupIdle; if (hasFrameGroups) frameGroupType = fin.getU8(); - // TODO: load IDLE and MOVING frames - if (groupCount == 1 || frameGroupType == FrameGroupType.FrameGroupMoving) { - let width = fin.getU8(); - let height = fin.getU8(); - this.m_size = new Size(width, height); - if (width > 1 || height > 1) { - this.m_realSize = fin.getU8(); - this.m_exactSize = Math.min(this.m_realSize, Math.max(width * 32, height * 32)); - } else - this.m_exactSize = 32; - - this.m_layers = fin.getU8(); - this.m_numPatternX = fin.getU8(); - this.m_numPatternY = fin.getU8(); - if (client.getClientVersion() >= 755) - this.m_numPatternZ = fin.getU8(); - else - this.m_numPatternZ = 1; - - let groupAnimationsPhases = fin.getU8(); - this.m_animationPhases = groupAnimationsPhases; - - if (groupAnimationsPhases > 1 && client.getFeature(GameFeature.GameEnhancedAnimations)) { - this.m_animator = new Animator(); - this.m_animator.unserialize(groupAnimationsPhases, fin); - } - - let totalSprites = this.m_size.area() * this.m_layers * this.m_numPatternX * this.m_numPatternY * this.m_numPatternZ * groupAnimationsPhases; - - if ((totalSpritesCount + totalSprites) > 4096) - error("a thing type has more than 4096 sprites", totalSprites, totalSpritesCount, this.m_size.area(), this.m_layers, this.m_numPatternX, this.m_numPatternY, this.m_numPatternZ, groupAnimationsPhases); - - this.m_spritesIndex = []; - for (let i = totalSpritesCount; i < (totalSpritesCount + totalSprites); i++) { - this.m_spritesIndex[i] = client.getFeature(GameFeature.GameSpritesU32) ? fin.getU32() : fin.getU16(); - } + const frameGroup = new FrameGroup(); + + let width = fin.getU8(); + let height = fin.getU8(); + frameGroup.m_size = new Size(width, height); + if (width > 1 || height > 1) { + frameGroup.m_realSize = fin.getU8(); + frameGroup.m_exactSize = Math.min(frameGroup.m_realSize, Math.max(width * 32, height * 32)); + } else + frameGroup.m_exactSize = 32; + + frameGroup.m_layers = fin.getU8(); + frameGroup.m_numPatternX = fin.getU8(); + frameGroup.m_numPatternY = fin.getU8(); + if (client.getClientVersion() >= 755) + frameGroup.m_numPatternZ = fin.getU8(); + else + frameGroup.m_numPatternZ = 1; - //console.log('spr', this.m_spritesIndex); - totalSpritesCount += totalSprites; - } else { - let width = fin.getU8(); - let height = fin.getU8(); - let tmpSize = new Size(width, height); - let tmpExactSize = 32; - if (width > 1 || height > 1) { - let tmpRealSize = fin.getU8(); - tmpExactSize = Math.min(tmpRealSize, Math.max(width * 32, height * 32)); - } + let groupAnimationsPhases = fin.getU8(); + frameGroup.m_animationPhases = groupAnimationsPhases; - let tmpLayers = fin.getU8(); - let tmpNumPatternX = fin.getU8(); - let tmpNumPatternY = fin.getU8(); - let tmpNumPatternZ = 1; - if (client.getClientVersion() >= 755) - tmpNumPatternZ = fin.getU8(); + if (groupAnimationsPhases > 1 && client.getFeature(GameFeature.GameEnhancedAnimations)) { + frameGroup.m_animator = new Animator(); + frameGroup.m_animator.unserialize(groupAnimationsPhases, fin); + } - let groupAnimationsPhases = fin.getU8(); + let totalSprites = frameGroup.m_size.area() * frameGroup.m_layers * frameGroup.m_numPatternX * frameGroup.m_numPatternY * frameGroup.m_numPatternZ * groupAnimationsPhases; - if (groupAnimationsPhases > 1 && client.getFeature(GameFeature.GameEnhancedAnimations)) { - let tmpAnimator = new Animator(); - tmpAnimator.unserialize(groupAnimationsPhases, fin); - } + if (totalSprites > 4096) + error("a thing type has more than 4096 sprites", totalSprites, frameGroup.m_size.area(), frameGroup.m_layers, frameGroup.m_numPatternX, frameGroup.m_numPatternY, frameGroup.m_numPatternZ, groupAnimationsPhases); - let totalSprites = tmpSize.area() * tmpLayers * tmpNumPatternX * tmpNumPatternY * tmpNumPatternZ * groupAnimationsPhases; + frameGroup.m_spritesIndex = []; + for (let i = 0; i < totalSprites; i++) { + frameGroup.m_spritesIndex[i] = client.getFeature(GameFeature.GameSpritesU32) ? fin.getU32() : fin.getU16(); + } - if ((totalSpritesCount + totalSprites) > 4096) - error("a thing type has more than 4096 sprites", totalSprites, totalSpritesCount, tmpSize.area(), tmpLayers, tmpNumPatternX, tmpNumPatternY, tmpNumPatternZ, groupAnimationsPhases); + //console.log('spr', this.m_spritesIndex); - let tmpSpritesIndex = []; - for (let i = totalSpritesCount; i < (totalSpritesCount + totalSprites); i++) { - tmpSpritesIndex[i] = client.getFeature(GameFeature.GameSpritesU32) ? fin.getU32() : fin.getU16(); - } - - //console.log('spr', this.m_spritesIndex); - } + this.m_frameGroups[frameGroupType] = frameGroup; } } @@ -284,51 +250,6 @@ export class DatThingType { return this.m_attribs.has(attr); } - getSize(): Size { - return this.m_size; - } - - getWidth(): number { - return this.m_size.width(); - } - - getHeight(): number { - return this.m_size.height(); - } - - getExactSize(layer: number = 0, xPattern: number = 0, yPattern: number = 0, zPattern: number = 0, animationPhase: number = 0): number { - /* todo */ - return 0; - } - - getRealSize(): number { - return this.m_realSize; - } - - getLayers(): number { - return this.m_layers; - } - - getNumPatternX(): number { - return this.m_numPatternX; - } - - getNumPatternY(): number { - return this.m_numPatternY; - } - - getNumPatternZ(): number { - return this.m_numPatternZ; - } - - getAnimationPhases(): number { - return this.m_animationPhases; - } - - getAnimator(): Animator { - return this.m_animator; - } - getDisplacement(): Point { return this.m_displacement; } @@ -345,6 +266,14 @@ export class DatThingType { return this.m_elevation; } + getFrameGroups(): FrameGroup[] { + return this.m_frameGroups; + } + + getFrameGroup(frameGroupType: FrameGroupType): FrameGroup { + return this.m_frameGroups[frameGroupType]; + } + getGroundSpeed(): number { return this.m_attribs.get(DatThingAttr.ThingAttrGround); } @@ -529,14 +458,6 @@ export class DatThingType { return this.m_attribs.has(DatThingAttr.ThingAttrTopEffect); } - getSprites(): number[] { - return this.m_spritesIndex; - } - - getSprite(index: number): number { - return this.m_spritesIndex[index]; - } - isNotPreWalkable(): boolean { return this.m_attribs.has(DatThingAttr.ThingAttrNotPreWalkable); } @@ -578,121 +499,7 @@ export class DatThingType { bestDimension = candidateDimension; } } - //console.log('dim', this.m_id, bestDimension); - return bestDimension; - //return new Size(w, h); - } - - getSpriteIndex(w: number, h: number, l: number, x: number, y: number, z: number, a: number): number { - let index = - ((((((a % this.m_animationPhases) - * this.m_numPatternZ + z) - * this.m_numPatternY + y) - * this.m_numPatternX + x) - * this.m_layers + l) - * this.m_size.height() + h) - * this.m_size.width() + w; - if (!(index < this.m_spritesIndex.length)) { - throw new Error('index < this.m_spritesIndex.length'); - } - return index; - } - getTextureIndex(l: number, x: number, y: number, z: number) { - return ((l * this.m_numPatternZ + z) - * this.m_numPatternY + y) - * this.m_numPatternX + x; + return bestDimension; } - - /* - getTexture(animationPhase: number): Texture { - - let animationPhaseTexture = this.m_textures[animationPhase]; - if (!animationPhaseTexture) { - - // we don't need layers in common items, they will be pre-drawn - let textureLayers = 1; - let numLayers = this.m_layers; - if (this.m_category == ThingCategory.ThingCategoryCreature && numLayers >= 2) { - // 5 layers: outfit base, red mask, green mask, blue mask, yellow mask - textureLayers = 5; - numLayers = 5; - } - - let indexSize = textureLayers * this.m_numPatternX * this.m_numPatternY * this.m_numPatternZ; - let textureSize = this.getBestTextureDimension(this.m_size.width(), this.m_size.height(), indexSize); - //console.log('dim', textureSize, this); - let fullImage = new Image(textureSize.mul(Otc.TILE_PIXELS)); - - //console.log('fi', fullImage.getWidth(), fullImage.getHeight()) - this.m_texturesFramesRects[animationPhase] = []; - this.m_texturesFramesOriginRects[animationPhase] = []; - this.m_texturesFramesOffsets[animationPhase] = []; - - for (let z = 0; z < this.m_numPatternZ; ++z) { - for (let y = 0; y < this.m_numPatternY; ++y) { - for (let x = 0; x < this.m_numPatternX; ++x) { - for (let l = 0; l < numLayers; ++l) { - let spriteMask = (this.m_category == ThingCategory.ThingCategoryCreature && l > 0); - let frameIndex = this.getTextureIndex(l % textureLayers, x, y, z); - let framePos = new Point(toInt(frameIndex % toInt(textureSize.width() / this.m_size.width()) * this.m_size.width()) * Otc.TILE_PIXELS, - toInt(frameIndex / toInt(textureSize.width() / this.m_size.width()) * this.m_size.height()) * Otc.TILE_PIXELS); - - //console.log('blitx', framePos); - for (let h = 0; h < this.m_size.height(); ++h) { - for (let w = 0; w < this.m_size.width(); ++w) { - let spriteIndex = this.getSpriteIndex(w, h, spriteMask ? 1 : l, x, y, z, animationPhase); - let spriteImage = g_sprites.getSpriteImage(this.m_spritesIndex[spriteIndex]); - - if (spriteImage) { - if (spriteMask) { - spriteImage.overwriteMask(ThingType.maskColors[l - 1]); - } - let spritePos = new Point((this.m_size.width() - w - 1) * Otc.TILE_PIXELS, - (this.m_size.height() - h - 1) * Otc.TILE_PIXELS); - - fullImage.blit(framePos.add(spritePos), spriteImage); - } else { - //console.error(this.m_spritesIndex, spriteIndex); - } - } - } - - let drawRect = new Rect( - framePos.add(new Point(this.m_size.width(), this.m_size.height())) - .mul(Otc.TILE_PIXELS) - .sub(new Point(1, 1)), - framePos); - - for (let x = framePos.x; x < framePos.x + this.m_size.width() * Otc.TILE_PIXELS; ++x) { - for (let y = framePos.y; y < framePos.y + this.m_size.height() * Otc.TILE_PIXELS; ++y) { - - let p = fullImage.getPixel(x, y); - if (p[3] != 0x00) { - drawRect.setTop(Math.min(y, drawRect.top())); - drawRect.setLeft(Math.min(x, drawRect.left())); - drawRect.setBottom(Math.max(y, drawRect.bottom())); - drawRect.setRight(Math.max(x, drawRect.right())); - } - - } - } - //console.log('blit', drawRect); - - this.m_texturesFramesRects[animationPhase][frameIndex] = drawRect; - this.m_texturesFramesOriginRects[animationPhase][frameIndex] = new Rect(framePos, new Size(this.m_size.width(), this.m_size.height()).mul(Otc.TILE_PIXELS)); - this.m_texturesFramesOffsets[animationPhase][frameIndex] = drawRect.topLeft().sub(framePos); - - } - } - } - } - animationPhaseTexture = new Texture(fullImage, true); - //animationPhaseTexture.setSmooth(true); - //console.log(this.m_id, animationPhase, animationPhaseTexture); - this.m_textures[animationPhase] = animationPhaseTexture; - } - return animationPhaseTexture; - } - */ } diff --git a/modules/datFile/frameGroup.ts b/modules/datFile/frameGroup.ts new file mode 100644 index 0000000..cfb4798 --- /dev/null +++ b/modules/datFile/frameGroup.ts @@ -0,0 +1,87 @@ +import {InputFile} from "../fileHandlers/inputFile"; +import {OutputFile} from "../fileHandlers/outputFile"; +import {Animator} from "./animator"; +import {Size} from "../structures/size"; + +export class FrameGroup { + m_size: Size = new Size(); + m_animator: Animator = null; + m_animationPhases: number = 0; + m_exactSize: number = 0; + m_realSize: number = 0; + m_numPatternX: number = 0; + m_numPatternY: number = 0; + m_numPatternZ: number = 0; + m_layers: number = 0; + m_spritesIndex: number[] = []; + + getSize(): Size { + return this.m_size; + } + + getWidth(): number { + return this.m_size.width(); + } + + getHeight(): number { + return this.m_size.height(); + } + + getRealSize(): number { + return this.m_realSize; + } + + getLayers(): number { + return this.m_layers; + } + + getNumPatternX(): number { + return this.m_numPatternX; + } + + getNumPatternY(): number { + return this.m_numPatternY; + } + + getNumPatternZ(): number { + return this.m_numPatternZ; + } + + getAnimationPhases(): number { + return this.m_animationPhases; + } + + getAnimator(): Animator { + return this.m_animator; + } + + getSprites(): number[] { + return this.m_spritesIndex; + } + + getSprite(index: number): number { + return this.m_spritesIndex[index]; + } + + getSpriteIndex(w: number, h: number, l: number, x: number, y: number, z: number, a: number): number { + let index = + ((((((a % this.m_animationPhases) + * this.m_numPatternZ + z) + * this.m_numPatternY + y) + * this.m_numPatternX + x) + * this.m_layers + l) + * this.m_size.height() + h) + * this.m_size.width() + w; + if (!(index < this.m_spritesIndex.length)) { + throw new Error('index < this.m_spritesIndex.length'); + } + return index; + } + + getTextureIndex(l: number, x: number, y: number, z: number) { + return ((l * this.m_numPatternZ + z) + * this.m_numPatternY + y) + * this.m_numPatternX + x; + } + +} diff --git a/modules/imageGenerator/imageGenerator.ts b/modules/imageGenerator/imageGenerator.ts index 8b12416..f10308c 100644 --- a/modules/imageGenerator/imageGenerator.ts +++ b/modules/imageGenerator/imageGenerator.ts @@ -4,6 +4,7 @@ import {OtbManager} from "../otbFile/otbManager"; import {Sprite} from "../sprFile/sprite"; import {Size} from "../structures/size"; import {Point} from "../structures/point"; +import {FrameGroupType} from "../constants/const"; export class ImageGenerator { constructor(private datManager: DatManager = null, private sprManager: SpriteManager = null, private otbManager: OtbManager = null) { @@ -52,13 +53,19 @@ export class ImageGenerator { return null; } - const itemSprite = new Sprite(new Size(SpriteManager.SPRITE_SIZE * itemThingType.m_size.width(), SpriteManager.SPRITE_SIZE * itemThingType.m_size.height())); + const frameGroup = itemThingType.getFrameGroup(FrameGroupType.FrameGroupIdle); + if (!frameGroup) { + console.log('missing idle frameGroup item', 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 < itemThingType.m_layers; ++l) { - for (let w = 0; w < itemThingType.m_size.width(); ++w) { - for (let h = 0; h < itemThingType.m_size.height(); ++h) { - const spriteId = itemThingType.m_spritesIndex[ - itemThingType.getSpriteIndex(w, h, l, 0, 0, 0, animationFrame) + 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, 0, 0, 0, animationFrame) ]; const sprite = this.sprManager.getSprite(spriteId); if (!sprite) { @@ -69,8 +76,8 @@ export class ImageGenerator { } itemSprite.blit( new Point( - SpriteManager.SPRITE_SIZE * (itemThingType.m_size.width() - w - 1), - SpriteManager.SPRITE_SIZE * (itemThingType.m_size.height() - h - 1) + SpriteManager.SPRITE_SIZE * (frameGroup.m_size.width() - w - 1), + SpriteManager.SPRITE_SIZE * (frameGroup.m_size.height() - h - 1) ), sprite ); @@ -94,8 +101,14 @@ export class ImageGenerator { return null; } + const frameGroup = itemThingType.getFrameGroup(FrameGroupType.FrameGroupIdle); + if (!frameGroup) { + console.log('missing idle frameGroup item', clientItemId); + return null; + } + const itemSprites = []; - for (let a = 0; a < itemThingType.m_animationPhases; ++a) { + for (let a = 0; a < frameGroup.m_animationPhases; ++a) { const itemSprite = this.generateItemImageByClientId(clientItemId, a); if (itemSprite) { itemSprites.push(itemSprite); @@ -105,7 +118,7 @@ export class ImageGenerator { return itemSprites; } - generateOutfitAnimationImages(outfitId: number) { + generateOutfitAnimationImages(outfitId: number, frameGroupType: FrameGroupType = FrameGroupType.FrameGroupMoving) { if (this.datManager === null) { throw new Error("datManager is not set"); } @@ -118,19 +131,25 @@ export class ImageGenerator { return null; } + const frameGroup = outfitThingType.getFrameGroup(frameGroupType); + if (!frameGroup) { + console.log('missing frameGroup outfit', outfitId, frameGroupType); + return null; + } + const sprites = []; - for(let z = 0; z < outfitThingType.m_numPatternZ; ++z) { - for(let y = 0; y < outfitThingType.m_numPatternY; ++y) { - for(let x = 0; x < outfitThingType.m_numPatternX; ++x) { - for(let l = 0; l < outfitThingType.m_layers; ++l) { - for(let a = 0; a < outfitThingType.m_animationPhases; ++a) { + for(let z = 0; z < frameGroup.m_numPatternZ; ++z) { + for(let y = 0; y < frameGroup.m_numPatternY; ++y) { + for(let x = 0; x < frameGroup.m_numPatternX; ++x) { + for(let l = 0; l < frameGroup.m_layers; ++l) { + for(let a = 0; a < frameGroup.m_animationPhases; ++a) { console.log('generate', 'outfits_anim/' + outfitId + '/' + (a+1) + '/' + (z+1) + '/' + (y+1) + '/' +(x+1)) - const outfitSprite = new Sprite(new Size(SpriteManager.SPRITE_SIZE * outfitThingType.m_size.width(), SpriteManager.SPRITE_SIZE * outfitThingType.m_size.height())); - for(let w = 0; w < outfitThingType.m_size.width(); ++w) { - for(let h = 0; h < outfitThingType.m_size.height(); ++h) { - const spriteId = outfitThingType.m_spritesIndex[ - outfitThingType.getSpriteIndex(w, h, l, x, y, z, a) + const outfitSprite = new Sprite(new Size(SpriteManager.SPRITE_SIZE * frameGroup.m_size.width(), SpriteManager.SPRITE_SIZE * frameGroup.m_size.height())); + 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, x, y, z, a) ]; const sprite = this.sprManager.getSprite(spriteId); if (!sprite) { @@ -141,8 +160,8 @@ export class ImageGenerator { } outfitSprite.blit( new Point( - SpriteManager.SPRITE_SIZE * (outfitThingType.m_size.width() - w - 1), - SpriteManager.SPRITE_SIZE * (outfitThingType.m_size.height() - h - 1) + SpriteManager.SPRITE_SIZE * (frameGroup.m_size.width() - w - 1), + SpriteManager.SPRITE_SIZE * (frameGroup.m_size.height() - h - 1) ), sprite ); diff --git a/outfitImageGenerator.html b/outfitImageGenerator.html index 386c99f..3e8f2eb 100644 --- a/outfitImageGenerator.html +++ b/outfitImageGenerator.html @@ -26,6 +26,7 @@ Sprite file:
Dat file:
Otb file:
+Load idle animation when possible:



diff --git a/outfitImageGenerator.ts b/outfitImageGenerator.ts index 20116a4..278724c 100644 --- a/outfitImageGenerator.ts +++ b/outfitImageGenerator.ts @@ -4,7 +4,7 @@ import {OtbManager} from "./modules/otbFile/otbManager"; import {SpriteManager} from "./modules/sprFile/spriteManager"; import {InputFile} from "./modules/fileHandlers/inputFile"; import {ImageGenerator} from "./modules/imageGenerator/imageGenerator"; -import {DatThingCategory} from "./modules/constants/const"; +import {DatThingCategory, FrameGroupType} from "./modules/constants/const"; let JSZip = require('jszip'); let GIF = require('gif.js'); @@ -14,6 +14,7 @@ class OutfitImageGenerator { private sprPicker: HTMLInputElement; private datPicker: HTMLInputElement; private otbPicker: HTMLInputElement; + private idleAnimation: HTMLInputElement; private loadFilesButton: HTMLButtonElement; private generateImagesButton: HTMLButtonElement; @@ -23,17 +24,20 @@ class OutfitImageGenerator { private spriteManager: SpriteManager; private datManager: DatManager; private otbManager: OtbManager; + private tryLoadIdleAnimation = true; init() { this.clientVersionInput = document.getElementById('clientversion'); this.sprPicker = document.getElementById('spr'); this.datPicker = document.getElementById('dat'); this.otbPicker = document.getElementById('otb'); + this.idleAnimation = document.getElementById('idleAnimation'); this.loadFilesButton = document.getElementById('loadFiles'); this.generateImagesButton = document.getElementById('generateImages'); const self = this; this.loadFilesButton.onclick = function () { + self.tryLoadIdleAnimation = self.idleAnimation.checked; self.loadFiles(); }; this.generateImagesButton.onclick = function () { @@ -141,7 +145,13 @@ class OutfitImageGenerator { return; } - const outfitSprites = imageGenerator.generateOutfitAnimationImages(outfitId); + let outfitSprites; + if (this.tryLoadIdleAnimation) { + outfitSprites = imageGenerator.generateOutfitAnimationImages(outfitId, FrameGroupType.FrameGroupIdle); + } + if (!outfitSprites || outfitSprites.length == 0) { + outfitSprites = imageGenerator.generateOutfitAnimationImages(outfitId, FrameGroupType.FrameGroupMoving); + } if (!outfitSprites || outfitSprites.length == 0) { setTimeout(function () { self.generateOutfitImage(imageGenerator, otbManager, datManager, zip, outfitId + 1); diff --git a/package-lock.json b/package-lock.json index e77a39d..2baff28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "open-tibia-library", - "version": "0.0.2", + "version": "0.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 39b1f61..9d9fa84 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-tibia-library", - "version": "0.0.2", + "version": "0.1.0", "description": "OpenTibiaLibrary", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", diff --git a/tests.ts b/tests.ts index e86b7fa..e420c87 100755 --- a/tests.ts +++ b/tests.ts @@ -7,6 +7,7 @@ import {InputFile} from "./modules/fileHandlers/inputFile"; import {ImageGenerator} from "./modules/imageGenerator/imageGenerator"; import {Size} from "./modules/structures/size"; import {Point} from "./modules/structures/point"; +import {FrameGroupType} from "./modules/constants/const"; const canvas = document.getElementById('view'); const ctx = canvas.getContext("2d"); @@ -19,9 +20,9 @@ function drawImage(sprite: Sprite, x, y) { async function testGenerateAllItemImages() { const client = new Client(); - client.setClientVersion(860); + client.setClientVersion(1076); - const serverUrl = 'http://127.0.0.1/Tibia860/'; + const serverUrl = 'http://127.0.0.1/1076/'; const datManager = new DatManager(client); await datManager.loadDatFromUrl(serverUrl + 'Tibia.dat').then(datLoaded => { @@ -42,9 +43,9 @@ async function testGenerateAllItemImages() { async function testLoadFromUrlsAndDrawImage() { const client = new Client(); - client.setClientVersion(860); + client.setClientVersion(1076); - const serverUrl = 'http://127.0.0.1/Tibia860/'; + const serverUrl = 'http://127.0.0.1/1076/'; const datManager = new DatManager(client); await datManager.loadDatFromUrl(serverUrl + 'Tibia.dat').then(datLoaded => { @@ -66,7 +67,7 @@ async function testLoadFromUrlsAndDrawImage() { // get data from '.dat' about that item let magicSwordThingType = datManager.getItem(magicSwordClientId); // get first sprite [image] of that item - let firstMagicSwordSprite = magicSwordThingType.getSprite(0); + let firstMagicSwordSprite = magicSwordThingType.getFrameGroup(FrameGroupType.FrameGroupIdle).getSprite(0); // get image from .spr file let firstImagePixelsData = spriteManager.getSprite(firstMagicSwordSprite); // draw image in webbrowser with Canvas on position 0, 0 @@ -89,6 +90,16 @@ async function testLoadFromUrlsAndDrawImage() { spriteManager: spriteManager }; + + // let otbFile = otbManager.saveOtb(); + // let a = document.createElement('a'); + // let url = window.URL.createObjectURL(new Blob(new Array(otbFile.getUint8Array()))); + // a.href = url; + // a.download = 'items.otb'; + // a.click(); + // window.URL.revokeObjectURL(url); + // a.remove(); + // console.log('Generated dat file', datManager.saveDat()); // console.log('Generated otb file', otbManager.saveOtb()); // console.log('Generated spr file', spriteManager.saveSpr()); @@ -117,7 +128,7 @@ async function testFilePicker() { try { let magicSwordClientId = otbManager.getItem(itemid).getClientId(); let magicSwordThingType = datManager.getItem(magicSwordClientId); - let firstMagicSwordSprite = magicSwordThingType.getSprite(0); + let firstMagicSwordSprite = magicSwordThingType.getFrameGroup(FrameGroupType.FrameGroupIdle).getSprite(0); let firstImagePixelsData = sprManager.getSprite(firstMagicSwordSprite); drawImage(firstImagePixelsData, 0, 0); @@ -136,9 +147,6 @@ async function testFilePicker() { otbPicker.onchange(null); updateItemView(parseInt(itemIdInput.value)); }; - itemIdInput.onchange = function (event) { - updateItemView(parseInt(itemIdInput.value)); - }; sprPicker.onchange = function (event) { if (sprPicker.files.length > 0) { @@ -178,10 +186,14 @@ async function testFilePicker() { } } }; + + itemIdInput.onchange = function (event) { + updateItemView(parseInt(itemIdInput.value)); + }; } -testGenerateAllItemImages(); -// testLoadFromUrlsAndDrawImage(); +// testGenerateAllItemImages(); +testLoadFromUrlsAndDrawImage(); // testFilePicker(); /* download OTB: @@ -194,4 +206,4 @@ a.download = 'items.otb'; a.click(); window.URL.revokeObjectURL(url); a.remove(); - */ \ No newline at end of file + */ diff --git a/tools/colored-outfit-images-generator/animoutfit.php b/tools/colored-outfit-images-generator/animoutfit.php index 3ca3ad2..17f57b3 100644 --- a/tools/colored-outfit-images-generator/animoutfit.php +++ b/tools/colored-outfit-images-generator/animoutfit.php @@ -31,7 +31,7 @@ // Block sites that hotlink your host and overload it $abusersList = array('aurera-global.com', 'bad-server.com'); -if (in_array(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), $abusersList) || in_array(substr(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), 4), $abusersList)) { +if (isset($_SERVER['HTTP_REFERER']) && (in_array(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), $abusersList) || in_array(substr(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), 4), $abusersList))) { header('Content-Type: image/png'); readfile('abuse_warning.png'); exit; diff --git a/tools/colored-outfit-images-generator/libs/outfitter.php b/tools/colored-outfit-images-generator/libs/outfitter.php index 2ca5eb3..75f5e0e 100644 --- a/tools/colored-outfit-images-generator/libs/outfitter.php +++ b/tools/colored-outfit-images-generator/libs/outfitter.php @@ -73,10 +73,12 @@ public static function loadData($outfitId, $isMount = false) { $tmp = unserialize(file_get_contents(self::$outfitPath . $outfitId . '/outfit.data.txt')); self::$data['files'] = array_merge(self::$data['files'], $tmp['files']); + self::$data['mountFramesNumber'] = $tmp['framesNumber']; } else { self::$data = unserialize(file_get_contents(self::$outfitPath . $outfitId . '/outfit.data.txt')); + self::$data['mountFramesNumber'] = 1; } return true; } @@ -90,7 +92,7 @@ public static function getOutfitFramesNumber() public static function file_exists($filePath) { - return in_array(str_replace('\\', '/', $filePath), self::$data['files']); + return in_array(trim(trim(str_replace('\\', '/', $filePath), '.'), '/'), self::$data['files']); } public function outfit($outfit, $addons, $head, $body, $legs, $feet, $mount, $direction = 3, $animation = 1) { @@ -111,33 +113,19 @@ public function outfit($outfit, $addons, $head, $body, $legs, $feet, $mount, $di $mountId = $mount; $mountState = 2; } - $creature = false; - if ($creature) { - $tmpOutfit = null; - if (self::file_exists(self::$outfitPath . $outfit . '/'.$animation.'_1_1_'.$direction.'.png')) - $tmpOutfit = imagecreatefrompng(self::$outfitPath . $outfit . '/'.$animation.'_1_1_'.$direction.'.png'); - elseif (self::file_exists(self::$outfitPath . $outfit . '/1_1_1_3.png')) - $tmpOutfit = imagecreatefrompng(self::$outfitPath . $outfit . '/1_1_1_3.png'); - if ($tmpOutfit == null) - return false; - - $width = imagesx($tmpOutfit); - $height = imagesy($tmpOutfit); - $image_outfit = imagecreatetruecolor($width, $height); - imagefill($image_outfit, 0, 0, $bgcolor = imagecolorallocate($image_outfit, self::$transparentBackgroundColor[0], self::$transparentBackgroundColor[1], self::$transparentBackgroundColor[2])); - imagecopyresampled($image_outfit, $tmpOutfit, 0, 0, 0, 0, $width, $height, $width, $height); - imagecolortransparent($image_outfit, $bgcolor); + $image_outfit = imagecreatefrompng(self::$outfitPath . $outfit . '/'.$animation.'_' . $mountState . '_1_'.$direction.'.png'); + if (file_exists(self::$outfitPath . $outfit . '/'.$animation.'_' . $mountState . '_1_'.$direction.'_template.png')) { + $image_template = imagecreatefrompng(self::$outfitPath . $outfit . '/'.$animation.'_' . $mountState . '_1_'.$direction.'_template.png'); + } else { + $image_template = imagecreatetruecolor(imagesx($image_outfit), imagesy($image_outfit)); + $bgcolor = imagecolorallocate($image_template, self::$transparentBackgroundColor[0], self::$transparentBackgroundColor[1], self::$transparentBackgroundColor[2]); + imagecolortransparent($image_template, $bgcolor); - imagealphablending($image_outfit, false); - imagesavealpha($image_outfit, true); - imagedestroy($tmpOutfit); - return $image_outfit; + imagealphablending($image_template, false); + imagesavealpha($image_template, true); } - $image_outfit = imagecreatefrompng(self::$outfitPath . $outfit . '/'.$animation.'_' . $mountState . '_1_'.$direction.'.png'); - $image_template = imagecreatefrompng(self::$outfitPath . $outfit . '/'.$animation.'_' . $mountState . '_1_'.$direction.'_template.png'); - if ($addons == 1 || $addons == 3) { $image_first = imagecreatefrompng(self::$outfitPath . $outfit . '/'.$animation.'_' . $mountState . '_2_'.$direction.'.png'); $this->alphaOverlay($image_outfit, $image_first, 64, 64); @@ -162,8 +150,10 @@ public function outfit($outfit, $addons, $head, $body, $legs, $feet, $mount, $di } $this->colorize($image_template, $image_outfit, $head, $body, $legs, $feet); - if ($mountState == 2 && self::file_exists(self::$outfitPath . $mountId . '/'.$animation.'_1_1_'.$direction.'.png')) { - $mount = imagecreatefrompng(self::$outfitPath . $mountId . '/'.$animation.'_1_1_'.$direction.'.png'); + $mountAnimationFrame = max(1,(self::$data['mountFramesNumber'] % $animation)); + + if ($mountState == 2 && self::file_exists(self::$outfitPath . $mountId . '/'.$mountAnimationFrame.'_1_1_'.$direction.'.png')) { + $mount = imagecreatefrompng(self::$outfitPath . $mountId . '/'.$mountAnimationFrame.'_1_1_'.$direction.'.png'); $this->alphaOverlay($mount, $image_outfit, 64, 64); imagedestroy($image_outfit); $image_outfit = $mount; @@ -179,7 +169,9 @@ public function outfit($outfit, $addons, $head, $body, $legs, $feet, $mount, $di imagealphablending($image_outfitT, false); imagesavealpha($image_outfitT, true); imagedestroy($image_outfit); - imagedestroy($image_template); + if (isset($image_template)) { + imagedestroy($image_template); + } return $image_outfitT; } diff --git a/tools/colored-outfit-images-generator/outfit.php b/tools/colored-outfit-images-generator/outfit.php index 684c0b1..fb61258 100644 --- a/tools/colored-outfit-images-generator/outfit.php +++ b/tools/colored-outfit-images-generator/outfit.php @@ -18,8 +18,7 @@ // Block sites that hotlink your host and overload it $abusersList = array('aurera-global.com', 'bad-server.com'); -if(in_array(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), $abusersList) || in_array(substr(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), 4), $abusersList)) -{ +if (isset($_SERVER['HTTP_REFERER']) && (in_array(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), $abusersList) || in_array(substr(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST), 4), $abusersList))) { header('Content-Type: image/png'); readfile('abuse_warning.png'); exit; @@ -71,4 +70,4 @@ $animationFrame = 1; header('Content-type: image/gif'); -imagegif(Outfitter::instance()->outfit($_GET['id'], $_GET['addons'], $_GET['head'], $_GET['body'], $_GET['legs'], $_GET['feet'], $mount, $direction, $animationFrame)); +imagegif(Outfitter::instance()->outfit($id, $addons, $head, $body, $legs, $feet, $mount, $direction, $animationFrame));