3
import flash.display.Sprite;
4
import flash.geom.Point;
5
import flash.events.IEventDispatcher;
6
import flash.events.MouseEvent;
7
import flash.events.Event;
8
import flash.display.Bitmap;
9
import flash.display.BitmapData;
10
import flash.filters.BevelFilter;
11
import flash.filters.BitmapFilterType;
12
import flash.filters.BitmapFilter;
13
import flash.filters.GlowFilter;
14
import flash.filters.ColorMatrixFilter;
17
import ui.AnimatedWidget;
19
import mathx.MatPoint;
24
class MapBackground extends AnimatedWidget, implements BuildDropTarget
26
private var gameDriver : GameDriver;
27
private var gm : GameMode;
28
private var tracer : Tracer;
30
private var costFields : Matrix<StaticText>;
33
var filtersBad : Array<BitmapFilter>;
34
var landBackground : BitmapData;
36
public function new( gd : GameDriver )
40
//requires Flash 9.0.28 (TODO: add backwards compatible mode)
41
addEventListener( "addedToStage" /*Event.ADDED_TO_STAGE*/, onAddedToStage );
42
addEventListener( "removedFromStage" /*Event.REMOVED_FROM_STAGE*/, onRemovedFromStage );
46
//switched to down to try an resolve a weird missing clicks defect (clearly FlashVM fault!)
47
addEventListener( MouseEvent.MOUSE_DOWN, onMouseClicked );
48
addEventListener( MouseEvent.DOUBLE_CLICK, onMouseDoubleClicked );
49
//doubleClickEnabled = true;
51
//not children should receive mouse events, this handles everything
52
mouseChildren = false;
54
tracer = new Tracer( gameDriver.data.map.landMap );
56
//create placement filters
57
filtersBad = new Array();
58
var matrix = new Array();
59
matrix = matrix.concat([1, 0, 0, 0, 0]); // red
60
matrix = matrix.concat([0, 0, 0, 0, 0]); // green
61
matrix = matrix.concat([0, 0, 0, 0, 0]); // blue
62
matrix = matrix.concat([0, 0, 0, 1, 0]); // alpha
63
filtersBad.push( new ColorMatrixFilter( matrix ) );
65
//landBackground = (new IBackground()).bitmapData;
66
landBackground = new IBitmapBackground();
74
function onAddedToStage( evt : Event )
76
//TODO: Make an a mechanism to do this adding and removed automatically...
77
gameDriver.addEventListener( GameEvent.MAP_CHANGED, onMapChanged );
78
gameDriver.addEventListener( LiveEvent.OBJECT_PLACED, onObjectPlaced );
79
gameDriver.addEventListener( LiveEvent.OBJECT_REMOVED, onObjectRemoved );
80
stage.addEventListener( UIEvent.OPTIONS_CHANGED, onOptionsChanged );
83
function onRemovedFromStage( evt : Event )
85
//make sure there are removed otherwise the backgorund object may keep
86
//piling up affecting performance!
87
gameDriver.removeEventListener( GameEvent.MAP_CHANGED, onMapChanged );
88
gameDriver.removeEventListener( LiveEvent.OBJECT_PLACED, onObjectPlaced );
89
gameDriver.removeEventListener( LiveEvent.OBJECT_REMOVED, onObjectRemoved );
90
stage.removeEventListener( UIEvent.OPTIONS_CHANGED, onOptionsChanged );
95
//reset drawing state (to ensure consistency)
99
//draw even without a know gamemode to make sure we
100
//have a size after creation (needed by layout to get this.scaleX/y)
106
function onOptionsChanged( evt : flash.events.Event )
111
public function onMouseDoubleClicked( evt : MouseEvent )
113
trace( "Not supposed to get these!" );
117
* There is somethign strange about missing clicks, it just doesn't feel
118
* as though all clicks are being handled correctly, especially under
119
* windows where when you have lots of frogs it is quite clear actually.
121
public function onMouseClicked( evt : MouseEvent )
123
if( gm != GameMode.Action )
126
//find closest available cannon and fire at location
127
var mp = gridGetPointF( new Point( evt.stageX, evt.stageY ) ); //why doesn't flash use it's own Point class in events?
128
var co = gameDriver.data.map.getClosestAvailCannon( mp.x, mp.y ); //why don't I use my own MatPoint class ;)
131
//show failed attempt
132
gameDriver.data.map.addLiveObject( new InvalidShotObject( gameDriver, mp ) );
139
public function setGameMode( agm : GameMode )
145
private function onMapChanged( evt : GameEvent )
153
override function _animate( elapsed : Float )
155
//don't do animation ehre, it is far too slow...
156
if( gm == GameMode.Build )
159
//check global options
160
if( !Options.animateWater )
170
if( wavesAt > waveSpeed )
171
wavesAt -= waveSpeed;
173
var w = GConst.gridSizeX * 10;
174
var wf = wavesAt / waveSpeed;
175
var waveFactor = wf * w;
177
//used for color/wavelength cycle
178
var it = Math.sin( (wf*wf)* Math.PI );
180
///////////////////////////////////////////////////////
181
//draw background layer
182
var m = new flash.geom.Matrix();
183
m.createGradientBox( w, w, Math.PI/3, waveFactor*2 );
184
var brush = draw.Brush.gradient(
185
flash.display.GradientType.LINEAR,
186
[ clrWater.asInt(), clrWater.adjustBrightness( 1 + 0.5*it ).asInt(), clrWater.asInt() ],
188
[ 180 - (it * 50), 230, 255 ], //alter wave-length in cycles
190
flash.display.SpreadMethod.REPEAT );
193
brush.apply( graphics );
194
graphics.drawRect( 0, 0,
195
gameDriver.data.map.landMap.size.x * GConst.gridSizeX,
196
gameDriver.data.map.landMap.size.y * GConst.gridSizeY );
200
override function _resize( w : Float, h : Float )
202
//ignore these, this uses a different system (see Layout)
206
static var clrWater = draw.Color.int(0x0000A0);
207
static var clrLand = draw.Color.int(0x008000);
209
var masses : Array<Sprite>;
211
private function drawPermanent()
215
//cleanup old items (if any)
222
masses = new Array<Sprite>();
223
for( i in 0...tracer.traces.length )
225
var tr = tracer.traces[i];
226
var sub = new Sprite();
227
sub.cacheAsBitmap = true;
228
var clr = tracer.tracesColor[i];
233
dclr = clrLand.asInt();
237
GConst.gridSizeX/2, //width
239
clrLand.brighten().asInt(), //highlight
241
clrLand.darken().asInt(), //shadow
243
GConst.gridSizeX/2,GConst.gridSizeY/2, //blur
245
flash.filters.BitmapFilterQuality.MEDIUM, //quality,
246
BitmapFilterType.INNER
250
case MapData.ctWater:
251
//only draw lakes, otherwise allow ocean to shine through
252
if( !tracer.tracesOverlap[i] )
254
dclr = clrWater.asInt();
259
//reduce traces node count (produce more natural shoreline, at expense of accuracy)
262
//var last = MatPoint.at( -1, -1 );
263
while( j < tr.length )
269
//draw curved shoreline
270
if( dclr == clrLand.asInt() )
272
var matrix = new flash.geom.Matrix();
273
//matrix.scale( 0.5, 0.5 ); //to get scaled filling, at original resolution, NOTE: unscaled form looks better somehow...
274
if( Options.texturedLand )
275
sub.graphics.beginBitmapFill( landBackground, matrix );
277
sub.graphics.beginFill( dclr );
280
sub.graphics.beginFill( dclr );
281
var s = q[0].promoteF().midTo( q[q.length-1].promoteF() ); //between first and last
282
sub.graphics.moveTo( s.x * GConst.gridSizeX, s.y * GConst.gridSizeY );
284
while( j < q.length ) //last taken care of above
286
var ctl = q[j].promoteF();
287
//offset ctl to remove grid-like spacing
288
if( ctl.x != 0 && ctl.x != gameDriver.data.map.landMap.size.x ) //leave edges alone
289
ctl.x += (Math.random() -0.5) / 2; //decrease to make rougher, increase for smoother
290
if( ctl.y != 0 && ctl.y != gameDriver.data.map.landMap.size.y ) //leave edges alone
291
ctl.y += (Math.random() -0.5) / 2;
294
if( j == q.length - 1 )
295
q[j].promoteF().midTo( q[0].promoteF() );
297
q[j].promoteF().midTo( q[j+1].promoteF() );
298
sub.graphics.curveTo( ctl.x * GConst.gridSizeX, ctl.y * GConst.gridSizeY,
299
anc.x * GConst.gridSizeX, anc.y * GConst.gridSizeY );
302
sub.graphics.endFill();
304
//sub.visible = false; //for fun debugging without masses
309
//draw water depth (taken out of draw, since this is permanent)
310
var water = new Sprite();
311
water.cacheAsBitmap = true;
312
if( Options.depthWater )
314
water.blendMode = flash.display.BlendMode.MULTIPLY;
315
for( m in gameDriver.data.map.landMap.xOrderIter() )
317
var ct = gameDriver.data.map.landMap.get( m.x, m.y );
318
var sdist = gameDriver.data.map.shoreDistMap.get( m.x, m.y );
321
if( ct != MapData.ctWater )
324
continue; //shore is handled by masses...
325
//color = draw.ColorUtil.adjustBrightness( clrWater, 1.0 - sdist/50);
326
color = draw.Color.int( 0xffffff ).adjustBrightness( 1.0 - sdist/35).asInt();
328
water.graphics.beginFill( color );
329
water.graphics.drawRect( m.x * GConst.gridSizeX, m.y * GConst.gridSizeY,
330
GConst.gridSizeX, GConst.gridSizeY );
331
water.graphics.endFill();
334
masses.push( water );
338
createBuildOverlay();
341
var buildOverlay : Sprite;
342
private function createBuildOverlay()
344
if( buildOverlay != null )
346
removeChild( buildOverlay );
349
buildOverlay = new Sprite();
350
buildOverlay.visible = false;
351
for( m in gameDriver.data.map.landMap.xOrderIter() )
353
//produce grid-like pattern
354
if( (m.x%2) ^ (m.y%2) == 1
355
//&& gameDriver.data.map.landMap.get( m.x, m.y ) == MapData.ctLand //only on land pieces (Doesn't look as good, sicne near shore it overlaps with water...)
358
buildOverlay.graphics.beginFill( 0x303030 );
359
buildOverlay.graphics.drawRect( m.x * GConst.gridSizeX, m.y * GConst.gridSizeY,
360
GConst.gridSizeX, GConst.gridSizeY );
361
buildOverlay.graphics.endFill();
363
//DIFF is good is background is light, ADD is good when background is dark
364
buildOverlay.blendMode = flash.display.BlendMode.DIFFERENCE;
367
addChild( buildOverlay );
370
var overlay : Sprite;
372
var walls : Array<IClipWall>;
374
private function draw()
376
var start = flash.Lib.getTimer();
378
if( overlay == null )
379
overlay = new Sprite();
381
//NOTE: wall logic takes less than 5% of time.
383
walls = new Array<IClipWall>();
388
/*for( i in 0...land.numChildren )
389
if( Std.is( land.getChildAt( i ), IClipWall ) )
390
walls.push( cast( land.getChildAt(i ) ) );*/
395
overlay.graphics.clear();
396
overlay.cacheAsBitmap = true;
399
if( costFields == null )
401
var unused : StaticText = null;
402
costFields = Matrix.create( gameDriver.data.map.landMap.size.x, gameDriver.data.map.landMap.size.y, unused );
403
for( mp in gameDriver.data.map.landMap.xOrderIter() )
405
var t = StaticText.singleLine( '99' );
406
t.x = mp.x * GConst.gridSizeX;
407
t.y = mp.y * GConst.gridSizeY;
408
t.rescale( GConst.gridSizeX, GConst.gridSizeY );
411
costFields.set( mp.x, mp.y, t );
416
//if not null then assume we are in cannon building mode
417
var ca = gameDriver.data.map.cannonAllowance;
419
for( my in 0...gameDriver.data.map.landMap.size.y )
420
for( mx in 0...gameDriver.data.map.landMap.size.x )
421
//for( m in gameDriver.data.map.landMap.xOrderIter() )
425
//NOTE: 25% of time is consumed strictly by the iterator! (replaced with direct iteration)
428
//costFields.get(m.x,m.y).text = "" + gameDriver.data.map.costMap.get(m.x,m.y);
429
costFields.get(mx,my).text = "" + gameDriver.data.map.modMap.get(mx,my);
432
var ct = gameDriver.data.map.landMap.get( mx, my );
436
if( ct == MapData.ctWater )
437
continue; //handled in permanent
438
else if( ct == MapData.ctLand )
440
var mod = gameDriver.data.map.modMap.get( mx, my );
441
if( mod & MapData.modFort == MapData.modFort )
443
if( ca != null && !ca.isAllowedAt( mx, my ) )
444
color = 0xA08040; //a bit red to indicate it isn't available
450
continue; //nothing speical, carry on
452
else if( ct == MapData.ctWall )
454
var wall : IClipWall;
455
var was : Bool = false;
456
if( wallAt < walls.length )
458
wall = walls[wallAt++];
464
wall = new IClipWall();
465
//add to list (to avoid needing to recreate each time)
468
//wall.cacheAsBitmap = true; (strangeness with redrawing of border when moving around the display)
470
wall.x = mx * GConst.gridSizeX;
471
wall.y = my * GConst.gridSizeY;
472
wall.width = GConst.gridSizeX;
473
wall.height = GConst.gridSizeY;
475
land.addChild( wall );
477
var mod = gameDriver.data.map.modMap.get( mx, my );
478
if( mod & MapData.modFort == MapData.modFort )
481
wall.filters = filtersBad;
488
Assert.unreachable();
490
continue; //what are these?
493
overlay.graphics.beginFill( color, alpha );
494
overlay.graphics.drawRect( mx * GConst.gridSizeX, my * GConst.gridSizeY,
495
GConst.gridSizeX, GConst.gridSizeY );
496
overlay.graphics.endFill();
499
//remove excessive walls
500
while( wallAt < walls.length )
501
walls[wallAt++].visible = false;
503
addChildAt( overlay, getChildIndex( masses[masses.length-1] ) + 1 );
504
addChildAt( land, getChildIndex( overlay ) );
506
if( gm == GameMode.Build )
508
buildOverlay.visible = true;
509
addChildAt( buildOverlay, getChildIndex( overlay ) + 1);
512
buildOverlay.visible = false;
514
//trace( "mbDraw: " + (flash.Lib.getTimer() - start) );
518
* Adjusts a stage point to be aligned with
519
* the local grid system -- moving it to the
520
* center of the closest cell
522
public function gridAdjustPoint( p : Point ) : Point
524
var mp = gridGetPoint( p );
525
var l : Point = new Point();
526
l.x = (mp.x + 0.5) * GConst.gridSizeX;
527
l.y = (mp.y + 0.5) * GConst.gridSizeY;
528
return localToGlobal( l );
532
* Translates a global point into the map grid point (the point
533
* in the pointed-to cell)
535
public function gridGetPoint( p : Point ) : MatPoint
537
var l = globalToLocal( p );
540
Math.floor( l.x / GConst.gridSizeX ),
541
Math.floor( l.y / GConst.gridSizeY )
547
public function gridGetPointF( p : Point ) : Point2
549
var l = globalToLocal( p );
552
l.x / GConst.gridSizeX,
553
l.y / GConst.gridSizeY
559
function redoLiveObjects()
561
//clear all live object children (go backwards as to not affect ordering)
562
for( i in new RangeIterI( numChildren-1, 0 ) )
564
var child = getChildAt( i );
565
if( Std.is( child, LiveObject ) )
566
removeChild( child );
569
for( lo in gameDriver.data.map.allLiveObjects() )
573
public function onObjectPlaced( lo : LiveEvent )
575
for( lobj in lo.liveObjs )
579
public function onObjectRemoved( lo : LiveEvent )
581
for( lobj in lo.liveObjs )
582
if( contains( lobj ) ) //this may be coming from a previous MapData during a level change
586
public function acceptObject( bo : BuildObject, sp : flash.geom.Point ) : Bool
588
var mp = gridGetPoint( sp );
589
return gameDriver.data.map.placeBuildObj( mp.x, mp.y, bo );
592
public function canAcceptObject( bo : BuildObject, sp : flash.geom.Point ) : Bool
594
var tsp = gridAdjustPoint( sp );
595
var mp = gridGetPoint( tsp );
596
return gameDriver.data.map.canPlaceBuildObj( mp.x, mp.y, bo );
599
public function alignDropObject( tsp : flash.geom.Point ) : flash.geom.Point
601
return gridAdjustPoint( tsp );
604
public function getDropPriority( ) : Int
606
return BuildDropTargetPriority.prioBackground;