1 package org.newdawn.slick;
2
3 import java.util.ArrayList;
4 import java.util.List;
5
6 import org.lwjgl.Sys;
7 import org.newdawn.slick.util.Log;
8
9 import javax.annotation.Nonnull;
10 import javax.annotation.Nullable;
11
12
13
14
15
16
17
18 public class Animation implements Renderable {
19
20 private final List<Frame> frames = new ArrayList<>();
21
22 private int currentFrame = -1;
23
24 private long nextChange = 0;
25
26 private boolean stopped = false;
27
28 private long timeLeft;
29
30 private float speed = 1.0f;
31
32 private int stopAt = -2;
33
34 private long lastUpdate;
35
36 private boolean firstUpdate = true;
37
38 private boolean autoUpdate = true;
39
40 private int direction = 1;
41
42 private boolean pingPong;
43
44 private boolean loop = true;
45
46 @Nullable
47 private SpriteSheet spriteSheet = null;
48
49 private long pauseDuration = 0;
50
51
52
53
54 private Animation() {
55 this(true);
56 }
57
58
59
60
61
62
63
64 public Animation(@Nonnull Image[] frames, int duration) {
65 this(frames, duration, true);
66 }
67
68
69
70
71
72
73
74 public Animation(@Nonnull Image[] frames, @Nonnull int[] durations) {
75 this(frames, durations, true);
76 }
77
78
79
80
81
82
83
84 private Animation(boolean autoUpdate) {
85 currentFrame = 0;
86 this.autoUpdate = autoUpdate;
87 }
88
89
90
91
92
93
94
95
96
97 private Animation(@Nonnull Image[] frames, int duration, boolean autoUpdate) {
98 for (Image frame : frames) {
99 addFrame(frame, duration);
100 }
101 currentFrame = 0;
102 this.autoUpdate = autoUpdate;
103 }
104
105
106
107
108
109
110
111
112
113 private Animation(@Nonnull Image[] frames, @Nonnull int[] durations, boolean autoUpdate) {
114 this.autoUpdate = autoUpdate;
115 if (frames.length != durations.length) {
116 throw new RuntimeException("There must be one duration per frame");
117 }
118
119 for (int i=0;i<frames.length;i++) {
120 addFrame(frames[i], durations[i]);
121 }
122 currentFrame = 0;
123 }
124
125
126
127
128
129
130
131
132
133 public Animation(@Nonnull SpriteSheet frames, int duration) {
134 this(frames, 0,0,frames.getHorizontalCount()-1,frames.getVerticalCount()-1,true,duration,true);
135 }
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 private Animation(@Nullable SpriteSheet frames, int x1, int y1, int x2, int y2, boolean horizontalScan, int duration, boolean autoUpdate) {
151 this.spriteSheet = frames;
152 this.autoUpdate = autoUpdate;
153
154 if (!horizontalScan) {
155 for (int x=x1;x<=x2;x++) {
156 for (int y=y1;y<=y2;y++) {
157
158 addFrame(duration, x, y);
159 }
160 }
161 } else {
162 for (int y=y1;y<=y2;y++) {
163 for (int x=x1;x<=x2;x++) {
164
165 addFrame(duration, x, y);
166 }
167 }
168 }
169 }
170
171
172
173
174
175
176
177 public Animation(@Nullable SpriteSheet ss, @Nonnull int[] frames, int[] duration){
178 spriteSheet = ss;
179 int x;
180 int y;
181
182 for(int i = 0; i < frames.length/2; i++){
183 x = frames[i*2];
184 y = frames[i*2 + 1];
185 addFrame(duration[i], x, y);
186 }
187 }
188
189
190
191
192
193
194
195 void addFrame(int duration, int x, int y){
196 if (duration == 0) {
197 Log.error("Invalid duration: "+duration);
198 throw new RuntimeException("Invalid duration: "+duration);
199 }
200
201 if (frames.isEmpty()) {
202 nextChange = (int) (duration / speed);
203 }
204
205 frames.add(new Frame(duration, x, y));
206 currentFrame = 0;
207 }
208
209
210
211
212
213
214
215
216 public void setAutoUpdate(boolean auto) {
217 this.autoUpdate = auto;
218 }
219
220
221
222
223
224
225 public void setPingPong(boolean pingPong) {
226 this.pingPong = pingPong;
227 }
228
229
230
231
232
233
234
235 public boolean isStopped() {
236 return stopped;
237 }
238
239
240
241
242
243
244 public void setSpeed(float spd) {
245 if (spd > 0) {
246
247 nextChange = (long) (nextChange * speed / spd);
248
249 speed = spd;
250 }
251 }
252
253
254
255
256
257
258 public float getSpeed() {
259 return speed;
260 }
261
262
263
264
265
266 public void stop() {
267 if (frames.size() == 0) {
268 return;
269 }
270 timeLeft = nextChange;
271 stopped = true;
272 }
273
274
275
276
277 public void start() {
278 if (!stopped) {
279 return;
280 }
281 if (frames.size() == 0) {
282 return;
283 }
284 stopped = false;
285 nextChange = timeLeft;
286 }
287
288
289
290
291 public void restart() {
292 if (frames.size() == 0) {
293 return;
294 }
295 stopped = false;
296 currentFrame = 0;
297 nextChange = (int) (frames.get(0).duration / speed);
298 firstUpdate = true;
299 lastUpdate = 0;
300 }
301
302
303
304
305
306
307
308 void addFrame(Image frame, int duration) {
309 if (duration == 0) {
310 Log.error("Invalid duration: "+duration);
311 throw new RuntimeException("Invalid duration: "+duration);
312 }
313
314 if (frames.isEmpty()) {
315 nextChange = (int) (duration / speed);
316 }
317
318 frames.add(new Frame(frame, duration));
319 currentFrame = 0;
320 }
321
322
323
324
325 public void draw() {
326 draw(0,0);
327 }
328
329
330
331
332
333
334
335 public void draw(float x,float y) {
336 draw(x,y,getWidth(),getHeight());
337 }
338
339
340
341
342
343
344
345
346 public void draw(float x,float y, Color filter) {
347 draw(x,y,getWidth(),getHeight(), filter);
348 }
349
350
351
352
353
354
355
356
357
358 void draw(float x, float y, float width, float height) {
359 draw(x,y,width,height,Color.white);
360 }
361
362
363
364
365
366
367
368
369
370
371 void draw(float x, float y, float width, float height, Color col) {
372 if (frames.size() == 0) {
373 return;
374 }
375
376 if (autoUpdate) {
377 long now = getTime();
378 long delta = now - lastUpdate;
379 if (firstUpdate) {
380 delta = 0;
381 firstUpdate = false;
382 }
383 lastUpdate = now;
384 nextFrame(delta);
385 }
386
387 Frame frame = frames.get(currentFrame);
388 frame.image.draw(x,y,width,height, col);
389 }
390
391
392
393
394
395
396 public void renderInUse(int x, int y){
397 if (frames.size() == 0) {
398 return;
399 }
400
401 if (autoUpdate) {
402 long now = getTime();
403 long delta = now - lastUpdate;
404 if (firstUpdate) {
405 delta = 0;
406 firstUpdate = false;
407 }
408 lastUpdate = now;
409 nextFrame(delta);
410 }
411
412 Frame frame = frames.get(currentFrame);
413
414 spriteSheet.renderInUse(x, y, frame.x, frame.y);
415 }
416
417
418
419
420
421
422
423 public void renderInUse(int x, int y, float rot){
424 if (frames.size() == 0) {
425 return;
426 }
427
428 if (autoUpdate) {
429 long now = getTime();
430 long delta = now - lastUpdate;
431 if (firstUpdate) {
432 delta = 0;
433 firstUpdate = false;
434 }
435 lastUpdate = now;
436 nextFrame(delta);
437 }
438
439
440
441 Frame frame = frames.get(currentFrame);
442 spriteSheet.renderInUse(x, y, rot, frame.x, frame.y);
443 }
444
445
446
447
448
449
450 int getWidth() {
451 return frames.get(currentFrame).image.getWidth();
452 }
453
454
455
456
457
458
459 int getHeight() {
460 return frames.get(currentFrame).image.getHeight();
461 }
462
463
464
465
466
467
468
469
470
471 public void drawFlash(float x,float y,float width,float height) {
472 drawFlash(x,y,width,height, Color.white);
473 }
474
475
476
477
478
479
480
481
482
483
484 void drawFlash(float x, float y, float width, float height, @Nonnull Color col) {
485 if (frames.size() == 0) {
486 return;
487 }
488
489 if (autoUpdate) {
490 long now = getTime();
491 long delta = now - lastUpdate;
492 if (firstUpdate) {
493 delta = 0;
494 firstUpdate = false;
495 }
496 lastUpdate = now;
497 nextFrame(delta);
498 }
499
500 Frame frame = frames.get(currentFrame);
501 frame.image.drawFlash(x,y,width,height,col);
502 }
503
504
505
506
507
508
509
510 public void updateNoDraw() {
511 if (autoUpdate) {
512 long now = getTime();
513 long delta = now - lastUpdate;
514 if (firstUpdate) {
515 delta = 0;
516 firstUpdate = false;
517 }
518 lastUpdate = now;
519 nextFrame(delta);
520 }
521 }
522
523
524
525
526
527
528
529
530 public void update(long delta) {
531 nextFrame(delta);
532 }
533
534
535
536
537
538
539 public int getFrame() {
540 return currentFrame;
541 }
542
543
544
545
546
547
548 public void setCurrentFrame(int index) {
549 currentFrame = index;
550 }
551
552
553
554
555
556
557
558 public Image getImage(int index) {
559 Frame frame = frames.get(index);
560 return frame.image;
561 }
562
563
564
565
566
567
568 public int getFrameCount() {
569 return frames.size();
570 }
571
572
573
574
575
576
577 public Image getCurrentFrame() {
578 Frame frame = frames.get(currentFrame);
579 return frame.image;
580 }
581
582
583
584
585
586
587 private void nextFrame(long delta) {
588 if (stopped) {
589 return;
590 }
591 if (frames.size() == 0) {
592 return;
593 }
594
595 nextChange -= delta;
596
597 while (nextChange < 0 && (!stopped)) {
598 if (currentFrame == stopAt) {
599 stopped = true;
600 break;
601 }
602 if ((currentFrame == frames.size() - 1) && (!loop) && (!pingPong)) {
603 stopped = true;
604 break;
605 }
606 currentFrame = (currentFrame + direction) % frames.size();
607
608 if (pingPong) {
609 if (currentFrame <= 0) {
610 currentFrame = 0;
611 direction = 1;
612 if (!loop) {
613 stopped = true;
614 break;
615 }
616 }
617 else if (currentFrame >= frames.size()-1) {
618 currentFrame = frames.size()-1;
619 direction = -1;
620 }
621 }
622 int realDuration = (int) (frames.get(currentFrame).duration/ speed);
623 nextChange = nextChange + realDuration;
624
625
626 if(pauseDuration >= 0 && currentFrame == 0)
627 nextChange += pauseDuration;
628
629
630 }
631 }
632
633
634
635
636
637
638 public void setLooping(boolean loop) {
639 this.loop = loop;
640 }
641
642
643
644
645
646
647 private long getTime() {
648 return (Sys.getTime() * 1000) / Sys.getTimerResolution();
649 }
650
651
652
653
654
655
656
657 public void stopAt(int frameIndex) {
658 stopAt = frameIndex;
659 }
660
661
662
663
664
665
666
667 int getDuration(int index) {
668 return frames.get(index).duration;
669 }
670
671
672
673
674
675
676
677 public void setDuration(int index, int duration) {
678 frames.get(index).duration = duration;
679 }
680
681
682
683
684
685
686 @Nonnull
687 public int[] getDurations() {
688 int[] durations = new int[frames.size()];
689 for (int i=0;i<frames.size();i++) {
690 durations[i] = getDuration(i);
691 }
692
693 return durations;
694 }
695
696
697
698
699
700 public boolean isPause ()
701 {
702 return pauseDuration > 0;
703 }
704
705
706
707
708
709
710 public long getPauseDuration ()
711 {
712 return pauseDuration;
713 }
714
715
716
717
718
719 public void setPauseDuration ( long pauseDuration )
720 {
721 this.pauseDuration = pauseDuration;
722 }
723
724
725
726
727
728 @Nonnull
729 public String toString() {
730 String res = "[Animation ("+frames.size()+") ";
731 for (Frame frame : frames) {
732 res += frame.duration + ",";
733 }
734
735 res += "]";
736 return res;
737 }
738
739
740
741
742
743
744
745 @Nonnull
746 public Animation copy() {
747 Animation copy = new Animation();
748
749 copy.spriteSheet = spriteSheet;
750 copy.frames.addAll(frames);
751 copy.autoUpdate = autoUpdate;
752 copy.direction = direction;
753 copy.loop = loop;
754 copy.pingPong = pingPong;
755 copy.speed = speed;
756
757 return copy;
758 }
759
760
761
762
763
764
765 private class Frame {
766
767 public final Image image;
768
769 public int duration;
770
771 public int x = -1;
772
773 public int y = -1;
774
775
776
777
778
779
780
781 public Frame(Image image, int duration) {
782 this.image = image;
783 this.duration = duration;
784 }
785
786
787
788
789
790
791
792 public Frame(int duration, int x, int y) {
793 this.image = spriteSheet.getSubImage(x, y);
794 this.duration = duration;
795 this.x = x;
796 this.y = y;
797 }
798 }
799 }