Passed
Push — depfu/update/npm/lodash-4.17.1... ( c1263d )
by
unknown
04:21
created

src/app/data/savefile-expanded/fragments/SpriteData.ts   A

Complexity

Total Complexity 19
Complexity/F 1.9

Size

Lines of Code 281
Function Count 10

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 184
dl 0
loc 281
rs 10
c 0
b 0
f 0
wmc 19
mnd 9
bc 9
fnc 10
bpm 0.9
cpm 1.9
noi 0

10 Functions

Rating   Name   Duplication   Size   Complexity  
A SpriteData.grabSpriteDataNPC 0 15 1
A SpriteData.save 0 11 2
A SpriteData.saveSpriteData1 0 14 1
A SpriteData.grabSpriteData1 0 15 1
A SpriteData.load 0 13 4
A SpriteData.saveSpriteDataNPC 0 12 1
A SpriteData.saveMissables 0 21 3
A SpriteData.saveSpriteData2 0 13 1
A SpriteData.grabSpriteData2 0 14 1
A SpriteData.checkMissable 0 28 4
1
import { SaveFileService } from './../../savefile.service';
2
3
export class SpriteData {
4
5
    /**
6
     * 0 arguments or false - Empty player character
7
     * 1 argument true - Empty NPC character
8
     * 2 arguments - Load data with savefile and index
9
     */
10
    constructor(savefile?: SaveFileService | boolean, index?: number) {
11
        if ((typeof savefile === "object") && savefile !== null)
12
            this.load(savefile as SaveFileService, index as number);
13
        else if (savefile === true) {
14
            this.rangeDirByte = 0;
15
            this.textID = 0;
16
            this.trainerClassOrItemID = 0;
17
            this.trainerSetID = 0;
18
            this.missableIndex = -1;
19
        }
20
    }
21
22
    // Load data all sprites on the map have
23
    public load(savefile: SaveFileService, index: number) {
24
25
        // Grab sprite data 1 and 2 which applies to all sprites
26
        this.grabSpriteData1(savefile, index);
27
        this.grabSpriteData2(savefile, index);
28
29
        // If this isn't the player sprite then load additional non-player data
30
        // and check to see if it's missable
31
        if (index > 0) {
32
            this.grabSpriteDataNPC(savefile, index);
33
            this.checkMissable(savefile, index);
34
        }
35
    }
36
37
    // Don't correct index, this data starts at sprite 0
38
    grabSpriteData1(saveFile: SaveFileService, index: number) {
39
40
        const it = saveFile.iterator.offsetTo((0x10 * index) + 0x2D2C);
41
        this.pictureID = it.getByte();
42
        this.movementStatus = it.getByte();
43
        this.imageIndex = it.getByte();
44
        this.yStepVector = it.getByte();
45
        this.yPixels = it.getByte();
46
        this.xStepVector = it.getByte();
47
        this.xPixels = it.getByte();
48
        this.intraAnimationFrameCounter = it.getByte();
49
        this.animFrameCounter = it.getByte();
50
        this.faceDir = it.getByte();
51
    }
52
53
    // Don't correct index, this data starts at sprite 0
54
    grabSpriteData2(saveFile: SaveFileService, index: number) {
55
56
        const it = saveFile.iterator.offsetTo((0x10 * index) + 0x2E2C);
57
        this.walkAnimationCounter = it.getByte(1);
58
        this.yDisp = it.getByte();
59
        this.xDisp = it.getByte();
60
        this.mapY = it.getByte();
61
        this.mapX = it.getByte();
62
        this.movementByte = it.getByte();
63
        this.grassPriority = it.getByte();
64
        this.movementDelay = it.getByte(5);
65
        this.imageBaseOffset = it.getByte();
66
    }
67
68
    // Correct index, this data starts sprite 0 at sprite 1
69
    grabSpriteDataNPC(saveFile: SaveFileService, index: number) {
70
        index--;
71
72
        // Init missable index to non-player value
73
        this.missableIndex = -1;
74
75
        const it = saveFile.iterator.offsetTo((2 * index) + 0x2790);
76
        this.rangeDirByte = it.getByte();
77
        this.textID = it.getByte();
78
79
        it.offsetTo((2 * index) + 0x27B0);
80
        this.trainerClassOrItemID = it.getByte();
81
        this.trainerSetID = it.getByte();
82
    }
83
84
    // Scan missable list to see if this sprite is in it
85
    // If a match is found load missable data making this sprite a missable
86
    // sprite and therefore have it's appearance controlled by the global
87
    // missable flags
88
89
    // Don't correct index, this data starts at sprite 1
90
    checkMissable(saveFile: SaveFileService, index: number) {
91
92
        const it = saveFile.iterator;
93
94
        for (let i = 0; i < 17; i++) {
95
            it.offsetTo((0x2 * i) + 0x287A);
96
            const mId = it.getByte();
97
            const mIndex = it.getByte();
98
99
            // Stop when reach list terminator
100
            if (mId == 0xFF)
101
                break;
102
103
            // Skip if the id doesn't match this sprite
104
            if (mId !== index)
105
                continue;
106
107
            // Save bit index this missable refers to and add to missable
108
            // array for quick reference
109
            this.missableIndex = mIndex;
110
            break;
111
        }
112
    }
113
114
    // Missables will have to be called seperate
115
    public save(savefile: SaveFileService, index: number) {
116
117
        // Grab sprite data 1 and 2 which applies to all sprites
118
        this.saveSpriteData1(savefile, index);
119
        this.saveSpriteData2(savefile, index);
120
121
        // If this isn't the player sprite then save NPC data
122
        if (index > 0) {
123
            this.saveSpriteDataNPC(savefile, index);
124
        }
125
    }
126
127
    saveSpriteData1(saveFile: SaveFileService, index: number) {
128
129
        const it = saveFile.iterator.offsetTo((0x10 * index) + 0x2D2C);
130
        it.setByte(this.pictureID)
131
        it.setByte(this.movementStatus)
132
        it.setByte(this.imageIndex)
133
        it.setByte(this.yStepVector)
134
        it.setByte(this.yPixels)
135
        it.setByte(this.xStepVector)
136
        it.setByte(this.xPixels)
137
        it.setByte(this.intraAnimationFrameCounter)
138
        it.setByte(this.animFrameCounter)
139
        it.setByte(this.faceDir)
140
    }
141
142
    saveSpriteData2(saveFile: SaveFileService, index: number) {
143
144
        const it = saveFile.iterator.offsetTo((0x10 * index) + 0x2E2C);
145
        it.setByte(this.walkAnimationCounter, 1);
146
        it.setByte(this.yDisp)
147
        it.setByte(this.xDisp)
148
        it.setByte(this.mapY)
149
        it.setByte(this.mapX)
150
        it.setByte(this.movementByte)
151
        it.setByte(this.grassPriority)
152
        it.setByte(this.movementDelay, 5);
153
        it.setByte(this.imageBaseOffset)
154
    }
155
156
    saveSpriteDataNPC(saveFile: SaveFileService, index: number) {
157
158
        // Correct index, this data starts sprite 0 at sprite 1
159
        index--;
160
        const it = saveFile.iterator.offsetTo((2 * index) + 0x2790);
161
        it.setByte(this.rangeDirByte as number);
162
        it.setByte(this.textID as number);
163
164
        it.offsetTo((2 * index) + 0x27B0);
165
        it.setByte(this.trainerClassOrItemID as number);
166
        it.setByte(this.trainerSetID as number);
167
    }
168
169
    // Because missables is somewhat tricky, it needs to be called seperately
170
    static saveMissables(saveFile: SaveFileService, spriteData: SpriteData[]) {
171
        const it = saveFile.iterator;
172
173
        it.offsetTo(0x287A);
174
        for (let i = 0; i < spriteData.length && i < 16; i++) {
175
            const val = spriteData[i];
176
177
            // Skip all sprites that aren't missables
178
            if (val.missableIndex === null || !(val.missableIndex >= 0))
179
                continue;
180
181
            // Save sprite index that's missable
182
            // Don't correct index, this data starts at sprite 1
183
            it.setByte(i);
184
185
            // Save missable index this sprite connects to
186
            it.setByte(val.missableIndex);
187
        }
188
        it.setByte(0xFF);
189
    }
190
191
    /**
192
     * Sprite data that applies to all sprites
193
     */
194
195
    // Actual sprite image shown
196
    public pictureID: number = 0;
197
198
    // (0: uninitialized, 1: ready, 2: delayed, 3: moving)
199
    public movementStatus: number = 0;
200
201
    // Basically, in the sprite sheet strip, which "pane" or "tile" is it at
202
    // 0xFF if not on the screen
203
    public imageIndex: number = 0;
204
205
    // When the sprite moves, exactly how far or how much is that?
206
    //(-1, 0 or 1)
207
    public yStepVector: number = 0;
208
209
    // Screen position in pixels aligned to 4 pixels offset from the grid (To appear centered)
210
    public yPixels: number = 0;
211
212
    // When the sprite moves, exactly how far or how much is that?
213
    //(-1, 0 or 1)
214
    public xStepVector: number = 0;
215
216
    // Screen position in pixels aligned to 4 pixels offset from the grid (To appear centered)
217
    public xPixels: number = 0;
218
219
    // Counter that helps delay between animation frames so things aren't so instant and fast
220
    public intraAnimationFrameCounter: number = 0;
221
222
    // Animation frame counter
223
    public animFrameCounter: number = 0;
224
225
    // (0: down, 4: up, 8: left, $c: right)
226
    public faceDir: number = 0;
227
228
    // Tracks movement & wandering, sprites are given 0x10 and it's decremented
229
    public walkAnimationCounter: number = 0;
230
231
    // Keep sprites from wandering too far however it's noted that it's bugged
232
    // to begin with. Both are init to 0x8
233
    public yDisp: number = 0;
234
    public xDisp: number = 0;
235
236
    // Placement in 2x2 grid steps
237
    // Origin (Top or Left) has a value of 4
238
    public mapY: number = 0;
239
    public mapX: number = 0;
240
241
    // (0xFF not moving, 0xFE random movements, others unknown)
242
    public movementByte: number = 0;
243
244
    // (0x80 in grass, 0x00 otherwise) - Prioritizing grass drawn around sprite
245
    public grassPriority: number = 0;
246
247
    // Delay until next movement, counts downward and flags movementStatus ready
248
    // once reached
249
    public movementDelay: number = 0;
250
251
    // Used to help compute imageIndex based on vram
252
    public imageBaseOffset: number = 0;
253
254
    /**
255
     * Sprite data that applies to all non-player sprites
256
     * (All sprites that aren't sprite #0)
257
     */
258
259
    // How far a walking sprite can wander, or if still the facing direction
260
    // A walking sprite having a value of 0 faces all directions
261
    public rangeDirByte: number | null = null;
262
263
    // Text id when this sprite is interacted with
264
    public textID: number | null = null;
265
266
    // If this is an item sprite, the item id, otherwise the trainer class
267
    public trainerClassOrItemID: null | number = null;
268
269
    // Trainer data id
270
    public trainerSetID: null | number = null;
271
272
    /**
273
     * Sprite data that applies to all non-player missable sprites
274
     * (All sprites that aren't sprite #0 and have an associated index in the
275
     * global missable list which determine if it's rendered or not)
276
     */
277
    // If this is not null, then this sprite is a missable and it's appearance
278
    // is determined by the flag in the global missable index this points to
279
    public missableIndex: null | number = null;
280
}
281