package src; import hxd.res.BitmapFont; import h3d.Matrix; import src.DtsObject; import h3d.Vector; import gui.Graphics; import src.GameObject; import h2d.Scene; import src.MarbleWorld; import src.Util; import src.Marble; import src.Settings; import src.ResourceLoader; class Radar { var level:MarbleWorld; var scene2d:Scene; var g:Graphics; var marbleNameTexts:Map; public var ellipseScreenFraction = new Vector(0.79, 0.85); public var fullArrowLength = 60.0; public var fullArrowWidth = 40.0; public var maxArrowAlpha = 0.6; public var maxTargetAlpha = 0.4; public var minArrowFraction = 0.4; var radarTiles:Array; var time:Float = 0.0; var _dirty = false; public function new(level:MarbleWorld, scene2d:Scene) { this.level = level; this.scene2d = scene2d; this.marbleNameTexts = []; var radarTileRedGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemRed.png").resource.toTile(); var radarTileYellowGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemYellow.png").resource.toTile(); var radarTileBlueGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemBlue.png").resource.toTile(); var radarTileGreenGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemGreen.png").resource.toTile(); var radarTileOrangeGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemOrange.png").resource.toTile(); var radarTilePinkGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemPink.png").resource.toTile(); var radarTilePurpleGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemPurple.png").resource.toTile(); var radarTileTurquoiseGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemTurquoise.png").resource.toTile(); var radarTileBlackGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemBlack.png").resource.toTile(); var radarTilePlatinumGem = ResourceLoader.getImage("data/ui/mp/radar/GemItemPlatinum.png").resource.toTile(); var radarTileEndPad = ResourceLoader.getImage("data/ui/mp/radar/EndPad.png").resource.toTile(); radarTiles = [ radarTileRedGem, radarTileYellowGem, radarTileBlueGem, radarTileGreenGem, radarTileOrangeGem, radarTilePinkGem, radarTilePurpleGem, radarTileTurquoiseGem, radarTileBlackGem, radarTilePlatinumGem, radarTileEndPad ]; } public function init() { g = new Graphics(scene2d); } public function update(dt:Float) { time += dt; _dirty = true; } public function render(doRender) { if (!_dirty) return; g.clear(); if (!doRender) { for (marble => marbleName in marbleNameTexts) { if (marbleName != null) marbleName.alpha = 0; } return; } var gemCount = 0; for (gem in level.gems) { if (!gem.pickedUp) { renderArrow(gem.boundingCollider.boundingBox.getCenter().toVector(), gem.radarGemColor, radarTiles[gem.radarGemIndex]); gemCount++; } } if (@:privateAccess level.endPad != null && gemCount == 0) { renderArrow(@:privateAccess level.endPad.getAbsPos().getPosition(), 0xE6E6E6, radarTiles[10]); } var fadeDistance = level.scene.camera.zFar * 0.1; for (marble => marbleName in marbleNameTexts) { if (marbleName != null) marbleName.alpha = 0; } for (marble in level.marbles) { if (marble != level.marble) { var shapePos = marble.getAbsPos().getPosition(); var shapeDir = shapePos.sub(level.scene.camera.pos); var shapeDist = shapeDir.lengthSq(); if (shapeDist == 0 || shapeDist > level.scene.camera.zFar * level.scene.camera.zFar) { dontRenderName(marble); continue; } var validProjection = frustumHasPoint(level.scene.camera.frustum, shapePos); if (!validProjection) { dontRenderName(marble); continue; } shapePos.z += 0.5; // Vertical offset var projectedPos = level.scene.camera.project(shapePos.x, shapePos.y, shapePos.z, scene2d.width, scene2d.height); var opacity = (shapeDist < fadeDistance) ? 1.0 : (1.0 - (shapeDist - fadeDistance) / (level.scene.camera.zFar - fadeDistance)); renderName(projectedPos, marble, opacity); } } _dirty = false; } public function blink() { time = 0; } public function reset() { time = 0; g.clear(); } public function dispose() { g.clear(); scene2d.removeChild(g); g = null; for (txt in marbleNameTexts) { if (txt != null) { scene2d.removeChild(txt); } } marbleNameTexts = null; } inline function planeDistance(plane:h3d.col.Plane, p:Vector) { return @:privateAccess plane.nx * p.x + @:privateAccess plane.ny * p.y + @:privateAccess plane.nz * p.z - @:privateAccess plane.d; } function frustumHasPoint(frustum:h3d.col.Frustum, p:Vector) { if (planeDistance(frustum.pleft, p) < 0) return false; if (planeDistance(frustum.pright, p) < 0) return false; if (planeDistance(frustum.ptop, p) < 0) return false; if (planeDistance(frustum.pbottom, p) < 0) return false; if (frustum.checkNearFar) { if (planeDistance(frustum.pnear, p) < 0) return false; if (planeDistance(frustum.pfar, p) < 0) return false; } return true; } function renderArrow(pos:Vector, color:Int, tile:h2d.Tile) { var validProjection = frustumHasPoint(level.scene.camera.frustum, pos); var projectedPos = level.scene.camera.project(pos.x, pos.y, pos.z, scene2d.width, scene2d.height); if (validProjection && tile != null) { g.lineStyle(0, 0, 0); g.drawTile(projectedPos.x - tile.width / 2, projectedPos.y - tile.height / 2, tile); } else if (!validProjection) { var centerDiff = projectedPos.sub(new Vector(scene2d.width / 2, scene2d.height / 2)); var theta = Math.atan2(centerDiff.y, centerDiff.x); if (projectedPos.z > 1) theta += Math.PI; var ellipsePos = new Vector(scene2d.width * (ellipseScreenFraction.x * Math.cos(theta) + 1) / 2, scene2d.height * (ellipseScreenFraction.y * Math.sin(theta) + 1) / 2); var arrowDir = projectedPos.sub(new Vector(scene2d.width / 2, scene2d.height / 2)).normalized(); var arrowDirPerp = new Vector(-arrowDir.y, arrowDir.x); if (projectedPos.z > 1) arrowDir.scale(-1); var tipPosition = ellipsePos.add(arrowDir.multiply(fullArrowLength)); var tipUpperPosition = ellipsePos.add(arrowDirPerp.multiply(fullArrowWidth / 2)); var tipLowerPosition = ellipsePos.add(arrowDirPerp.multiply(-fullArrowWidth / 2)); g.beginFill(color, 0.6); g.lineStyle(1, 0, 0.6); g.moveTo(tipPosition.x, tipPosition.y); g.lineTo(tipUpperPosition.x, tipUpperPosition.y); g.lineTo(tipLowerPosition.x, tipLowerPosition.y); g.endFill(); } } function renderName(pos:Vector, marble:Marble, opacity:Float) { if (!marbleNameTexts.exists(marble)) { var markerFelt32fontdata = ResourceLoader.getFileEntry("data/font/MarkerFelt.fnt"); var markerFelt32b = new BitmapFont(markerFelt32fontdata.entry); @:privateAccess markerFelt32b.loader = ResourceLoader.loader; var markerFelt18 = markerFelt32b.toSdfFont(cast 14 * Settings.uiScale, MultiChannel); var txt = new h2d.Text(markerFelt18, scene2d); marbleNameTexts.set(marble, txt); txt.textColor = 0xFFFF00; } var textObj = marbleNameTexts.get(marble); textObj.text = @:privateAccess marble.connection.getName(); textObj.setPosition(pos.x - textObj.textWidth / 2, pos.y - textObj.textHeight); textObj.alpha = opacity; } function dontRenderName(marble:Marble) { if (marbleNameTexts.exists(marble)) { var el = marbleNameTexts.get(marble); el.alpha = 0; } } }