Subversion Repositories spacemission

Rev

Rev 4 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 daniel-mar 1
unit DXSprite;
2
 
3
interface
4
 
5
{$INCLUDE DelphiXcfg.inc}
6
 
7
uses
8
  Windows, SysUtils, Classes, DXClass, DXDraws, DirectX;
9
 
10
type
11
 
12
  {  ESpriteError  }
13
 
14
  ESpriteError = class(Exception);
15
 
16
  {  TSprite  }
17
 
18
  TSpriteEngine = class;
19
 
20
  TSprite = class
21
  private
22
    FEngine: TSpriteEngine;
23
    FParent: TSprite;
24
    FList: TList;
25
    FDeaded: Boolean;
26
    FDrawList: TList;
27
    FCollisioned: Boolean;
28
    FMoved: Boolean;
29
    FVisible: Boolean;
30
    FX: Double;
31
    FY: Double;
32
    FZ: Integer;
33
    FWidth: Integer;
34
    FHeight: Integer;
35
    procedure Add(Sprite: TSprite);
36
    procedure Remove(Sprite: TSprite);
37
    procedure AddDrawList(Sprite: TSprite);
38
    procedure Collision2;
39
    procedure Draw;
40
    function GetClientRect: TRect;
41
    function GetCount: Integer;
42
    function GetItem(Index: Integer): TSprite;
43
    function GetWorldX: Double;
44
    function GetWorldY: Double;
45
    procedure SetZ(Value: Integer);
46
  protected
47
    procedure DoCollision(Sprite: TSprite; var Done: Boolean); virtual;
48
    procedure DoDraw; virtual;
49
    procedure DoMove(MoveCount: Integer); virtual;
50
    function GetBoundsRect: TRect; virtual;
51
    function TestCollision(Sprite: TSprite): Boolean; virtual;
52
  public
53
    constructor Create(AParent: TSprite); virtual;
54
    destructor Destroy; override;
55
    procedure Clear;
56
    function Collision: Integer;
57
    procedure Dead;
58
    procedure Move(MoveCount: Integer);
59
    function GetSpriteAt(X, Y: Integer): TSprite;
60
    property BoundsRect: TRect read GetBoundsRect;
61
    property ClientRect: TRect read GetClientRect;
62
    property Collisioned: Boolean read FCollisioned write FCollisioned;
63
    property Count: Integer read GetCount;
64
    property Engine: TSpriteEngine read FEngine;
65
    property Items[Index: Integer]: TSprite read GetItem; default;
66
    property Moved: Boolean read FMoved write FMoved;
67
    property Parent: TSprite read FParent;
68
    property Visible: Boolean read FVisible write FVisible;
69
    property Width: Integer read FWidth write FWidth;
70
    property WorldX: Double read GetWorldX;
71
    property WorldY: Double read GetWorldY;
72
    property Height: Integer read FHeight write FHeight;
73
    property X: Double read FX write FX;
74
    property Y: Double read FY write FY;
75
    property Z: Integer read FZ write SetZ;
76
  end;
77
 
78
  {  TImageSprite  }
79
 
80
  TImageSprite = class(TSprite)
81
  private
82
    FAnimCount: Integer;
83
    FAnimLooped: Boolean;
84
    FAnimPos: Double;
85
    FAnimSpeed: Double;
86
    FAnimStart: Integer;
87
    FImage: TPictureCollectionItem;
88
    FPixelCheck: Boolean;
89
    FTile: Boolean;
90
    FTransparent: Boolean;
91
    function GetDrawImageIndex: Integer;
92
    function GetDrawRect: TRect;
93
  protected
94
    procedure DoDraw; override;
95
    procedure DoMove(MoveCount: Integer); override;
96
    function GetBoundsRect: TRect; override;
97
    function TestCollision(Sprite: TSprite): Boolean; override;
98
  public
99
    constructor Create(AParent: TSprite); override;
100
    property AnimCount: Integer read FAnimCount write FAnimCount;
101
    property AnimLooped: Boolean read FAnimLooped write FAnimLooped;
102
    property AnimPos: Double read FAnimPos write FAnimPos;
103
    property AnimSpeed: Double read FAnimSpeed write FAnimSpeed;
104
    property AnimStart: Integer read FAnimStart write FAnimStart;
105
    property PixelCheck: Boolean read FPixelCheck write FPixelCheck;
106
    property Image: TPictureCollectionItem read FImage write FImage;
107
    property Tile: Boolean read FTile write FTile;
108
  end;
109
 
110
  {  TImageSpriteEx  }
111
 
112
  TImageSpriteEx = class(TImageSprite)
113
  private
114
    FAngle: Integer;
115
    FAlpha: Integer;
116
  protected
117
    procedure DoDraw; override;
118
    function GetBoundsRect: TRect; override;
119
    function TestCollision(Sprite: TSprite): Boolean; override;
120
  public
121
    constructor Create(AParent: TSprite); override;
122
    property Angle: Integer read FAngle write FAngle;
123
    property Alpha: Integer read FAlpha write FAlpha;
124
  end;
125
 
126
  {  TBackgroundSprite  }
