Survivalcraft API 1.8.2.3 v1.8.2.3
Survivalcraft 2.4
载入中...
搜索中...
未找到
ComponentChaseBehavior.cs
浏览该文件的文档.
1using Engine;
4
5namespace Game {
7 // ReSharper disable UnusedMember.Local
8 Dictionary<ModLoader, Action> Hooks = [];
9 // ReSharper restore UnusedMember.Local
10
12
14
16
18
20
22
24
26
28
30
32
33 public DynamicArray<ComponentBody> m_componentBodies = [];
34
35 public Random m_random = new();
36
38
40
41 public float m_dayChaseRange;
42
43 public float m_nightChaseRange;
44
45 public float m_dayChaseTime;
46
47 public float m_nightChaseTime;
48
50
52
54
56
57 public float m_importanceLevel;
58
60
61 public float m_targetInRangeTime;
62
63 public double m_nextUpdateTime;
64
66
67 public float m_dt;
68
69 public float m_range;
70
71 public float m_chaseTime;
72
73 public bool m_isPersistent;
74
76
77 public float ImportanceLevelNonPersistent = 200f;
78
79 public float ImportanceLevelPersistent = 200f;
80
81 public float MaxAttackRange = 1.75f;
82
83 public bool AllowAttackingStandingOnBody = true;
84
85 public bool JumpWhenTargetStanding = true;
86
87 public bool AttacksPlayer = true;
88
89 public bool AttacksNonPlayerCreature = true;
90
91 public float ChaseRangeOnTouch = 7f;
92
93 public float ChaseTimeOnTouch = 7f;
94
95 public float? ChaseRangeOnAttacked = null;
96
97 public float? ChaseTimeOnAttacked = null;
98
99 public bool? ChasePersistentOnAttacked = null;
100
101 public float MinHealthToAttackActively = 0.4f;
102
103 public bool Suppressed = false;
104
106
107 public bool PlayAngrySoundWhenChasing = true;
108 public float TargetInRangeTimeToChase = 3f;
109
111
113
114 public override float ImportanceLevel => m_importanceLevel;
115
116 public virtual void Attack(ComponentCreature componentCreature, float maxRange, float maxChaseTime, bool isPersistent) {
117 if (Suppressed) {
118 return;
119 }
120 m_target = componentCreature;
121 m_nextUpdateTime = 0.0;
122 m_range = maxRange;
123 m_chaseTime = maxChaseTime;
124 m_isPersistent = isPersistent;
127 "OnChaseBehaviorStartChasing",
128 loader => {
129 loader.OnChaseBehaviorStartChasing(this);
130 return false;
131 }
132 );
133 }
134
135 public virtual void StopAttack() {
136 m_stateMachine.TransitionTo("LookingForTarget");
137 IsActive = false;
138 m_target = null;
139 m_nextUpdateTime = 0.0;
140 m_range = 0;
141 m_chaseTime = 0;
142 m_isPersistent = false;
145 "OnChaseBehaviorStopChasing",
146 loader => {
147 loader.OnChaseBehaviorStopChasing(this);
148 return false;
149 }
150 );
151 }
152
153 public virtual void Update(float dt) {
154 if (Suppressed) {
155 StopAttack();
156 }
158 if (IsActive && m_target != null) {
159 m_chaseTime -= dt;
160 m_componentCreature.ComponentCreatureModel.LookAtOrder = m_target.ComponentCreatureModel.EyePosition;
161 if (IsTargetInAttackRange(m_target.ComponentBody)) {
162 m_componentCreatureModel.AttackOrder = true;
163 }
164 if (m_componentCreatureModel.IsAttackHitMoment) {
165 ComponentBody hitBody = GetHitBody(m_target.ComponentBody, out Vector3 hitPoint);
166 if (hitBody != null) {
167 float chaseTimeBefore = m_chaseTime;
168 float x = m_isPersistent ? m_random.Float(8f, 10f) : 2f;
170 bool bodyToHit = true;
171 bool playAttackSound = true;
173 "OnChaseBehaviorAttacked",
174 loader => {
175 loader.OnChaseBehaviorAttacked(this, chaseTimeBefore, ref m_chaseTime, ref bodyToHit, ref playAttackSound);
176 return false;
177 }
178 );
179 if (bodyToHit) {
180 m_componentMiner.Hit(hitBody, hitPoint, m_componentCreature.ComponentBody.Matrix.Forward);
181 }
182 if (playAttackSound) {
183 m_componentCreature.ComponentCreatureSounds.PlayAttackSound();
184 }
185 }
186 else {
188 "OnChaseBehaviorAttackFailed",
189 loader => {
190 loader.OnChaseBehaviorAttackFailed(this, ref m_chaseTime);
191 return false;
192 }
193 );
194 }
195 }
196 }
197 if (m_subsystemTime.GameTime >= m_nextUpdateTime) {
198 m_dt = m_random.Float(0.25f, 0.35f) + MathUtils.Min((float)(m_subsystemTime.GameTime - m_nextUpdateTime), 0.1f);
199 m_nextUpdateTime = m_subsystemTime.GameTime + m_dt;
200 m_stateMachine.Update();
201 }
202 }
203
204 public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap) {
205 m_subsystemGameInfo = Project.FindSubsystem<SubsystemGameInfo>(true);
206 m_subsystemPlayers = Project.FindSubsystem<SubsystemPlayers>(true);
207 m_subsystemSky = Project.FindSubsystem<SubsystemSky>(true);
208 m_subsystemBodies = Project.FindSubsystem<SubsystemBodies>(true);
209 m_subsystemTime = Project.FindSubsystem<SubsystemTime>(true);
210 m_subsystemNoise = Project.FindSubsystem<SubsystemNoise>(true);
211 m_componentCreature = Entity.FindComponent<ComponentCreature>(true);
213 m_componentMiner = Entity.FindComponent<ComponentMiner>(true);
216 m_componentFactors = Entity.FindComponent<ComponentFactors>(true);
217 m_dayChaseRange = valuesDictionary.GetValue<float>("DayChaseRange");
218 m_nightChaseRange = valuesDictionary.GetValue<float>("NightChaseRange");
219 m_dayChaseTime = valuesDictionary.GetValue<float>("DayChaseTime");
220 m_nightChaseTime = valuesDictionary.GetValue<float>("NightChaseTime");
221 m_autoChaseMask = valuesDictionary.GetValue<CreatureCategory>("AutoChaseMask");
222 m_chaseNonPlayerProbability = valuesDictionary.GetValue<float>("ChaseNonPlayerProbability");
223 m_chaseWhenAttackedProbability = valuesDictionary.GetValue<float>("ChaseWhenAttackedProbability");
224 m_chaseOnTouchProbability = valuesDictionary.GetValue<float>("ChaseOnTouchProbability");
225 m_componentCreature.ComponentBody.CollidedWithBody += delegate(ComponentBody body) {
226 if (m_target == null
228 && m_random.Float(0f, 1f) < m_chaseOnTouchProbability) {
229 ComponentCreature componentCreature2 = body.Entity.FindComponent<ComponentCreature>();
230 if (componentCreature2 != null) {
231 bool flag2 = m_subsystemPlayers.IsPlayer(body.Entity);
232 bool flag3 = (componentCreature2.Category & m_autoChaseMask) != 0;
233 if ((AttacksPlayer && flag2 && m_subsystemGameInfo.WorldSettings.GameMode > GameMode.Harmless)
234 || (AttacksNonPlayerCreature && !flag2 && flag3)) {
235 Attack(componentCreature2, ChaseRangeOnTouch, ChaseTimeOnTouch, false);
236 }
237 }
238 }
239 if (m_target != null
241 && body == m_target.ComponentBody
242 && body.StandingOnBody == m_componentCreature.ComponentBody) {
243 m_componentCreature.ComponentLocomotion.JumpOrder = 1f;
244 }
245 };
246 m_componentCreature.ComponentHealth.Injured += delegate(Injury injury) {
247 ComponentCreature attacker = injury.Attacker;
248 if (m_random.Float(0f, 1f) < m_chaseWhenAttackedProbability) {
249 float chaseRange;
250 float chaseTime;
251 bool chasePersistent = false;
253 chaseRange = 30f;
254 chaseTime = 60f;
255 chasePersistent = true;
256 }
257 else {
258 chaseRange = 7f;
259 chaseTime = 7f;
260 //chasePersistent = false;
261 }
262 chaseRange = ChaseRangeOnAttacked ?? chaseRange;
263 chaseTime = ChaseTimeOnAttacked ?? chaseTime;
264 chasePersistent = ChasePersistentOnAttacked ?? chasePersistent;
265 Attack(attacker, chaseRange, chaseTime, chasePersistent);
266 }
267 };
268 m_stateMachine.AddState(
269 "LookingForTarget",
270 delegate {
272 m_target = null;
273 },
274 delegate {
275 if (IsActive) {
276 m_stateMachine.TransitionTo("Chasing");
277 }
278 else {
279 if (!Suppressed
281 && (m_target == null || ScoreTarget(m_target) <= 0f)
282 && m_componentCreature.ComponentHealth.Health > MinHealthToAttackActively) {
283 m_range = m_subsystemSky.SkyLightIntensity < 0.2f ? m_nightChaseRange : m_dayChaseRange;
284 m_range *= m_componentFactors.GetOtherFactorResult("ChaseRange");
285 ComponentCreature componentCreature = FindTarget();
286 if (componentCreature != null) {
288 }
289 else {
291 }
293 bool flag = m_subsystemSky.SkyLightIntensity >= 0.1f;
294 float maxRange = flag ? m_dayChaseRange + 6f : m_nightChaseRange + 6f;
295 float maxChaseTime = flag ? m_dayChaseTime * m_random.Float(0.75f, 1f) : m_nightChaseTime * m_random.Float(0.75f, 1f);
296 Attack(componentCreature, maxRange, maxChaseTime, !flag);
297 }
298 }
300 "UpdateChaseBehaviorLookingForTarget",
301 loader => {
302 loader.UpdateChaseBehaviorLookingForTarget(this);
303 return false;
304 }
305 );
306 }
307 },
308 null
309 );
310 m_stateMachine.AddState(
311 "RandomMoving",
312 delegate {
313 m_componentPathfinding.SetDestination(
314 m_componentCreature.ComponentBody.Position + new Vector3(6f * m_random.Float(-1f, 1f), 0f, 6f * m_random.Float(-1f, 1f)),
315 1f,
316 1f,
317 0,
318 false,
319 true,
320 false,
321 null
322 );
323 },
324 delegate {
325 if (m_componentPathfinding.IsStuck
326 || !m_componentPathfinding.Destination.HasValue) {
327 m_stateMachine.TransitionTo("Chasing");
328 }
329 if (!IsActive) {
330 m_stateMachine.TransitionTo("LookingForTarget");
331 }
332 },
333 delegate { m_componentPathfinding.Stop(); }
334 );
335 m_stateMachine.AddState(
336 "Chasing",
337 delegate {
338 m_subsystemNoise.MakeNoise(m_componentCreature.ComponentBody, 0.25f, 6f);
340 m_componentCreature.ComponentCreatureSounds.PlayIdleSound(false);
341 }
342 m_nextUpdateTime = 0.0;
343 },
344 delegate {
345 if (!IsActive) {
346 m_stateMachine.TransitionTo("LookingForTarget");
347 }
348 else if (m_chaseTime <= 0f) {
349 m_autoChaseSuppressionTime = m_random.Float(10f, 60f);
351 }
352 else if (m_target == null) {
354 }
355 else if (m_target.ComponentHealth.Health <= 0f) {
356 if (m_componentFeedBehavior != null) {
357 m_subsystemTime.QueueGameTimeDelayedExecution(
358 m_subsystemTime.GameTime + m_random.Float(1f, 3f),
359 delegate {
360 if (m_target != null) {
361 m_componentFeedBehavior.Feed(m_target.ComponentBody.Position);
362 }
363 }
364 );
365 }
367 }
368 else if (!m_isPersistent
369 && m_componentPathfinding.IsStuck) {
370 m_importanceLevel = 0f;
371 }
372 else if (m_isPersistent && m_componentPathfinding.IsStuck) {
373 m_stateMachine.TransitionTo("RandomMoving");
374 }
375 else {
376 if (ScoreTarget(m_target) <= 0f) {
377 m_targetUnsuitableTime += m_dt;
378 }
379 else {
380 m_targetUnsuitableTime = 0f;
381 }
382 if (m_targetUnsuitableTime > 3f) {
384 }
385 else {
386 int maxPathfindingPositions = 0;
387 if (m_isPersistent) {
388 maxPathfindingPositions = m_subsystemTime.FixedTimeStep.HasValue ? 2000 : 500;
389 }
390 BoundingBox boundingBox = m_componentCreature.ComponentBody.BoundingBox;
391 BoundingBox boundingBox2 = m_target.ComponentBody.BoundingBox;
392 Vector3 v = 0.5f * (boundingBox.Min + boundingBox.Max);
393 Vector3 vector = 0.5f * (boundingBox2.Min + boundingBox2.Max);
394 float num = Vector3.Distance(v, vector);
395 float num2 = num < 4f ? 0.2f : 0f;
396 m_componentPathfinding.SetDestination(
397 vector + num2 * num * m_target.ComponentBody.Velocity,
398 1f,
399 1.5f,
400 maxPathfindingPositions,
401 true,
402 false,
403 true,
404 m_target.ComponentBody
405 );
406 if (PlayAngrySoundWhenChasing && m_random.Float(0f, 1f) < 0.33f * m_dt) {
407 m_componentCreature.ComponentCreatureSounds.PlayAttackSound();
408 }
409 }
410 }
412 "UpdateChaseBehaviorChasing",
413 loader => {
414 loader.UpdateChaseBehaviorChasing(this);
415 return false;
416 }
417 );
418 },
419 null
420 );
421 m_stateMachine.TransitionTo("LookingForTarget");
422 }
423
424 public virtual ComponentCreature FindTarget() {
425 Vector3 position = m_componentCreature.ComponentBody.Position;
426 ComponentCreature result = null;
427 float num = 0f;
428 m_componentBodies.Clear();
429 m_subsystemBodies.FindBodiesAroundPoint(new Vector2(position.X, position.Z), m_range, m_componentBodies);
430 for (int i = 0; i < m_componentBodies.Count; i++) {
431 ComponentCreature componentCreature = m_componentBodies.Array[i].Entity.FindComponent<ComponentCreature>();
432 if (componentCreature != null) {
433 float num2 = ScoreTarget(componentCreature);
434 if (num2 > num) {
435 num = num2;
436 result = componentCreature;
437 }
438 }
439 }
440 return result;
441 }
442
443 public virtual float ScoreTarget(ComponentCreature componentCreature) {
444 float score = 0f;
445 bool isPlayer = componentCreature.Entity.FindComponent<ComponentPlayer>() != null;
446 bool isLandCreature = m_componentCreature.Category != CreatureCategory.WaterPredator
447 && m_componentCreature.Category != CreatureCategory.WaterOther;
448 bool notHarmless = componentCreature == Target || m_subsystemGameInfo.WorldSettings.GameMode > GameMode.Harmless;
449 bool isAutoChaseMask = (componentCreature.Category & m_autoChaseMask) != 0;
450 bool allowToChaseNonCreature = componentCreature == Target
451 || (isAutoChaseMask
453 0.004999999888241291 * m_subsystemTime.GameTime
454 + GetHashCode() % 1000 / 1000f
455 + componentCreature.GetHashCode() % 1000 / 1000f,
456 1.0
457 )
459 if (componentCreature != m_componentCreature
460 && ((!isPlayer && allowToChaseNonCreature) || (isPlayer && notHarmless))
461 && componentCreature.Entity.IsAddedToProject
462 && componentCreature.ComponentHealth.Health > 0f
463 && (isLandCreature || IsTargetInWater(componentCreature.ComponentBody))) {
464 float num = Vector3.Distance(m_componentCreature.ComponentBody.Position, componentCreature.ComponentBody.Position);
465 if (num < m_range) {
466 score = m_range - num;
467 }
468 }
470 "ChaseBehaviorScoreTarget",
471 loader => {
472 loader.ChaseBehaviorScoreTarget(this, componentCreature, ref score);
473 return false;
474 }
475 );
476 return score;
477 }
478
479 public virtual bool IsTargetInWater(ComponentBody target) {
480 if (target.ImmersionDepth > 0f) {
481 return true;
482 }
483 if (target.ParentBody != null
484 && IsTargetInWater(target.ParentBody)) {
485 return true;
486 }
487 if (target.StandingOnBody != null
488 && target.StandingOnBody.Position.Y < target.Position.Y
489 && IsTargetInWater(target.StandingOnBody)) {
490 return true;
491 }
492 return false;
493 }
494
495 public virtual bool IsTargetInAttackRange(ComponentBody target) {
496 if (IsBodyInAttackRange(target)) {
497 return true;
498 }
499 BoundingBox boundingBox = m_componentCreature.ComponentBody.BoundingBox;
500 BoundingBox boundingBox2 = target.BoundingBox;
501 Vector3 v = 0.5f * (boundingBox.Min + boundingBox.Max);
502 Vector3 v2 = 0.5f * (boundingBox2.Min + boundingBox2.Max) - v;
503 float num = v2.Length();
504 Vector3 v3 = v2 / num;
505 float num2 = 0.5f * (boundingBox.Max.X - boundingBox.Min.X + boundingBox2.Max.X - boundingBox2.Min.X);
506 float num3 = 0.5f * (boundingBox.Max.Y - boundingBox.Min.Y + boundingBox2.Max.Y - boundingBox2.Min.Y);
507 if (MathF.Abs(v2.Y) < num3 * 0.99f) {
508 if (num < num2 + 0.99f
509 && Vector3.Dot(v3, m_componentCreature.ComponentBody.Matrix.Forward) > 0.25f) {
510 return true;
511 }
512 }
513 else if (num < num3 + 0.3f
514 && MathF.Abs(Vector3.Dot(v3, Vector3.UnitY)) > 0.8f) {
515 return true;
516 }
517 if (target.ParentBody != null
519 return true;
520 }
522 && target.StandingOnBody != null
523 && target.StandingOnBody.Position.Y < target.Position.Y
525 return true;
526 }
527 return false;
528 }
529
530 public virtual bool IsBodyInAttackRange(ComponentBody target) {
531 BoundingBox boundingBox = m_componentCreature.ComponentBody.BoundingBox;
532 BoundingBox boundingBox2 = target.BoundingBox;
533 Vector3 v = 0.5f * (boundingBox.Min + boundingBox.Max);
534 Vector3 v2 = 0.5f * (boundingBox2.Min + boundingBox2.Max) - v;
535 float num = v2.Length();
536 Vector3 v3 = v2 / num;
537 float num2 = 0.5f * (boundingBox.Max.X - boundingBox.Min.X + boundingBox2.Max.X - boundingBox2.Min.X);
538 float num3 = 0.5f * (boundingBox.Max.Y - boundingBox.Min.Y + boundingBox2.Max.Y - boundingBox2.Min.Y);
539 if (MathF.Abs(v2.Y) < num3 * 0.99f) {
540 if (num < num2 + 0.99f
541 && Vector3.Dot(v3, m_componentCreature.ComponentBody.Matrix.Forward) > 0.25f) {
542 return true;
543 }
544 }
545 else if (num < num3 + 0.3f
546 && MathF.Abs(Vector3.Dot(v3, Vector3.UnitY)) > 0.8f) {
547 return true;
548 }
549 return false;
550 }
551
552 public virtual ComponentBody GetHitBody(ComponentBody target, out Vector3 hitPoint) {
553 Vector3 vector = m_componentCreature.ComponentBody.BoundingBox.Center();
554 Vector3 v = target.BoundingBox.Center();
555 Ray3 ray = new(vector, Vector3.Normalize(v - vector));
556 BodyRaycastResult? bodyRaycastResult = m_componentMiner.Raycast<BodyRaycastResult>(ray, RaycastMode.Interaction);
557 if (bodyRaycastResult.HasValue
558 && bodyRaycastResult.Value.Distance < MaxAttackRange
559 && (bodyRaycastResult.Value.ComponentBody == target
560 || bodyRaycastResult.Value.ComponentBody.IsChildOfBody(target)
561 || target.IsChildOfBody(bodyRaycastResult.Value.ComponentBody)
562 || (target.StandingOnBody == bodyRaycastResult.Value.ComponentBody && AllowAttackingStandingOnBody))) {
563 hitPoint = bodyRaycastResult.Value.HitPoint();
564 return bodyRaycastResult.Value.ComponentBody;
565 }
566 hitPoint = default;
567 return null;
568 }
569 }
570}
static float Remainder(float x, float y)
static int Min(int x1, int x2)
static int Max(int x1, int x2)
virtual ComponentBody ParentBody
virtual BoundingBox BoundingBox
virtual ComponentBody StandingOnBody
virtual bool IsChildOfBody(ComponentBody componentBody)
virtual float ScoreTarget(ComponentCreature componentCreature)
DynamicArray< ComponentBody > m_componentBodies
virtual bool IsTargetInAttackRange(ComponentBody target)
virtual bool IsBodyInAttackRange(ComponentBody target)
virtual ComponentCreature FindTarget()
ComponentRandomFeedBehavior m_componentFeedBehavior
ComponentCreatureModel m_componentCreatureModel
virtual void Attack(ComponentCreature componentCreature, float maxRange, float maxChaseTime, bool isPersistent)
virtual ComponentBody GetHitBody(ComponentBody target, out Vector3 hitPoint)
override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap)
Dictionary< ModLoader, Action > Hooks
virtual bool IsTargetInWater(ComponentBody target)
virtual ComponentCreature Attacker
void TransitionTo(string stateName)
ValuesDictionary ValuesDictionary
Component FindComponent(Type type, string name, bool throwOnError)
static void HookAction(string HookName, Func< ModLoader, bool > action)
执行Hook
static Vector3 Normalize(Vector3 v)
static float Distance(Vector3 v1, Vector3 v2)
static readonly Vector3 UnitY
static float Dot(Vector3 v1, Vector3 v2)