- Modified startup script to instantiate GameProcessServer separately
before wrapping in WsServer/HttpServer, allowing direct access to
set event loop after IoServer creation
- Implemented setLoop() method to set event loop after construction
- Added error handling for fwrite() to gracefully handle broken pipes
when process closes
- Suppressed fread() warnings with @ operator to avoid noise
- Simplified startOutputReader() since event loop polling is now
handled directly in onOpen()
- Added null checks for event loop before using in timers
- Server now properly starts with continuous 100ms output polling
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
131 lines
3.6 KiB
PHP
131 lines
3.6 KiB
PHP
<?php
|
||
namespace Game\Entities;
|
||
|
||
class Partner extends Actor
|
||
{
|
||
public $id;
|
||
// Partner特有的天赋加成(与 Actor 不同)
|
||
public static array $talentBonus = [
|
||
'hp' => 20,
|
||
'patk' => 5,
|
||
'matk' => 4,
|
||
'pdef' => 3,
|
||
'mdef' => 3,
|
||
'crit' => 2,
|
||
'critdmg' => 10,
|
||
];
|
||
|
||
public function __construct(array $data)
|
||
{
|
||
$this->id = $data['id'];
|
||
$this->name = $data['name'];
|
||
$this->level = $data['level'] ?? 1;
|
||
$this->exp = $data['exp'] ?? 0;
|
||
$this->maxExp = $data['maxExp'] ?? (int)(100 * pow(1.5, $this->level - 1));
|
||
|
||
$this->patk = $data['patk'] ;
|
||
$this->matk = $data['matk'] ;
|
||
$this->pdef = $data['pdef'] ;
|
||
$this->mdef = $data['mdef'] ;
|
||
$this->crit = $data['crit'] ;
|
||
$this->critdmg = $data['critdmg'] ?? 130;
|
||
$this->maxHp = $data['maxHp'];
|
||
$this->hp = $data['hp'] ?? $data['maxHp'];
|
||
// 装备和天赋系统
|
||
$this->equip = $data['equip'] ?? [];
|
||
$this->talents = $data['talents'] ?? $this->talents;
|
||
$this->talentWeights = $data['talentWeights'] ?? $this->talentWeights;
|
||
|
||
// 加载技能槽位数据 (新法术系统)
|
||
$this->mana = $data['mana'] ?? $this->mana;
|
||
$this->maxMana = $data['maxMana'] ?? 100;
|
||
$this->skillSlots = $data['skillSlots'] ?? $this->skillSlots;
|
||
}
|
||
|
||
/**
|
||
* 根据权重自动分配天赋点
|
||
* 重要: HP(生命值)总是必须至少加1点
|
||
*/
|
||
private function autoAllocateTalent(int $points): void
|
||
{
|
||
$totalWeight = array_sum($this->talentWeights);
|
||
if ($totalWeight <= 0) {
|
||
$totalWeight = 7; // 默认平均分配
|
||
}
|
||
|
||
// 按权重分配点数
|
||
$remaining = $points;
|
||
$allocated = [];
|
||
|
||
foreach ($this->talentWeights as $talent => $weight) {
|
||
$share = (int)floor($points * $weight / $totalWeight);
|
||
$allocated[$talent] = $share;
|
||
$remaining -= $share;
|
||
}
|
||
|
||
// 关键: 确保 HP 至少获得1点(HP是必须的)
|
||
if ($allocated['hp'] < 1) {
|
||
$allocated['hp'] = 1;
|
||
$remaining--;
|
||
}
|
||
|
||
// 剩余点数按权重优先分配
|
||
$sortedTalents = $this->talentWeights;
|
||
arsort($sortedTalents);
|
||
|
||
foreach ($sortedTalents as $talent => $weight) {
|
||
if ($remaining <= 0) break;
|
||
$allocated[$talent]++;
|
||
$remaining--;
|
||
}
|
||
|
||
// 应用分配
|
||
foreach ($allocated as $talent => $addPoints) {
|
||
$this->talents[$talent] += $addPoints;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Partner特有的升级逻辑,自动分配天赋点
|
||
*/
|
||
public function gainExp(int $amount): bool
|
||
{
|
||
$this->exp += $amount;
|
||
if ($this->exp >= $this->maxExp) {
|
||
$this->level++;
|
||
$this->exp -= $this->maxExp;
|
||
$this->maxExp = (int)($this->maxExp * 1.5);
|
||
|
||
// 升级时自动分配3点天赋(根据权重,且 HP 至少加1点)
|
||
$this->autoAllocateTalent(3);
|
||
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 获取总天赋点数
|
||
*/
|
||
public function getTotalTalentPoints(): int
|
||
{
|
||
return array_sum($this->talents);
|
||
}
|
||
|
||
/**
|
||
* 检查是否已学习法术
|
||
*/
|
||
public function hasSpell(int $spellId): bool
|
||
{
|
||
return isset($this->spells[$spellId]);
|
||
}
|
||
|
||
/**
|
||
* 获取法术等级
|
||
*/
|
||
public function getSpellLevel(int $spellId): int
|
||
{
|
||
return $this->spells[$spellId]['level'] ?? 0;
|
||
}
|
||
}
|