127
 
128
  TBackgroundSprite = class(TSprite)
129
  private
130
    FImage: TPictureCollectionItem;
131
    FCollisionMap: Pointer;
132
    FMap: Pointer;
133
    FMapWidth: Integer;
134
    FMapHeight: Integer;
135
    FTile: Boolean;
136
    function GetCollisionMapItem(X, Y: Integer): Boolean;
137
    function GetChip(X, Y: Integer): Integer;
138
    procedure SetChip(X, Y: Integer; Value: Integer);
139
    procedure SetCollisionMapItem(X, Y: Integer; Value: Boolean);
140
    procedure SetMapHeight(Value: Integer);
141
    procedure SetMapWidth(Value: Integer);
142
  protected
143
    procedure DoDraw; override;
144
    function GetBoundsRect: TRect; override;
145
    function TestCollision(Sprite: TSprite): Boolean; override;
146
  public
147
    constructor Create(AParent: TSprite); override;
148
    destructor Destroy; override;
149
    procedure SetMapSize(AMapWidth, AMapHeight: Integer);
150
    property Chips[X, Y: Integer]: Integer read GetChip write SetChip;
151
    property CollisionMap[X, Y: Integer]: Boolean read GetCollisionMapItem write SetCollisionMapItem;
152
    property Image: TPictureCollectionItem read FImage write FImage;
153
    property MapHeight: Integer read FMapHeight write SetMapHeight;
154
    property MapWidth: Integer read FMapWidth write SetMapWidth;
155
    property Tile: Boolean read FTile write FTile;
156
  end;
157
 
158
  {  TSpriteEngine  }
159
 
160
  TSpriteEngine = class(TSprite)
161
  private
162
    FAllCount: Integer;
163
    FCollisionCount: Integer;
164
    FCollisionDone: Boolean;
165
    FCollisionRect: TRect;
166
    FCollisionSprite: TSprite;
167
    FDeadList: TList;
168
    FDrawCount: Integer;
169
    FSurface: TDirectDrawSurface;
170
    FSurfaceRect: TRect;
171
    procedure SetSurface(Value: TDirectDrawSurface);
172
  public
173
    constructor Create(AParent: TSprite); override;
174
    destructor Destroy; override;
175
    procedure Dead;
176
    procedure Draw;
177
    property AllCount: Integer read FAllCount;
178
    property DrawCount: Integer read FDrawCount;
179
    property Surface: TDirectDrawSurface read FSurface write SetSurface;
180
    property SurfaceRect: TRect read FSurfaceRect;
181
  end;
182
 
183
  {  EDXSpriteEngineError  }
184
 
185
  EDXSpriteEngineError = class(Exception);
186
 
187
  {  TCustomDXSpriteEngine  }
188
 
189
  TCustomDXSpriteEngine = class(TComponent)
190
  private
191
    FDXDraw: TCustomDXDraw;
192
    FEngine: TSpriteEngine;
193
    procedure DXDrawNotifyEvent(Sender: TCustomDXDraw; NotifyType: TDXDrawNotifyType);
194
    procedure SetDXDraw(Value: TCustomDXDraw);
195
  protected
196
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
197
  public
198
    constructor Create(AOnwer: TComponent); override;
199
    destructor Destroy; override;
200
    procedure Dead;
201
    procedure Draw;
202
    procedure Move(MoveCount: Integer);
203
    property DXDraw: TCustomDXDraw read FDXDraw write SetDXDraw;
204
    property Engine: TSpriteEngine read FEngine;                
205
  end;
206
 
207
  {  TDXSpriteEngine  }
208
 
209
  TDXSpriteEngine = class(TCustomDXSpriteEngine)
210
  published
211
    property DXDraw;
212
  end;
213
 
214
implementation
215
 
216
uses DXConsts;
217
 
218
function Mod2(i, i2: Integer): Integer;
219
begin
220
  Result := i mod i2;
221
  if Result<0 then
222
    Result := i2+Result;
223
end;
224
 
225
function Mod2f(i: Double; i2: Integer): Double;
226
begin
227
  if i2=0 then
228
    Result := i
229
  else
230
  begin
231
    Result := i-Trunc(i/i2)*i2;
232
    if Result<0 then
233
      Result := i2+Result;
234
  end;
235
end;
236
 
237
{  TSprite  }
238
 
239
constructor TSprite.Create(AParent: TSprite);
240
begin
241
  inherited Create;
242
  FParent := AParent;
243
  if FParent<>nil then
244
  begin
245
    FParent.Add(Self);
246
    if FParent is TSpriteEngine then
247
      FEngine := TSpriteEngine(FParent)
248
    else
249
      FEngine := FParent.Engine;
250
    Inc(FEngine.FAllCount);
251
  end;
252
 
253
  FCollisioned := True;
254
  FMoved := True;
255
  FVisible := True;
256
end;
257
 
258
destructor TSprite.Destroy;
259
begin
260
  Clear;
261
  if FParent<>nil then
262
  begin
263
    Dec(FEngine.FAllCount);
264
    FParent.Remove(Self);
265
    FEngine.FDeadList.Remove(Self);
