fix: complete actor behavior timelines

This commit is contained in:
ud18010
2026-07-30 13:29:35 +08:00
parent 1380d5107a
commit bc52b74ff8
4 changed files with 221 additions and 2 deletions
@@ -204,7 +204,11 @@ namespace XWorld.Framework.ActorBehavior
foreach (FlyObjectSpec fly in _flyObjects.Values)
{
ValidateDuration(fly.Duration, "flyObject " + fly.Id);
if (fly.Movement.Speed < 0f)
throw new InvalidOperationException("Invalid speed for flyObject " + fly.Id);
ValidateDuration(fly.Collision.Radius, "flyObject collision " + fly.Id);
if (fly.Collision.HitLimit < 0)
throw new InvalidOperationException("Invalid hitLimit for flyObject " + fly.Id);
ValidateTimeline(fly.Timeline, fly.LogicEffects, "flyObject " + fly.Id);
ValidateLogicEffects(fly.LogicEffects, "flyObject " + fly.Id);
}
@@ -242,8 +246,12 @@ namespace XWorld.Framework.ActorBehavior
AddUnique(ids, effect.Id, "logicEffect");
if (effect.Shape != LogicShapeKind.Locked && effect.Radius < 0f)
throw new InvalidOperationException("Invalid radius for " + effect.Id + " in " + owner);
if (effect.Shape == LogicShapeKind.Ring && (effect.MinRadius < 0f || effect.MinRadius > effect.Radius))
throw new InvalidOperationException("Invalid minRadius for " + effect.Id + " in " + owner);
if (effect.Shape == LogicShapeKind.Sector && (effect.Angle <= 0f || effect.Angle > 360f))
throw new InvalidOperationException("Invalid sector angle for " + effect.Id + " in " + owner);
if (effect.Shape == LogicShapeKind.Line && effect.Width < 0f)
throw new InvalidOperationException("Invalid width for " + effect.Id + " in " + owner);
foreach (LogicEffectOpSpec op in effect.Effects)
{
if (op.Type == LogicEffectOpKind.Buff && !_buffs.ContainsKey(op.BuffId))
@@ -236,6 +236,13 @@ namespace XWorld.Framework.ActorBehavior
return;
}
if (entry.Kind == TimelineEntryKind.Buff)
{
int targetActorId = active.Input.Kind == BehaviorInputKind.TargetActor ? active.Input.TargetActorId : active.ActorId;
ApplyBuff(targetActorId, active.Catalog.Type, entry.BuffId, active.ActorId);
return;
}
if (entry.Kind == TimelineEntryKind.Projectile)
{
SpawnProjectile(active.ActorId, active.Catalog, entry.FlyObjectId, active.Input);
@@ -308,8 +315,11 @@ namespace XWorld.Framework.ActorBehavior
TargetActorId = buff.TargetActorId,
BuffId = buff.Spec.Id
});
for (int e = 0; e < buff.Spec.LogicEffects.Count; e++)
ApplyLogicEffect(buff.TargetActorId, buff.Catalog, buff.Spec.LogicEffects[e], BehaviorInput.FromTargetActor(buff.TargetActorId), buff.TargetActorId);
if (_options.Mode == BehaviorRunMode.ServerLogic)
{
for (int e = 0; e < buff.Spec.LogicEffects.Count; e++)
ApplyLogicEffect(buff.TargetActorId, buff.Catalog, buff.Spec.LogicEffects[e], BehaviorInput.FromTargetActor(buff.TargetActorId), buff.TargetActorId);
}
buff.NextTickTime += buff.Spec.TickInterval;
}
@@ -364,6 +374,7 @@ namespace XWorld.Framework.ActorBehavior
FlyObjectId = spec.Id,
Position = projectile.Position
});
FireProjectileTimeline(projectile, -0.00001f, 0f);
}
private BehaviorVector3 ResolveProjectileDirection(BehaviorActorState source, BehaviorInput input, int targetActorId)
@@ -382,6 +393,7 @@ namespace XWorld.Framework.ActorBehavior
var expired = new HashSet<ActiveProjectile>();
foreach (ActiveProjectile projectile in _projectiles)
{
float previousAge = projectile.Age;
projectile.Age += deltaTime;
MoveProjectile(projectile, deltaTime);
_events.Add(new BehaviorEvent
@@ -392,6 +404,7 @@ namespace XWorld.Framework.ActorBehavior
FlyObjectId = projectile.Spec.Id,
Position = projectile.Position
});
FireProjectileTimeline(projectile, previousAge, projectile.Age);
if (_options.Mode == BehaviorRunMode.ServerLogic)
CheckProjectileCollision(projectile, expired);
@@ -458,6 +471,54 @@ namespace XWorld.Framework.ActorBehavior
}
}
private void FireProjectileTimeline(ActiveProjectile projectile, float previousElapsed, float currentElapsed)
{
List<TimelineEntrySpec> timeline = projectile.Spec.Timeline;
for (int i = 0; i < timeline.Count; i++)
{
if (projectile.FiredTimeline.Contains(i)) continue;
TimelineEntrySpec entry = timeline[i];
if (entry.Trigger != TimelineTriggerKind.Time) continue;
if (entry.Time > currentElapsed || entry.Time <= previousElapsed) continue;
projectile.FiredTimeline.Add(i);
FireProjectileTimelineEntry(projectile, entry);
}
}
private void FireProjectileTimelineEntry(ActiveProjectile projectile, TimelineEntrySpec entry)
{
if (entry.Kind == TimelineEntryKind.Effect)
{
if (_options.Mode == BehaviorRunMode.ClientPresentation || _options.Mode == BehaviorRunMode.ClientPredict)
{
_events.Add(new BehaviorEvent
{
Kind = BehaviorEventKind.TimelineEffect,
ActorId = projectile.SourceActorId,
SourceActorId = projectile.SourceActorId,
FlyObjectId = projectile.Spec.Id,
Prefab = entry.Prefab,
Position = projectile.Position
});
}
return;
}
if (entry.Kind == TimelineEntryKind.LogicEffect && _options.Mode == BehaviorRunMode.ServerLogic)
{
LogicEffectSpec effect = FindLogicEffect(projectile.Spec.LogicEffects, entry.EffectId);
if (effect != null)
ApplyLogicEffect(projectile.SourceActorId, projectile.Catalog, effect, BehaviorInput.FromTargetActor(projectile.TargetActorId), projectile.TargetActorId);
return;
}
if (entry.Kind == TimelineEntryKind.Buff)
{
int targetActorId = projectile.TargetActorId != 0 ? projectile.TargetActorId : projectile.SourceActorId;
ApplyBuff(targetActorId, projectile.Catalog.Type, entry.BuffId, projectile.SourceActorId);
}
}
private void FireProjectileCollisionLogic(ActiveProjectile projectile, int hitActorId)
{
List<TimelineEntrySpec> timeline = projectile.Spec.Timeline;
@@ -575,6 +636,7 @@ namespace XWorld.Framework.ActorBehavior
public float Age;
public float VerticalVelocity;
public int HitCount;
public readonly HashSet<int> FiredTimeline = new HashSet<int>();
}
private sealed class ActiveBuff