Skip to content

Commit 733c3ea

Browse files
committed
wip
1 parent 7a9bf9c commit 733c3ea

4 files changed

Lines changed: 73 additions & 20 deletions

File tree

‎game/Entities/PickupEntity.php‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ class PickupEntity extends AbstractEntity
1919

2020
/**
2121
* @param int $respawnTime Respawn time in ticks after being picked up (-1 = never respawn).
22+
* @param int $spawnDelay Initial spawn delay in ticks before the pickup first appears (0 = immediately available).
2223
*/
2324
public function __construct(
2425
Vector2 $position,
2526
private int $type,
2627
private int $subType = 0,
2728
private int $respawnTime = -1,
29+
private int $spawnDelay = 0,
2830
) {
2931
parent::__construct(position: $position);
3032
}
@@ -33,9 +35,11 @@ public function setWorld(AbstractWorld $world): void
3335
{
3436
parent::setWorld($world);
3537

36-
// Pickups are immediately available (spawn delay = 0),
37-
// matching Teeworlds 0.6 DM datafile defaults.
38-
$this->spawnTick = -1;
38+
if ($this->spawnDelay > 0) {
39+
$this->spawnTick = $world->getCurrentTick() + $this->spawnDelay;
40+
} else {
41+
$this->spawnTick = -1;
42+
}
3943
}
4044

4145
public function getHitBoxRadius(): int

‎game/Entities/PvpCharacterEntity.php‎

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class PvpCharacterEntity extends AbstractCharacterEntity
1616
{
1717
public int $damageTaken = 0;
1818
public int $damageTakenTick = 0;
19+
public int $ammoRegenStart = -1;
1920
protected function shootHammer(): int
2021
{
2122
$world = $this->world;
@@ -164,7 +165,7 @@ protected function shootShotgun(): int
164165
soundId: GameConstants::SOUND_SHOTGUN_FIRE,
165166
));
166167

167-
return 10; // ~200ms at 50 tick/s
168+
return 25; // 500ms at 50 tick/s
168169
}
169170

170171
protected function shootGrenade(): int
@@ -196,7 +197,7 @@ protected function shootGrenade(): int
196197
soundId: GameConstants::SOUND_GRENADE_FIRE,
197198
));
198199

199-
return 10;
200+
return 25; // 500ms at 50 tick/s
200201
}
201202

202203
protected function shootRifle(): int
@@ -223,7 +224,7 @@ protected function shootRifle(): int
223224
soundId: GameConstants::SOUND_RIFLE_FIRE,
224225
));
225226

226-
return 16; // ~320ms at 50 tick/s
227+
return 40; // 800ms at 50 tick/s
227228
}
228229

229230
protected function handleWeapons(bool $firePressed): void
@@ -232,6 +233,8 @@ protected function handleWeapons(bool $firePressed): void
232233
$this->handleNinja();
233234

234235
if (! $firePressed || $this->reloadTimer > 0) {
236+
$this->tickAmmoRegen();
237+
235238
return;
236239
}
237240

@@ -247,6 +250,8 @@ protected function handleWeapons(bool $firePressed): void
247250
soundId: GameConstants::SOUND_WEAPON_NOAMMO,
248251
));
249252

253+
$this->ammoRegenStart = -1;
254+
250255
return;
251256
}
252257

@@ -276,6 +281,46 @@ protected function handleWeapons(bool $firePressed): void
276281
if ($this->aWeapons[$this->activeWeapon]['ammo'] > 0) {
277282
$this->aWeapons[$this->activeWeapon]['ammo']--;
278283
}
284+
285+
$this->ammoRegenStart = -1;
286+
}
287+
288+
/**
289+
* Ammo regeneration for the gun.
290+
* Ported from Teeworlds 0.6 CCharacter::HandleWeapons() ammo regen section.
291+
*/
292+
private function tickAmmoRegen(): void
293+
{
294+
// Only the gun regenerates ammo
295+
if ($this->activeWeapon !== GameConstants::WEAPON_GUN) {
296+
$this->ammoRegenStart = -1;
297+
298+
return;
299+
}
300+
301+
// Pause during reload — don't reset, just wait
302+
if ($this->reloadTimer > 0) {
303+
return;
304+
}
305+
306+
$weaponAmmo = $this->aWeapons[$this->activeWeapon]['ammo'];
307+
$weaponGot = $this->aWeapons[$this->activeWeapon]['got'];
308+
309+
if (! $weaponGot || $weaponAmmo < 0 || $weaponAmmo >= 10) {
310+
$this->ammoRegenStart = -1;
311+
312+
return;
313+
}
314+
315+
if ($this->ammoRegenStart < 0) {
316+
$this->ammoRegenStart = $this->tick;
317+
}
318+
319+
// 500ms at 50 tick/s = 25 ticks
320+
if (($this->tick - $this->ammoRegenStart) >= 25) {
321+
$this->aWeapons[$this->activeWeapon]['ammo'] = min(10, $weaponAmmo + 1);
322+
$this->ammoRegenStart = -1;
323+
}
279324
}
280325

281326
public function takeDamage(Vector2 $force, int $damage, AbstractCharacterEntity $inflictor): void
@@ -429,6 +474,7 @@ protected function shootNinja(): int
429474
}
430475

431476
$angle = $this->angle / 256.0;
477+
432478
$this->ninjaActivationDir = new Vector2(cos($angle), sin($angle));
433479
$this->ninjaCurrentMoveTime = 10; // 200ms at 50 tick/s
434480
$this->ninjaOldVelAmount = $this->vel->length();
@@ -441,6 +487,6 @@ protected function shootNinja(): int
441487
soundId: GameConstants::SOUND_NINJA_FIRE,
442488
));
443489