266
  end;
267
  FList.Free;
268
  FDrawList.Free;
269
  inherited Destroy;
270
end;
271
 
272
procedure TSprite.Add(Sprite: TSprite);
273
begin
274
  if FList=nil then
275
  begin
276
    FList := TList.Create;
277
    FDrawList := TList.Create;
278
  end;
279
  FList.Add(Sprite);
280
  AddDrawList(Sprite);
281
end;
282
 
283
procedure TSprite.Remove(Sprite: TSprite);
284
begin
285
  FList.Remove(Sprite);
286
  FDrawList.Remove(Sprite);
287
  if FList.Count=0 then
288
  begin
289
    FList.Free;
290
    FList := nil;
291
    FDrawList.Free;
292
    FDrawList := nil;
293
  end;
294
end;
295
 
296
procedure TSprite.AddDrawList(Sprite: TSprite);
297
var
298
  L, H, I, C: Integer;
299
begin
300
  L := 0;
301
  H := FDrawList.Count - 1;
302
  while L <= H do
303
  begin
304
    I := (L + H) div 2;
305
    C := TSprite(FDrawList[I]).Z-Sprite.Z;
306
    if C < 0 then L := I + 1 else
307
      H := I - 1;
308
  end;
309
  FDrawList.Insert(L, Sprite);
310
end;
311
 
312
procedure TSprite.Clear;
313
begin
314
  while Count>0 do
315
    Items[Count-1].Free;
316
end;
317
 
318
function TSprite.Collision: Integer;
319
var
320
  i: Integer;
321
begin
322
  Result := 0;
323
  if (FEngine<>nil) and (not FDeaded) and (Collisioned) then
324
  begin
325
    with FEngine do
326
    begin
327
      FCollisionCount := 0;
328
      FCollisionDone := False;
329
      FCollisionRect := Self.BoundsRect;
330
      FCollisionSprite := Self;
331
 
332
      for i:=0 to Count-1 do
333
        Items[i].Collision2;
334
 
335
      Result := FCollisionCount;
336
    end;
337
  end;
338
end;
339
 
340
procedure TSprite.Collision2;
341
var
342
  i: Integer;
343
begin
344
  if Collisioned then
345
  begin
346
    if (Self<>FEngine.FCollisionSprite) and OverlapRect(BoundsRect, FEngine.FCollisionRect) and
347
      FEngine.FCollisionSprite.TestCollision(Self) and TestCollision(FEngine.FCollisionSprite) then
348
    begin
349
      Inc(FEngine.FCollisionCount);
350
      FEngine.FCollisionSprite.DoCollision(Self, FEngine.FCollisionDone);
351
      if (not FEngine.FCollisionSprite.Collisioned) or (FEngine.FCollisionSprite.FDeaded) then
352
      begin
353
        FEngine.FCollisionDone := True;
354
      end;
355
    end;
356
    if FEngine.FCollisionDone then Exit;
357
    for i:=0 to Count-1 do
358
      Items[i].Collision2;
359
  end;
360
end;
361
 
362
procedure TSprite.Dead;
363
begin
364
  if (FEngine<>nil) and (not FDeaded) then
365
  begin
366
    FDeaded := True;
367
    FEngine.FDeadList.Add(Self);
368
  end;
369
end;
370
 
371
procedure TSprite.DoMove;
372
begin
373
end;
374
 
375
procedure TSprite.DoDraw;
376
begin
377
end;
378
 
379
procedure TSprite.DoCollision(Sprite: TSprite; var Done: Boolean);
380
begin
381
end;
382
 
383
function TSprite.TestCollision(Sprite: TSprite): Boolean;
384
begin
385
  Result := True;
386
end;
387
 
388
procedure TSprite.Move(MoveCount: Integer);
389
var
390
  i: Integer;
391
begin
392
  if FMoved then
393
  begin
394
    DoMove(MoveCount);
395
    for i:=0 to Count-1 do
396
      Items[i].Move(MoveCount);
397
  end;
398
end;
399
 
400
procedure TSprite.Draw;
401
var
402
  i: Integer;
403
begin
404
  if FVisible then
405
  begin
406
    if FEngine<>nil then
407
    begin
408
      if OverlapRect(FEngine.FSurfaceRect, BoundsRect) then
409
      begin
410
        DoDraw;
411
        Inc(FEngine.FDrawCount);
412
      end;
413
    end;
414
 
415
    if FDrawList<>nil then
416
    begin
417
      for i:=0 to FDrawList.Count-1 do
418
        TSprite(FDrawList[i]).Draw;
419
    end;
420
  end;
421
end;
422
 
423
function TSprite.GetSpriteAt(X, Y: Integer): TSprite;
424
 
425
  procedure Collision_GetSpriteAt(X, Y: Double; Sprite: TSprite);
426
  var
427
    i: Integer;
428
    X2, Y2: Double;
429
  begin
430
    if Sprite.Visible and PointInRect(Point(Round(X), Round(Y)), Bounds(Round(Sprite.X), Round(Sprite.Y), Sprite.Width, Sprite.Width)) then
431
    begin
