126 lines
3.5 KiB
PHP
126 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Game\Entities;
|
|
|
|
use App\Game\EntityList;
|
|
use App\Game\Player;
|
|
use App\Game\Room;
|
|
use App\Game\Vector2;
|
|
|
|
class Witch extends Zombie
|
|
{
|
|
public float $preferredDistance;
|
|
|
|
public float $fireInterval;
|
|
|
|
public float $projectileSpeed;
|
|
|
|
public int $projectileCount;
|
|
|
|
public float $projectileSpread;
|
|
|
|
/** @var array<float> */
|
|
public array $projectileColor;
|
|
|
|
protected float $fireTimer = 0.0;
|
|
|
|
public function __construct(
|
|
float $x,
|
|
float $y,
|
|
EntityList $container,
|
|
Room $room,
|
|
?Player $initialTarget,
|
|
array $config = [],
|
|
?callable $onDeath = null,
|
|
) {
|
|
parent::__construct($x, $y, $container, $room, $config, $onDeath);
|
|
if ($initialTarget !== null) {
|
|
$this->target = $initialTarget;
|
|
}
|
|
$this->preferredDistance = $config['preferredDistance'] ?? 220.0;
|
|
$this->fireInterval = $config['fireInterval'] ?? 2.2;
|
|
$this->projectileSpeed = $config['projectileSpeed'] ?? 220.0;
|
|
$this->projectileCount = $config['projectileCount'] ?? 1;
|
|
$this->projectileSpread = $config['projectileSpread'] ?? 0.08;
|
|
$this->projectileColor = $config['projectileColor'] ?? [0.7, 0.2, 1.0, 1.0];
|
|
}
|
|
|
|
public function kind(): string
|
|
{
|
|
return 'witch';
|
|
}
|
|
|
|
public function update(float $dt): void
|
|
{
|
|
$this->time += $dt;
|
|
|
|
if ($this->updateEmerge($dt)) {
|
|
return;
|
|
}
|
|
|
|
$this->fireTimer -= $dt;
|
|
|
|
$target = $this->target;
|
|
if ($target === null) {
|
|
return;
|
|
}
|
|
|
|
$delta = $target->pos->subV($this->pos);
|
|
$distance = $delta->length();
|
|
|
|
$move = Vector2::zero();
|
|
if ($distance > 1.0) {
|
|
$dir = $delta->divS($distance);
|
|
if ($distance < $this->preferredDistance) {
|
|
$move = $dir->mulS(-$this->speed);
|
|
} elseif ($distance > $this->preferredDistance + 40.0) {
|
|
$move = $dir->mulS($this->speed);
|
|
}
|
|
}
|
|
|
|
$this->velocity = $move->addV($this->hitKnockback);
|
|
$this->move($dt);
|
|
|
|
$this->hitKnockback = $this->hitKnockback->mulS(max(0.0, 1 - $dt * $this->knockbackDamping));
|
|
$this->flash = max(0.0, $this->flash - $dt);
|
|
|
|
if ($this->fireTimer <= 0.0 && ! $target->dead && $distance < 480.0) {
|
|
$this->fireTimer = $this->fireInterval * (0.8 + $this->rand01() * 0.4);
|
|
$this->fire();
|
|
}
|
|
}
|
|
|
|
protected function onEmergeComplete(): void
|
|
{
|
|
$this->fireTimer = $this->fireInterval * (0.4 + $this->rand01() * 0.4);
|
|
}
|
|
|
|
public function fire(): void
|
|
{
|
|
$target = $this->target;
|
|
if ($target === null) {
|
|
return;
|
|
}
|
|
$aim = $target->pos->subV($this->pos);
|
|
$aimAngle = $aim->lengthSquared() > 1.0 ? atan2($aim->y, $aim->x) : $this->time;
|
|
|
|
$offset = (($this->projectileCount - 1) / 2.0) * $this->projectileSpread;
|
|
for ($i = 0; $i < $this->projectileCount; $i++) {
|
|
$angle = $aimAngle - $offset + $i * $this->projectileSpread;
|
|
$direction = new Vector2(cos($angle), sin($angle));
|
|
$this->container->append(new WitchProjectile(
|
|
$this->pos->x,
|
|
$this->pos->y,
|
|
$direction,
|
|
$this->container,
|
|
$this->room,
|
|
$this->projectileSpeed,
|
|
$this->size * 0.4,
|
|
$this->projectileColor,
|
|
));
|
|
}
|
|
}
|
|
}
|