444-
return 25; // 500ms firedelay at 50 tick/s
490+
return 40; // 800ms at 50 tick/s
445491
}
446-
}
492+
}

‎game/World/PickupSpawner.php‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ class PickupSpawner
1515
* Scan the game layer for pickup entities and spawn them in the world.
1616
*
1717
* @param array<int, int> $respawnTimes Entity type => respawn ticks (-1 = never respawn)
18+
* @param array<int, int> $spawnDelays Entity type => initial spawn delay in ticks (0 = immediately available)
1819
*/
19-
public static function spawn(AbstractWorld $world, GameLayer $gameLayer, array $respawnTimes = []): void
20+
public static function spawn(AbstractWorld $world, GameLayer $gameLayer, array $respawnTimes = [], array $spawnDelays = []): void
2021
{
2122
foreach ($gameLayer->getEntityPositions() as $entity) {
2223
$pos = new Vector2($entity['x'], $entity['y']);
23-
$pickup = self::createPickup($entity['type'], $pos, $respawnTimes);
24+
$pickup = self::createPickup($entity['type'], $pos, $respawnTimes, $spawnDelays);
2425

2526
if ($pickup !== null) {
2627
$world->addEntity($pickup);
@@ -32,18 +33,20 @@ public static function spawn(AbstractWorld $world, GameLayer $gameLayer, array $
3233
* Map a game layer entity type to a PickupEntity.
3334
*
3435
* @param array<int, int> $respawnTimes
36+
* @param array<int, int> $spawnDelays
3537
*/
36-
private static function createPickup(int $entityType, Vector2 $pos, array $respawnTimes): ?PickupEntity
38+
private static function createPickup(int $entityType, Vector2 $pos, array $respawnTimes, array $spawnDelays): ?PickupEntity
3739
{
38-
$respawn = $respawnTimes[$entityType] ?? -1;
40+
$respawn = $respawnTimes[$entityType] ?? -1;
41+
$spawnDelay = $spawnDelays[$entityType] ?? 0;
3942

4043
return match ($entityType) {
41-
GameLayer::ENTITY_ARMOR_1 => new PickupEntity($pos, NetworkMessages::POWERUP_ARMOR, respawnTime: $respawn),
42-
GameLayer::ENTITY_HEALTH_1 => new PickupEntity($pos, NetworkMessages::POWERUP_HEALTH, respawnTime: $respawn),
43-
GameLayer::ENTITY_WEAPON_SHOTGUN => new PickupEntity($pos, NetworkMessages::POWERUP_WEAPON, GameConstants::WEAPON_SHOTGUN, respawnTime: $respawn),
44-
GameLayer::ENTITY_WEAPON_GRENADE => new PickupEntity($pos, NetworkMessages::POWERUP_WEAPON, GameConstants::WEAPON_GRENADE, respawnTime: $respawn),
45-
GameLayer::ENTITY_WEAPON_RIFLE => new PickupEntity($pos, NetworkMessages::POWERUP_WEAPON, GameConstants::WEAPON_RIFLE, respawnTime: $respawn),
46-
GameLayer::ENTITY_POWERUP_NINJA => new PickupEntity($pos, NetworkMessages::POWERUP_NINJA, respawnTime: $respawn),
44+
GameLayer::ENTITY_ARMOR_1 => new PickupEntity($pos, NetworkMessages::POWERUP_ARMOR, respawnTime: $respawn, spawnDelay: $spawnDelay),
45+
GameLayer::ENTITY_HEALTH_1 => new PickupEntity($pos, NetworkMessages::POWERUP_HEALTH, respawnTime: $respawn, spawnDelay: $spawnDelay),
46+
GameLayer::ENTITY_WEAPON_SHOTGUN => new PickupEntity($pos, NetworkMessages::POWERUP_WEAPON, GameConstants::WEAPON_SHOTGUN, respawnTime: $respawn, spawnDelay: $spawnDelay),
47+
GameLayer::ENTITY_WEAPON_GRENADE => new PickupEntity($pos, NetworkMessages::POWERUP_WEAPON, GameConstants::WEAPON_GRENADE, respawnTime: $respawn, spawnDelay: $spawnDelay),
48+
GameLayer::ENTITY_WEAPON_RIFLE => new PickupEntity($pos, NetworkMessages::POWERUP_WEAPON, GameConstants::WEAPON_RIFLE, respawnTime: $respawn, spawnDelay: $spawnDelay),
49+
GameLayer::ENTITY_POWERUP_NINJA => new PickupEntity($pos, NetworkMessages::POWERUP_NINJA, respawnTime: $respawn, spawnDelay: $spawnDelay),
4750
default => null,
4851
};
4952
}

‎tests/Feature/NinjaTest.php‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ function advanceWorldTick(AbstractWorld $world): void
275275
expect($character->position->y)->toEqual($posAfterDash->y);
276276
});
277277

278-
test('ninja has 25 tick cooldown between shots', function () use ($mapPath, $mapExists) {
278+
test('ninja has 40 tick cooldown between shots', function () use ($mapPath, $mapExists) {
279279
if (! $mapExists) {
280280
return;
281281
}
@@ -308,7 +308,7 @@ function advanceWorldTick(AbstractWorld $world): void
308308

309309
// First shot sets reload timer
310310
$reloadTimer = $shootMethod->invoke($character);
311-
expect($reloadTimer)->toBe(25);
311+
expect($reloadTimer)->toBe(40);
312312

313313
// Simulate the reload timer being set (as handleWeapons would do)
314314
$character->reloadTimer = $reloadTimer;

0 commit comments

Comments
 (0)