432
      if (Result=nil) or (Sprite.Z>Result.Z) then
433
        Result := Sprite;
434
    end;
435
 
436
    X2 := X-Sprite.X;
437
    Y2 := Y-Sprite.Y;
438
    for i:=0 to Sprite.Count-1 do
439
      Collision_GetSpriteAt(X2, Y2, Sprite.Items[i]);
440
  end;
441
 
442
var
443
  i: Integer;
444
  X2, Y2: Double;
445
begin
446
  Result := nil;
447
 
448
  X2 := X-Self.X;
449
  Y2 := Y-Self.Y;
450
  for i:=0 to Count-1 do
451
    Collision_GetSpriteAt(X2, Y2, Items[i]);
452
end;                                    
453
 
454
function TSprite.GetBoundsRect: TRect;
455
begin
456
  Result := Bounds(Trunc(WorldX), Trunc(WorldY), Width, Height);
457
end;
458
 
459
function TSprite.GetClientRect: TRect;
460
begin
461
  Result := Bounds(0, 0, Width, Height);
462
end;
463
 
464
function TSprite.GetCount: Integer;
465
begin
466
  if FList<>nil then
467
    Result := FList.Count
468
  else
469
    Result := 0;
470
end;
471
 
472
function TSprite.GetItem(Index: Integer): TSprite;
473
begin
474
  if FList<>nil then
475
    Result := FList[Index]
476
  else
477
    raise ESpriteError.CreateFmt(SListIndexError, [Index]);
478
end;          
479
 
480
function TSprite.GetWorldX: Double;
481
begin
482
  if Parent<>nil then
483
    Result := Parent.WorldX+FX
484
  else
485
    Result := FX;
486
end;
487
 
488
function TSprite.GetWorldY: Double;
489
begin
490
  if Parent<>nil then
491
    Result := Parent.WorldY+FY
492
  else
493
    Result := FY;
494
end;
495
 
496
procedure TSprite.SetZ(Value: Integer);
497
begin
498
  if FZ<>Value then
499
  begin
500
    FZ := Value;
501
    if Parent<>nil then
502
    begin
503
      Parent.FDrawList.Remove(Self);
504
      Parent.AddDrawList(Self);
505
    end;
506
  end;
507
end;
508
 
509
{  TImageSprite  }
510
 
511
constructor TImageSprite.Create(AParent: TSprite);
512
begin
513
  inherited Create(AParent);
514
  FTransparent := True;
515
end;
516
 
517
function TImageSprite.GetBoundsRect: TRect;
518
var
519
  dx, dy: Integer;
520
begin
521
  dx := Trunc(WorldX);
522
  dy := Trunc(WorldY);
523
  if FTile then
524
  begin
525
    dx := Mod2(dx, FEngine.SurfaceRect.Right+Width);
526
    dy := Mod2(dy, FEngine.SurfaceRect.Bottom+Height);
527
 
528
    if dx>FEngine.SurfaceRect.Right then
529
      dx := (dx-FEngine.SurfaceRect.Right)-Width;
530
 
531
    if dy>FEngine.SurfaceRect.Bottom then
532
      dy := (dy-FEngine.SurfaceRect.Bottom)-Height;
533
  end;
534
 
535
  Result := Bounds(dx, dy, Width, Height);
536
end;
537
 
538
procedure TImageSprite.DoMove(MoveCount: Integer);
539
begin
540
  FAnimPos := FAnimPos + FAnimSpeed*MoveCount;
541
 
542
  if FAnimLooped then
543
  begin
544
    if FAnimCount>0 then
545
      FAnimPos := Mod2f(FAnimPos, FAnimCount)
546
    else
547
      FAnimPos := 0;
548
  end else
549
  begin
550
    if FAnimPos>=FAnimCount then
551
    begin
552
      FAnimPos := FAnimCount-1;
553
      FAnimSpeed := 0;
554
    end;
555
    if FAnimPos<0 then
556
    begin
557
      FAnimPos := 0;
558
      FAnimSpeed := 0;
559
    end;
560
  end;
561
end;
562
 
563
function TImageSprite.GetDrawImageIndex: Integer;
564
begin
565
  Result := FAnimStart+Trunc(FAnimPos);
566
end;
567
 
568
function TImageSprite.GetDrawRect: TRect;
569
begin
570
  Result := BoundsRect;
571
  OffsetRect(Result, (Width-Image.Width) div 2, (Height-Image.Height) div 2);
572
end;
573
 
574
procedure TImageSprite.DoDraw;
575
var
576
  ImageIndex: Integer;
577
  r: TRect;
578
begin
579
  ImageIndex := GetDrawImageIndex;
580
  r := GetDrawRect;
581
  Image.Draw(FEngine.Surface, r.Left, r.Top, ImageIndex);
582
end;
583
 
584
function ImageCollisionTest(suf1, suf2: TDirectDrawSurface; const rect1, rect2: TRect;
585
  x1,y1,x2,y2: Integer; DoPixelCheck: Boolean): Boolean;
586
 
587
  function ClipRect(var DestRect: TRect; const DestRect2: TRect): Boolean;
588
  begin
