DE

Elfs & Orcs Tower Defense

von malabke · Strategie
5,0 (1)
▶ Spielen 💬 Feedback Quellcode 🕘 Versionen Bearbeiten GitHub
games/elfs-orcs-tower-defense / tests / PortraitShotsTest.php · commit 65500d9
<?phpnamespace Tests;use App\Scene\PortraitScene;use App\Td\GameData;use App\View3d\Units;use JumPHP\Testing\GameTestCase;/** * The portraits of the interface, photographed from the 3D models (the ring menu's towers, the hero's button, the * next wave's preview, the creatures of the side cards): every creature and every tower alone in PortraitScene, over * black then over white; the difference of the two gives each pixel's transparency. Written to * assets/sprites/portraits/unit-<sprite>.png and tower-<sprite>.png (SIZE x SIZE, transparent), read by * Sprites::drawPortrait(). Needs OpenGL (skipped without it): re-run after a change of a model. */final class PortraitShotsTest extends GameTestCase{    private const SIZE = 128;    protected function setUp(): void    {        parent::setUp();        if (!$this->game->engine->gl()->isAvailable()) {            self::markTestSkipped('No OpenGL here: no 3D models to photograph.');        }    }    public function testEveryCreatureAndTowerHasAPortraitMadeFromItsModel(): void    {        $this->game->start(PortraitScene::class);        /** @var PortraitScene $scene */        $scene = $this->game->scene();        self::assertTrue($scene->isReady());        $dir = $this->game->engine->config->path('assets/sprites/portraits');        is_dir($dir) || mkdir($dir, 0777, true);        foreach (array_keys(Units::KINDS) as $sprite) {            $this->photograph($scene, "$dir/unit-$sprite.png", 'unit', $sprite);        }        $data = GameData::load($this->game->engine->config->path());        foreach (['elves', 'dwarves', 'horsefolk', 'littlefolk', 'orcs'] as $side) {            $faction = $data->faction($side);            foreach ($faction['towers'] as $type) {                $sprite = (string)$data->tower($type)['sprite'];                $this->photograph($scene, "$dir/tower-$sprite.png", 'tower', $sprite, (string)$faction['colors']['main']);            }        }        self::assertCount(count(Units::KINDS) + 20, glob("$dir/*.png") ?: []);    }    private function photograph(PortraitScene $scene, string $file, string $kind, string $sprite, string $flag = '#3fa860'): void    {        $layers = [];        foreach ([0, 255] as $bg) {            $scene->pose($kind, $sprite, [$bg, $bg, $bg], 1, null, $flag);            $this->game->run(2);            $layers[] = imagecreatefrompng($this->game->shot("portrait-$bg"));        }        [$black, $white] = $layers;        $w = imagesx($black);        $h = imagesy($black);        // the model with its transparency: over black c = a * colour, over white c = a * colour + (1 - a) * 255        $out = imagecreatetruecolor($w, $h);        imagealphablending($out, false);        imagesavealpha($out, true);        imagefill($out, 0, 0, imagecolorallocatealpha($out, 0, 0, 0, 127));        [$x0, $y0, $x1, $y1] = [$w, $h, -1, -1];        for ($y = 0; $y < $h; $y++) {            for ($x = 0; $x < $w; $x++) {                $b = imagecolorat($black, $x, $y);                $l = imagecolorat($white, $x, $y);                $diff = ((($l >> 16) & 255) - (($b >> 16) & 255) + (($l >> 8) & 255) - (($b >> 8) & 255) + ($l & 255) - ($b & 255)) / 3;                $a = max(0.0, min(1.0, 1.0 - $diff / 255));                if ($a < 0.03) {                    continue;                }                $c = static fn (int $v): int => (int)min(255, round($v / $a));                imagesetpixel($out, $x, $y, imagecolorallocatealpha($out, $c(($b >> 16) & 255), $c(($b >> 8) & 255), $c($b & 255),                    (int)round(127 * (1 - $a))));                [$x0, $y0, $x1, $y1] = [min($x0, $x), min($y0, $y), max($x1, $x), max($y1, $y)];            }        }        self::assertGreaterThan(0, $x1, "$kind $sprite is in the picture");        self::assertTrue($x0 > 0 && $y0 > 0 && $x1 < $w - 1 && $y1 < $h - 1, "$kind $sprite is whole in the picture");        // a square around it (a small margin), scaled down smoothly        $side = (int)ceil(max($x1 - $x0, $y1 - $y0) * 1.06) + 2;        $square = imagecreatetruecolor(self::SIZE, self::SIZE);        imagealphablending($square, false);        imagesavealpha($square, true);        imagefill($square, 0, 0, imagecolorallocatealpha($square, 0, 0, 0, 127));        $scale = self::SIZE / $side;        $dw = (int)round(($x1 - $x0 + 1) * $scale);        $dh = (int)round(($y1 - $y0 + 1) * $scale);        imagecopyresampled($square, $out, (int)((self::SIZE - $dw) / 2), (int)((self::SIZE - $dh) / 2), $x0, $y0, $dw, $dh, $x1 - $x0 + 1, $y1 - $y0 + 1);        imagepng($square, $file, 9);    }}