first commit
This commit is contained in:
137
internal/ai/mob.go
Normal file
137
internal/ai/mob.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"a301_game_server/internal/combat"
|
||||
"a301_game_server/internal/entity"
|
||||
"a301_game_server/pkg/mathutil"
|
||||
pb "a301_game_server/proto/gen/pb"
|
||||
)
|
||||
|
||||
// MobDef defines a mob template loaded from data.
|
||||
type MobDef struct {
|
||||
ID uint32
|
||||
Name string
|
||||
Level int32
|
||||
HP int32
|
||||
MP int32
|
||||
Str int32
|
||||
Dex int32
|
||||
Int int32
|
||||
MoveSpeed float32 // units per second
|
||||
AggroRange float32 // distance to start chasing
|
||||
AttackRange float32
|
||||
AttackSkill uint32 // skill ID used for auto-attack
|
||||
LeashRange float32 // max distance from spawn before returning
|
||||
ExpReward int64
|
||||
LootTable []LootEntry
|
||||
}
|
||||
|
||||
// LootEntry defines a possible drop.
|
||||
type LootEntry struct {
|
||||
ItemID uint32
|
||||
Quantity int32
|
||||
Chance float32 // 0.0 - 1.0
|
||||
}
|
||||
|
||||
// Mob is a server-controlled enemy entity.
|
||||
type Mob struct {
|
||||
id uint64
|
||||
def *MobDef
|
||||
position mathutil.Vec3
|
||||
rotation float32
|
||||
hp int32
|
||||
maxHP int32
|
||||
mp int32
|
||||
maxMP int32
|
||||
spawnPos mathutil.Vec3
|
||||
alive bool
|
||||
|
||||
// AI state
|
||||
state AIState
|
||||
targetID uint64 // entity being chased/attacked
|
||||
}
|
||||
|
||||
// NewMob creates a mob from a definition at the given position.
|
||||
func NewMob(id uint64, def *MobDef, spawnPos mathutil.Vec3) *Mob {
|
||||
return &Mob{
|
||||
id: id,
|
||||
def: def,
|
||||
position: spawnPos,
|
||||
spawnPos: spawnPos,
|
||||
hp: def.HP,
|
||||
maxHP: def.HP,
|
||||
mp: def.MP,
|
||||
maxMP: def.MP,
|
||||
alive: true,
|
||||
state: StateIdle,
|
||||
}
|
||||
}
|
||||
|
||||
// Entity interface
|
||||
func (m *Mob) EntityID() uint64 { return m.id }
|
||||
func (m *Mob) EntityType() entity.Type { return entity.TypeMob }
|
||||
|
||||
func (m *Mob) Position() mathutil.Vec3 { return m.position }
|
||||
func (m *Mob) SetPosition(p mathutil.Vec3) { m.position = p }
|
||||
func (m *Mob) Rotation() float32 { return m.rotation }
|
||||
func (m *Mob) SetRotation(r float32) { m.rotation = r }
|
||||
|
||||
// Combatant interface
|
||||
func (m *Mob) HP() int32 { return m.hp }
|
||||
func (m *Mob) MaxHP() int32 { return m.maxHP }
|
||||
func (m *Mob) SetHP(hp int32) {
|
||||
if hp < 0 {
|
||||
hp = 0
|
||||
}
|
||||
if hp > m.maxHP {
|
||||
hp = m.maxHP
|
||||
}
|
||||
m.hp = hp
|
||||
m.alive = hp > 0
|
||||
}
|
||||
func (m *Mob) MP() int32 { return m.mp }
|
||||
func (m *Mob) SetMP(mp int32) {
|
||||
if mp < 0 {
|
||||
mp = 0
|
||||
}
|
||||
m.mp = mp
|
||||
}
|
||||
func (m *Mob) IsAlive() bool { return m.alive }
|
||||
func (m *Mob) Stats() combat.CombatStats {
|
||||
return combat.CombatStats{
|
||||
Str: m.def.Str,
|
||||
Dex: m.def.Dex,
|
||||
Int: m.def.Int,
|
||||
Level: m.def.Level,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mob) Def() *MobDef { return m.def }
|
||||
func (m *Mob) SpawnPos() mathutil.Vec3 { return m.spawnPos }
|
||||
func (m *Mob) State() AIState { return m.state }
|
||||
func (m *Mob) SetState(s AIState) { m.state = s }
|
||||
func (m *Mob) TargetID() uint64 { return m.targetID }
|
||||
func (m *Mob) SetTargetID(id uint64) { m.targetID = id }
|
||||
|
||||
func (m *Mob) ToProto() *pb.EntityState {
|
||||
return &pb.EntityState{
|
||||
EntityId: m.id,
|
||||
Name: m.def.Name,
|
||||
Position: &pb.Vector3{X: m.position.X, Y: m.position.Y, Z: m.position.Z},
|
||||
Rotation: m.rotation,
|
||||
Hp: m.hp,
|
||||
MaxHp: m.maxHP,
|
||||
Level: m.def.Level,
|
||||
EntityType: pb.EntityType_ENTITY_TYPE_MOB,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset restores the mob to full health at its spawn position.
|
||||
func (m *Mob) Reset() {
|
||||
m.hp = m.maxHP
|
||||
m.mp = m.maxMP
|
||||
m.position = m.spawnPos
|
||||
m.alive = true
|
||||
m.state = StateIdle
|
||||
m.targetID = 0
|
||||
}
|
||||
Reference in New Issue
Block a user