589
    with DestRect do
590
    begin
591
      Left := Max(Left, DestRect2.Left);
592
      Right := Min(Right, DestRect2.Right);
593
      Top := Max(Top, DestRect2.Top);
594
      Bottom := Min(Bottom, DestRect2.Bottom);
595
 
596
      Result := (Left < Right) and (Top < Bottom);
597
    end;
598
  end;
599
 
600
type
601
  PRGB = ^TRGB;
602
  TRGB = packed record
603
    R, G, B: Byte;
604
  end;
605
var
606
  ddsd1, ddsd2: TDDSurfaceDesc;
607
  r1, r2: TRect;
608
  tc1, tc2: DWORD;
609
  x, y, w, h: Integer;
610
  P1, P2: Pointer;
611
begin
612
  r1 := rect1;
613
  with rect2 do r2 := Bounds(x2-x1, y2-y1, Right-Left, Bottom-Top);
614
 
615
  Result := OverlapRect(r1, r2);
616
 
617
  if (suf1=nil) or (suf2=nil) then Exit;
618
 
619
  if DoPixelCheck and Result then
620
  begin
621
    {  Get Overlapping rectangle  }
622
    with r1 do r1 := Bounds(Max(x2-x1, 0), Max(y2-y1, 0), Right-Left, Bottom-Top);
623
    with r2 do r2 := Bounds(Max(x1-x2, 0), Max(y1-y2, 0), Right-Left, Bottom-Top);
624
 
625
    ClipRect(r1, rect1);
626
    ClipRect(r2, rect2);
627
 
628
    w := Min(r1.Right-r1.Left, r2.Right-r2.Left);
629
    h := Min(r1.Bottom-r1.Top, r2.Bottom-r2.Top);
630
 
631
    ClipRect(r1, bounds(r1.Left, r1.Top, w, h));
632
    ClipRect(r2, bounds(r2.Left, r2.Top, w, h));
633
 
634
    {  Pixel check !!!  }
635
    ddsd1.dwSize := SizeOf(ddsd1);
636
    if suf1.Lock(r1, ddsd1) then
637
    begin
638
      try
639
        ddsd2.dwSize := SizeOf(ddsd2);
640
        if (suf1=suf2) or suf2.Lock(r2, ddsd2) then
641
        begin
642
          try
643
            if suf1=suf2 then ddsd2 := ddsd1;
644
            if ddsd1.ddpfPixelFormat.dwRGBBitCount<>ddsd2.ddpfPixelFormat.dwRGBBitCount then Exit;
645
 
646
            {  Get transparent color  }
647
            tc1 := ddsd1.ddckCKSrcBlt.dwColorSpaceLowValue;
648
            tc2 := ddsd2.ddckCKSrcBlt.dwColorSpaceLowValue;
649
 
650
            case ddsd1.ddpfPixelFormat.dwRGBBitCount of
651
              8 : begin
652
                    for y:=0 to h-1 do
653
                    begin
654
                      P1 := Pointer(Integer(ddsd1.lpSurface)+y*ddsd1.lPitch);
655
                      P2 := Pointer(Integer(ddsd2.lpSurface)+y*ddsd2.lPitch);
656
                      for x:=0 to w-1 do
657
                      begin
658
                        if (PByte(P1)^<>tc1) and (PByte(P2)^<>tc2) then Exit;
659
                        Inc(PByte(P1));
660
                        Inc(PByte(P2));
661
                      end;
662
                    end;
663
                  end;
664
              16: begin
665
                    for y:=0 to h-1 do
666
                    begin
667
                      P1 := Pointer(Integer(ddsd1.lpSurface)+y*ddsd1.lPitch);
668
                      P2 := Pointer(Integer(ddsd2.lpSurface)+y*ddsd2.lPitch);
669
                      for x:=0 to w-1 do
670
                      begin
671
                        if (PWord(P1)^<>tc1) and (PWord(P2)^<>tc2) then Exit;
672
                        Inc(PWord(P1));
673
                        Inc(PWord(P2));
674
                      end;
675
                    end;
676
                  end;
677
              24: begin
678
                    for y:=0 to h-1 do
679
                    begin
680
                      P1 := Pointer(Integer(ddsd1.lpSurface)+y*ddsd1.lPitch);
681
                      P2 := Pointer(Integer(ddsd2.lpSurface)+y*ddsd2.lPitch);
682
                      for x:=0 to w-1 do
683
                      begin        
684
                        if ((PRGB(P1)^.R shl 16) or (PRGB(P1)^.G shl 8) or PRGB(P1)^.B<>tc1) and
685
                          ((PRGB(P2)^.R shl 16) or (PRGB(P2)^.G shl 8) or PRGB(P2)^.B<>tc2) then Exit;
686
                        Inc(PRGB(P1));
687
                        Inc(PRGB(P2));
688
                      end;
689
                    end;
690
                  end;
691
              32: begin
692
                    for y:=0 to h-1 do
693
                    begin
694
                      P1 := Pointer(Integer(ddsd1.lpSurface)+y*ddsd1.lPitch);
