diff --git a/source/simulation2/components/CCmpUnitMotion.cpp b/source/simulation2/components/CCmpUnitMotion.cpp
index 741c749..59552a2 100644
a
|
b
|
public:
|
339 | 339 | serialize.NumberFixed_Unbounded("target max range", m_TargetMaxRange); |
340 | 340 | |
341 | 341 | serialize.NumberFixed_Unbounded("speed", m_Speed); |
| 342 | serialize.NumberFixed_Unbounded("current speed", m_CurSpeed); |
342 | 343 | |
343 | 344 | serialize.Bool("moving", m_Moving); |
344 | 345 | serialize.Bool("facePointAfterMove", m_FacePointAfterMove); |
diff --git a/source/simulation2/components/CCmpVisualActor.cpp b/source/simulation2/components/CCmpVisualActor.cpp
index 0d701d5..7c65360 100644
a
|
b
|
private:
|
81 | 81 | std::wstring m_SoundGroup; |
82 | 82 | fixed m_AnimDesync; |
83 | 83 | fixed m_AnimSyncRepeatTime; // 0.0 if not synced |
| 84 | fixed m_AnimSyncOffsetTime; |
84 | 85 | |
85 | 86 | std::map<CStr, CStr> m_VariantSelections; |
86 | 87 | |
… |
… |
public:
|
202 | 203 | |
203 | 204 | m_VisibleInAtlasOnly = paramNode.GetChild("VisibleInAtlasOnly").ToBool(); |
204 | 205 | m_IsActorOnly = paramNode.GetChild("ActorOnly").IsOk(); |
205 | | |
| 206 | |
206 | 207 | InitModel(paramNode); |
207 | 208 | |
208 | | // We need to select animation even if graphics are disabled, as this modifies serialized state |
209 | 209 | SelectAnimation("idle", false, fixed::FromInt(1), L""); |
210 | 210 | } |
211 | 211 | |
… |
… |
public:
|
225 | 225 | serialize.NumberFixed_Unbounded("g", m_G); |
226 | 226 | serialize.NumberFixed_Unbounded("b", m_B); |
227 | 227 | |
| 228 | SerializeMap<SerializeString, SerializeString>()(serialize, "anim overrides", m_AnimOverride); |
| 229 | |
228 | 230 | serialize.NumberFixed_Unbounded("anim run threshold", m_AnimRunThreshold); |
229 | 231 | serialize.StringASCII("anim name", m_AnimName, 0, 256); |
230 | 232 | serialize.Bool("anim once", m_AnimOnce); |
… |
… |
public:
|
232 | 234 | serialize.String("sound group", m_SoundGroup, 0, 256); |
233 | 235 | serialize.NumberFixed_Unbounded("anim desync", m_AnimDesync); |
234 | 236 | serialize.NumberFixed_Unbounded("anim sync repeat time", m_AnimSyncRepeatTime); |
| 237 | serialize.NumberFixed_Unbounded("anim sync offset time", m_AnimSyncOffsetTime); |
235 | 238 | |
236 | 239 | SerializeMap<SerializeString, SerializeString>()(serialize, "variation", m_VariantSelections); |
237 | 240 | |
… |
… |
public:
|
264 | 267 | // If we serialized a different seed or different actor, reload actor |
265 | 268 | if (oldSeed != GetActorSeed() || m_BaseActorName != m_ActorName) |
266 | 269 | ReloadActor(); |
267 | | else if (m_Unit) |
268 | | m_Unit->SetEntitySelection(m_VariantSelections); |
269 | | |
270 | | fixed repeattime = m_AnimSyncRepeatTime; // save because SelectAnimation overwrites it |
271 | | |
272 | | if (m_AnimRunThreshold.IsZero()) |
273 | | SelectAnimation(m_AnimName, m_AnimOnce, m_AnimSpeed, m_SoundGroup); |
274 | 270 | else |
275 | | SelectMovementAnimation(m_AnimRunThreshold); |
276 | | |
277 | | SetAnimationSyncRepeat(repeattime); |
| 271 | ReloadUnitAnimation(); |
278 | 272 | |
279 | 273 | if (m_Unit) |
280 | 274 | { |
… |
… |
public:
|
286 | 280 | |
287 | 281 | virtual void HandleMessage(const CMessage& msg, bool UNUSED(global)) |
288 | 282 | { |
289 | | // Quick exit for running in non-graphical mode |
290 | | if (m_Unit == NULL) |
291 | | return; |
292 | | |
293 | 283 | switch (msg.GetType()) |
294 | 284 | { |
295 | 285 | case MT_Update_Final: |
… |
… |
public:
|
300 | 290 | } |
301 | 291 | case MT_OwnershipChanged: |
302 | 292 | { |
| 293 | if (!m_Unit) |
| 294 | break; |
| 295 | |
303 | 296 | const CMessageOwnershipChanged& msgData = static_cast<const CMessageOwnershipChanged&> (msg); |
304 | 297 | m_Unit->GetModel().SetPlayerID(msgData.to); |
305 | 298 | break; |
306 | 299 | } |
307 | 300 | case MT_TerrainChanged: |
308 | 301 | { |
| 302 | if (!m_Unit) |
| 303 | break; |
| 304 | |
309 | 305 | const CMessageTerrainChanged& msgData = static_cast<const CMessageTerrainChanged&> (msg); |
310 | 306 | m_Unit->GetModel().SetTerrainDirty(msgData.i0, msgData.j0, msgData.i1, msgData.j1); |
311 | 307 | break; |
… |
… |
public:
|
420 | 416 | virtual void SetVariant(const CStr& key, const CStr& selection) |
421 | 417 | { |
422 | 418 | m_VariantSelections[key] = selection; |
423 | | if (!m_Unit) |
424 | | return; |
425 | | m_Unit->SetEntitySelection(key, selection); |
| 419 | |
| 420 | if (m_Unit) |
| 421 | m_Unit->SetEntitySelection(key, selection); |
426 | 422 | } |
427 | 423 | |
428 | 424 | virtual void SelectAnimation(const std::string& name, bool once, fixed speed, const std::wstring& soundgroup) |
… |
… |
public:
|
435 | 431 | m_AnimDesync = fixed::FromInt(1)/20; // TODO: make this an argument |
436 | 432 | m_AnimSyncRepeatTime = fixed::Zero(); |
437 | 433 | |
438 | | if (m_Unit) |
439 | | { |
440 | | SetVariant("animation", m_AnimName); |
441 | | if (m_Unit->GetAnimation()) |
442 | | m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str()); |
443 | | } |
| 434 | SetVariant("animation", m_AnimName); |
| 435 | |
| 436 | if (m_Unit && m_Unit->GetAnimation()) |
| 437 | m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str()); |
444 | 438 | } |
445 | 439 | |
446 | 440 | virtual void ReplaceMoveAnimation(const std::string& name, const std::string& replace) |
… |
… |
public:
|
457 | 451 | |
458 | 452 | virtual void SelectMovementAnimation(fixed runThreshold) |
459 | 453 | { |
| 454 | SelectAnimation("walk", false, fixed::FromFloat(1.f), L""); |
460 | 455 | m_AnimRunThreshold = runThreshold; |
461 | | |
462 | | if (m_Unit) |
463 | | { |
464 | | SetVariant("animation", "walk"); |
465 | | if (m_Unit->GetAnimation()) |
466 | | m_Unit->GetAnimation()->SetAnimationState("walk", false, 1.f, 0.f, L""); |
467 | | } |
468 | 456 | } |
469 | 457 | |
470 | 458 | virtual void SetAnimationSyncRepeat(fixed repeattime) |
471 | 459 | { |
472 | 460 | m_AnimSyncRepeatTime = repeattime; |
473 | 461 | |
474 | | if (m_Unit) |
475 | | { |
476 | | if (m_Unit->GetAnimation()) |
477 | | m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat()); |
478 | | } |
| 462 | if (m_Unit && m_Unit->GetAnimation()) |
| 463 | m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat()); |
479 | 464 | } |
480 | 465 | |
481 | 466 | virtual void SetAnimationSyncOffset(fixed actiontime) |
482 | 467 | { |
483 | | if (m_Unit) |
484 | | { |
485 | | if (m_Unit->GetAnimation()) |
486 | | m_Unit->GetAnimation()->SetAnimationSyncOffset(actiontime.ToFloat()); |
487 | | } |
| 468 | m_AnimSyncOffsetTime = actiontime; |
| 469 | |
| 470 | if (m_Unit && m_Unit->GetAnimation()) |
| 471 | m_Unit->GetAnimation()->SetAnimationSyncOffset(m_AnimSyncOffsetTime.ToFloat()); |
488 | 472 | } |
489 | 473 | |
490 | 474 | virtual void SetShadingColor(fixed r, fixed g, fixed b, fixed a) |
… |
… |
public:
|
504 | 488 | virtual void SetVariable(const std::string& name, float value) |
505 | 489 | { |
506 | 490 | if (m_Unit) |
507 | | { |
508 | 491 | m_Unit->GetModel().SetEntityVariable(name, value); |
509 | | } |
510 | 492 | } |
511 | 493 | |
512 | 494 | virtual u32 GetActorSeed() |
513 | 495 | { |
514 | 496 | return m_Seed; |
515 | 497 | } |
516 | | |
| 498 | |
517 | 499 | virtual void SetActorSeed(u32 seed) |
518 | 500 | { |
519 | 501 | if (seed == m_Seed) |
… |
… |
private:
|
546 | 528 | /// Helper method; initializes the model selection shape descriptor from XML. Factored out for readability of @ref Init. |
547 | 529 | void InitSelectionShapeDescriptor(const CParamNode& paramNode); |
548 | 530 | |
| 531 | // ReloadActor is used when the actor or seed changes. |
549 | 532 | void ReloadActor(); |
| 533 | // ReloadUnitAnimation is used for a minimal reloading upon deserialization, when the actor and seed are identical. |
| 534 | // It is also used by ReloadActor. |
| 535 | void ReloadUnitAnimation(); |
550 | 536 | |
551 | 537 | void Update(fixed turnLength); |
552 | 538 | }; |
… |
… |
void CCmpVisualActor::InitSelectionShapeDescriptor(const CParamNode& paramNode)
|
653 | 639 | float size0 = fpSize0.ToFloat(); |
654 | 640 | float size1 = fpSize1.ToFloat(); |
655 | 641 | |
656 | | // TODO: we should properly distinguish between CIRCLE and SQUARE footprint shapes here, but since cylinders |
657 | | // aren't implemented yet and are almost indistinguishable from boxes for small enough sizes anyway, |
658 | | // we'll just use boxes for either case. However, for circular footprints the size0 and size1 values both |
659 | | // represent the radius, so we do have to adjust them to match the size1 and size0's of square footprints |
| 642 | // TODO: we should properly distinguish between CIRCLE and SQUARE footprint shapes here, but since cylinders |
| 643 | // aren't implemented yet and are almost indistinguishable from boxes for small enough sizes anyway, |
| 644 | // we'll just use boxes for either case. However, for circular footprints the size0 and size1 values both |
| 645 | // represent the radius, so we do have to adjust them to match the size1 and size0's of square footprints |
660 | 646 | // (which represent the full width and depth). |
661 | 647 | if (fpShape == ICmpFootprint::CIRCLE) |
662 | 648 | { |
… |
… |
void CCmpVisualActor::ReloadActor()
|
720 | 706 | |
721 | 707 | InitModel(node->GetChild("VisualActor")); |
722 | 708 | |
723 | | m_Unit->SetEntitySelection(m_VariantSelections); |
724 | | |
725 | | if (m_Unit->GetAnimation()) |
726 | | m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str()); |
727 | | |
728 | | // We'll lose the exact synchronisation but we should at least make sure it's going at the correct rate |
729 | | if (!m_AnimSyncRepeatTime.IsZero()) |
730 | | if (m_Unit->GetAnimation()) |
731 | | m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat()); |
| 709 | ReloadUnitAnimation(); |
732 | 710 | |
733 | 711 | m_Unit->GetModel().SetShadingColor(shading); |
734 | 712 | |
… |
… |
void CCmpVisualActor::ReloadActor()
|
743 | 721 | } |
744 | 722 | } |
745 | 723 | |
746 | | void CCmpVisualActor::Update(fixed UNUSED(turnLength)) |
| 724 | void CCmpVisualActor::ReloadUnitAnimation() |
747 | 725 | { |
748 | 726 | if (!m_Unit) |
749 | 727 | return; |
750 | 728 | |
751 | | // If we're in the special movement mode, select an appropriate animation |
752 | | if (!m_AnimRunThreshold.IsZero()) |
753 | | { |
754 | | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); |
755 | | if (!cmpPosition || !cmpPosition->IsInWorld()) |
756 | | return; |
| 729 | m_Unit->SetEntitySelection(m_VariantSelections); |
757 | 730 | |
758 | | CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetEntityHandle()); |
759 | | if (!cmpUnitMotion) |
760 | | return; |
| 731 | if (!m_Unit->GetAnimation()) |
| 732 | return; |
761 | 733 | |
762 | | float speed = cmpUnitMotion->GetCurrentSpeed().ToFloat(); |
| 734 | m_Unit->GetAnimation()->SetAnimationState(m_AnimName, m_AnimOnce, m_AnimSpeed.ToFloat(), m_AnimDesync.ToFloat(), m_SoundGroup.c_str()); |
763 | 735 | |
764 | | std::string name; |
765 | | if (speed == 0.0f) |
766 | | name = "idle"; |
767 | | else |
768 | | name = (speed < m_AnimRunThreshold.ToFloat()) ? "walk" : "run"; |
| 736 | // We'll lose the exact synchronisation but we should at least make sure it's going at the correct rate |
| 737 | if (!m_AnimSyncRepeatTime.IsZero()) |
| 738 | m_Unit->GetAnimation()->SetAnimationSyncRepeat(m_AnimSyncRepeatTime.ToFloat()); |
| 739 | if (!m_AnimSyncOffsetTime.IsZero()) |
| 740 | m_Unit->GetAnimation()->SetAnimationSyncOffset(m_AnimSyncOffsetTime.ToFloat()); |
| 741 | } |
769 | 742 | |
770 | | std::map<std::string, std::string>::const_iterator it = m_AnimOverride.find(name); |
771 | | if (it != m_AnimOverride.end()) |
772 | | name = it->second; |
| 743 | void CCmpVisualActor::Update(fixed UNUSED(turnLength)) |
| 744 | { |
| 745 | // This function is currently only used to update the animation if the speed in |
| 746 | // CCmpUnitMotion changes. This also only happens in the "special movement mode" |
| 747 | // triggered by SelectMovementAnimation. |
773 | 748 | |
774 | | SetVariant("animation", name); |
775 | | if (m_Unit->GetAnimation()) |
776 | | { |
777 | | if (speed == 0.0f) |
778 | | m_Unit->GetAnimation()->SetAnimationState(name, false, 1.f, 0.f, L""); |
779 | | else |
780 | | m_Unit->GetAnimation()->SetAnimationState(name, false, speed, 0.f, L""); |
781 | | } |
| 749 | // TODO: This should become event based, in order to save performance and to make the code |
| 750 | // far less hacky. We should also take into account the speed when the animation is different |
| 751 | // from the "special movement mode" walking animation. |
| 752 | |
| 753 | // If we're not in the special movement mode, nothing to do. |
| 754 | if (m_AnimRunThreshold.IsZero()) |
| 755 | return; |
| 756 | |
| 757 | CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); |
| 758 | if (!cmpPosition || !cmpPosition->IsInWorld()) |
| 759 | return; |
| 760 | |
| 761 | CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetEntityHandle()); |
| 762 | if (!cmpUnitMotion) |
| 763 | return; |
| 764 | |
| 765 | fixed speed = cmpUnitMotion->GetCurrentSpeed(); |
| 766 | std::string name; |
| 767 | |
| 768 | if (speed.IsZero()) |
| 769 | { |
| 770 | speed = fixed::FromFloat(1.f); |
| 771 | name = "idle"; |
782 | 772 | } |
| 773 | else |
| 774 | name = speed < m_AnimRunThreshold ? "walk" : "run"; |
| 775 | |
| 776 | std::map<std::string, std::string>::const_iterator it = m_AnimOverride.find(name); |
| 777 | if (it != m_AnimOverride.end()) |
| 778 | name = it->second; |
| 779 | |
| 780 | // Selecting the animation is going to reset the anim run threshold, so save it |
| 781 | fixed runThreshold = m_AnimRunThreshold; |
| 782 | SelectAnimation(name, false, speed, L""); |
| 783 | m_AnimRunThreshold = runThreshold; |
783 | 784 | } |