695
                      P2 := Pointer(Integer(ddsd2.lpSurface)+y*ddsd2.lPitch);
696
                      for x:=0 to w-1 do
697
                      begin
698
                        if (PDWORD(P1)^ and $FFFFFF<>tc1) and (PDWORD(P2)^ and $FFFFFF<>tc2) then Exit;
699
                        Inc(PDWORD(P1));
700
                        Inc(PDWORD(P2));
701
                      end;
702
                    end;
703
                  end;
704
            end;
705
          finally
706
            if suf1<>suf2 then suf2.UnLock;
707
          end;
708
        end;
709
      finally
710
        suf1.UnLock;
711
      end;
712
    end;
713
 
714
    Result := False;
715
  end;
716
end;
717
 
718
function TImageSprite.TestCollision(Sprite: TSprite): Boolean;
719
var
720
  img1, img2: Integer;
721
  b1, b2: TRect;
722
begin
723
  if (Sprite is TImageSprite) and FPixelCheck then
724
  begin
725
    b1 := GetDrawRect;
726
    b2 := TImageSprite(Sprite).GetDrawRect;
727
 
728
    img1 := GetDrawImageIndex;
729
    img2 := TImageSprite(Sprite).GetDrawImageIndex;
730
 
731
    Result := ImageCollisionTest(Image.PatternSurfaces[img1], TImageSprite(Sprite).Image.PatternSurfaces[img2],
732
      Image.PatternRects[img1], TImageSprite(Sprite).Image.PatternRects[img2],
733
      b1.Left, b1.Top, b2.Left, b2.Top, True);
734
  end else
735
    Result := inherited TestCollision(Sprite);
736
end;
737
 
738
{  TImageSpriteEx  }
739
 
740
constructor TImageSpriteEx.Create(AParent: TSprite);
741
begin
742
  inherited Create(AParent);
743
  FAlpha := 255;
744
end;
745
 
746
procedure TImageSpriteEx.DoDraw;
747
var
748
  r: TRect;
749
begin
750
  r := Bounds(Trunc(WorldX), Trunc(WorldY), Width, Height);
751
 
752
  if FAngle and $FF=0 then
753
  begin
754
    if FAlpha<255 then
755
    begin
756
      Image.DrawAlpha(FEngine.FSurface, r, GetDrawImageIndex, FAlpha)
757
    end else
758
    begin
759
      Image.StretchDraw(FEngine.FSurface, r, GetDrawImageIndex);
760
    end;
761
  end else
762
  begin
763
    if FAlpha<255 then
764
    begin
765
      Image.DrawRotateAlpha(FEngine.FSurface, (r.Left+r.Right) div 2, (r.Top+r.Bottom) div 2,
766
        Width, Height, GetDrawImageIndex, 0.5, 0.5, FAngle, FAlpha)
767
    end else
768
    begin
769
      Image.DrawRotate(FEngine.FSurface, (r.Left+r.Right) div 2, (r.Top+r.Bottom) div 2,
770
        Width, Height, GetDrawImageIndex, 0.5, 0.5, FAngle)
771
    end;
772
  end;
773
end;
774
 
775
function TImageSpriteEx.GetBoundsRect: TRect;
776
begin
777
  Result := FEngine.SurfaceRect;
778
end;
779
 
780
function TImageSpriteEx.TestCollision(Sprite: TSprite): Boolean;
781
begin
782
  if Sprite is TImageSpriteEx then
783
  begin
784
    Result := OverlapRect(Bounds(Trunc(Sprite.WorldX), Trunc(Sprite.WorldY), Sprite.Width, Sprite.Height),
785
      Bounds(Trunc(WorldX), Trunc(WorldY), Width, Height));
786
  end else
787
  begin
788
    Result := OverlapRect(Sprite.BoundsRect, Bounds(Trunc(WorldX), Trunc(WorldY), Width, Height));
789
  end;
790
end;
791
 
792
{  TBackgroundSprite  }
793
 
794
constructor TBackgroundSprite.Create(AParent: TSprite);
795
begin
796
  inherited Create(AParent);
797
  Collisioned := False;
798
end;
799
 
800
destructor TBackgroundSprite.Destroy;
801
begin
802
  SetMapSize(0, 0);
803
  inherited Destroy;
804
end;
805
 
806
procedure TBackgroundSprite.DoDraw;
807
var
808
  _x, _y, cx, cy, cx2, cy2, c, ChipWidth, ChipHeight: Integer;
809
  StartX, StartY, EndX, EndY, StartX_, StartY_, OfsX, OfsY, dWidth, dHeight: Integer;
810
  r: TRect;
811
begin
812
  if Image=nil then Exit;
813
 
814
  if (FMapWidth<=0) or (FMapHeight<=0) then Exit;
815
 
816
  r := Image.PatternRects[0];
817
  ChipWidth := r.Right-r.Left;
818
  ChipHeight := r.Bottom-r.Top;
819
 
820
  dWidth := (FEngine.SurfaceRect.Right+ChipWidth) div ChipWidth+1;
821
  dHeight := (FEngine.SurfaceRect.Bottom+ChipHeight) div ChipHeight+1;
822
 
823
  _x := Trunc(WorldX);
824
  _y := Trunc(WorldY);
825
 
826
  OfsX := _x mod ChipWidth;
827
  OfsY := _y mod ChipHeight;
828
 
829
  StartX := _x div ChipWidth;
830
  StartX_ := 0;
831
 
832
  if StartX<0 then
833
  begin
834
    StartX_ := -StartX;
835
    StartX := 0;
836
  end;
837
 
838
  StartY := _y div ChipHeight;
839
  StartY_ := 0;
840
 
841
  if StartY<0 then
842
  begin
843
    StartY_ := -StartY;
844
    StartY := 0;
845
  end;
846
 
847
  EndX := Min(StartX+FMapWidth-StartX_, dWidth);
848
  EndY := Min(StartY+FMapHeight-StartY_, dHeight);
849
 
850
  if FTile then
851
  begin
852
    for cy:=-1 to dHeight do
853
    begin
854
      cy2 := Mod2((cy-StartY+StartY_), FMapHeight);
855
      for cx:=-1 to dWidth do
856
      begin
857
        cx2 := Mod2((cx-StartX+StartX_), FMapWidth);
858
        c := Chips[cx2, cy2];
859
        if c>=0 then
860
          Image.Draw(FEngine.Surface, cx*ChipWidth+OfsX, cy*ChipHeight+OfsY, c);
861
      end;
862
    end;
863
  end else
864
  begin
865
    for cy:=StartY to EndY-1 do
866
      for cx:=StartX to EndX-1 do
867
      begin
868
        c := Chips[cx-StartX+StartX_, cy-StartY+StartY_];
869
        if c>=0 then
870
          Image.Draw(FEngine.Surface, cx*ChipWidth+OfsX, cy*ChipHeight+OfsY, c);
871
      end;
872
  end;
873
end;
874
 
875
function TBackgroundSprite.TestCollision(Sprite: TSprite): Boolean;
876
var
877
  b, b1, b2: TRect;
878
  cx, cy, ChipWidth, ChipHeight: Integer;
879
  r: TRect;
880
begin
881
  Result := True;
882
  if Image=nil then Exit;
883
  if (FMapWidth<=0) or (FMapHeight<=0) then Exit;
884
 
885
  r := Image.PatternRects[0];
886
  ChipWidth := r.Right-r.Left;
887
  ChipHeight := r.Bottom-r.Top;
888
 
889
 
890
 
891
  b1 := Sprite.BoundsRect;
892
  b2 := BoundsRect;
893
 
894
  IntersectRect(b, b1, b2);
895
 
896
  OffsetRect(b, -Trunc(WorldX), -Trunc(WorldY));
897
  OffsetRect(b1, -Trunc(WorldX), -Trunc(WorldY));
898
 
899
  for cy:=(b.Top-ChipHeight+1) div ChipHeight to b.Bottom div ChipHeight do
900
    for cx:=(b.Left-ChipWidth+1) div ChipWidth to b.Right div ChipWidth do
901
      if CollisionMap[Mod2(cx, MapWidth), Mod2(cy, MapHeight)] then
902
      begin
903
        if OverlapRect(Bounds(cx*ChipWidth, cy*ChipHeight, ChipWidth, ChipHeight), b1) then Exit;
904
      end;
905
 
906
  Result := False;
907
end;
908
 
909
function TBackgroundSprite.GetChip(X, Y: Integer): Integer;
910
begin
911
  if (X>=0) and (X<FMapWidth) and (Y>=0) and (Y<FMapHeight) then
912
    Result := PInteger(Integer(FMap)+(Y*FMapWidth+X)*SizeOf(Integer))^
913
  else
914
    Result := -1;
915
end;
916
 
917
type
918
  PBoolean = ^Boolean;
919
 
920
function TBackgroundSprite.GetCollisionMapItem(X, Y: Integer): Boolean;
921
begin
922
  if (X>=0) and (X<FMapWidth) and (Y>=0) and (Y<FMapHeight) then
923
    Result := PBoolean(Integer(FCollisionMap)+(Y*FMapWidth+X)*SizeOf(Boolean))^
924
  else
925
    Result := False;
926
end;
927
 
928
function TBackgroundSprite.GetBoundsRect: TRect;
929
begin
930
  if FTile then
931
    Result := FEngine.SurfaceRect
932
  else
933
  begin
934
    if Image<>nil then
935
      Result := Bounds(Trunc(WorldX), Trunc(WorldY),
936
        Image.Width*FMapWidth, Image.Height*FMapHeight)
937
    else
938
      Result := Rect(0, 0, 0, 0);
939
  end;
940
end;
941
 
942
procedure TBackgroundSprite.SetChip(X, Y: Integer; Value: Integer);
943
begin
944
  if (X>=0) and (X<FMapWidth) and (Y>=0) and (Y<FMapHeight) then
945
    PInteger(Integer(FMap)+(Y*FMapWidth+X)*SizeOf(Integer))^ := Value;
946
end;
947
 
948
procedure TBackgroundSprite.SetCollisionMapItem(X, Y: Integer; Value: Boolean);
949
begin
950
  if (X>=0) and (X<FMapWidth) and (Y>=0) and (Y<FMapHeight) then
951
    PBoolean(Integer(FCollisionMap)+(Y*FMapWidth+X)*SizeOf(Boolean))^ := Value;
952
end;
953
 
954
procedure TBackgroundSprite.SetMapHeight(Value: Integer);
955
begin
956
  SetMapSize(FMapWidth, Value);
957
end;
958
 
959
procedure TBackgroundSprite.SetMapWidth(Value: Integer);
960
begin
961
  SetMapSize(Value, FMapHeight);
962
end;
963
 
964
procedure TBackgroundSprite.SetMapSize(AMapWidth, AMapHeight: Integer);
965
begin
966
  if (FMapWidth<>AMapWidth) or (FMapHeight<>AMapHeight) then
967
  begin
968
    if (AMapWidth<=0) or (AMapHeight<=0) then
969
    begin
970
      AMapWidth := 0;
971
      AMapHeight := 0;
972
    end;
973
    FMapWidth := AMapWidth;
974
    FMapHeight := AMapHeight;
975
    ReAllocMem(FMap, FMapWidth*FMapHeight*SizeOf(Integer));
976
    FillChar(FMap^, FMapWidth*FMapHeight*SizeOf(Integer), 0);
977
 
978
    ReAllocMem(FCollisionMap, FMapWidth*FMapHeight*SizeOf(Boolean));
979
    FillChar(FCollisionMap^, FMapWidth*FMapHeight*SizeOf(Boolean), 1);
980
  end;
981
end;
982
 
983
{  TSpriteEngine  }
984
 
985
constructor TSpriteEngine.Create(AParent: TSprite);
986
begin
987
  inherited Create(AParent);
988
  FDeadList := TList.Create;
989
end;
990
 
991
destructor TSpriteEngine.Destroy;
992
begin
993
  FDeadList.Free;
994
  inherited Destroy;
995
end;
996
 
997
procedure TSpriteEngine.Dead;
998
begin
999
  while FDeadList.Count>0 do
1000
    TSprite(FDeadList[FDeadList.Count-1]).Free;
1001
end;
1002
 
1003
procedure TSpriteEngine.Draw;
1004
begin
1005
  FDrawCount := 0;
1006
  inherited Draw;
1007
end;
1008
 
1009
procedure TSpriteEngine.SetSurface(Value: TDirectDrawSurface);
1010
begin
1011
  FSurface := Value;
1012
  if FSurface<>nil then
1013
  begin
1014
    FSurfaceRect := Surface.ClientRect;
1015
    Width := FSurfaceRect.Right-FSurfaceRect.Left;
1016
    Height := FSurfaceRect.Bottom-FSurfaceRect.Top;
1017
  end;
1018
end;
1019
 
1020
{  TCustomDXSpriteEngine  }
1021
 
1022
constructor TCustomDXSpriteEngine.Create(AOnwer: TComponent);
1023
begin
1024
  inherited Create(AOnwer);
1025
  FEngine := TSpriteEngine.Create(nil);
1026
end;
1027
 
1028
destructor TCustomDXSpriteEngine.Destroy;
1029
begin                    
1030
  FEngine.Free;
1031
  inherited Destroy;
1032
end;
1033
 
1034
procedure TCustomDXSpriteEngine.Notification(AComponent: TComponent;
1035
  Operation: TOperation);
1036
begin
1037
  inherited Notification(AComponent, Operation);
1038
  if (Operation=opRemove) and (DXDraw=AComponent) then
1039
    DXDraw := nil;
1040
end;
1041
 
1042
procedure TCustomDXSpriteEngine.Dead;
1043
begin
1044
  FEngine.Dead;
1045
end;
1046
 
1047
procedure TCustomDXSpriteEngine.Draw;
1048
begin
1049
  if (FDXDraw<>nil) and (FDXDraw.Initialized) then
1050
    FEngine.Draw;
1051
end;
1052
 
1053
procedure TCustomDXSpriteEngine.Move(MoveCount: Integer);
1054
begin
1055
  FEngine.Move(MoveCount);
1056
end;
1057
 
1058
procedure TCustomDXSpriteEngine.DXDrawNotifyEvent(Sender: TCustomDXDraw;
1059
  NotifyType: TDXDrawNotifyType);
1060
begin
1061
  case NotifyType of
1062
    dxntDestroying: DXDraw := nil;
1063
    dxntInitialize: FEngine.Surface := Sender.Surface;
1064
    dxntFinalize  : FEngine.Surface := nil;
1065
  end;
1066
end;
1067
 
1068
procedure TCustomDXSpriteEngine.SetDXDraw(Value: TCustomDXDraw);
1069
begin
1070
  if FDXDraw<>nil then
1071
    FDXDraw.UnRegisterNotifyEvent(DXDrawNotifyEvent);
1072
 
1073
  FDXDraw := Value;
1074
 
1075
  if FDXDraw<>nil then
1076
    FDXDraw.RegisterNotifyEvent(DXDrawNotifyEvent);
1077
end;
1078
 
1079
end.