public void RunCombatActUsageAnimation(ActDescription usedActDescription, IGraphNode targetNode)
        {
            if (!CanDraw)
            {
                return;
            }

            var serviceScope = ((LivGame)_game).ServiceProvider;

            var animationBlockerService = serviceScope.GetRequiredService <IAnimationBlockerService>();

            var hexSize = MapMetrics.UnitSize / 2;
            var playerActorWorldCoords = HexHelper.ConvertToWorld(((HexNode)targetNode).OffsetCoords);
            var newPosition            = new Vector2(
                (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                playerActorWorldCoords[1] * hexSize * 2 / 2
                );

            var targetSpritePosition = newPosition;

            var attackSoundEffectInstance = GetAttackSoundEffect(usedActDescription);
            var hitVisualEffect           = GetAttackVisualEffect(targetNode, targetSpritePosition, usedActDescription);

            var stateEngine = new ActorMeleeAttackEngine(
                _rootSprite,
                targetSpritePosition,
                animationBlockerService,
                attackSoundEffectInstance,
                hitVisualEffect);

            AddStateEngine(stateEngine);
        }
Beispiel #2
0
    private List <MapNodeVM> InitNodeViewModels()
    {
        var nodeVMs = new List <MapNodeVM>();

        foreach (var node in _sector.Map.Nodes)
        {
            var mapNodeVm = Instantiate(MapNodePrefab, transform);

            var hexNode = (HexNode)node;
            var nodeWorldPositionParts = HexHelper.ConvertToWorld(hexNode.OffsetX, hexNode.OffsetY);
            var worldPosition          = new Vector3(nodeWorldPositionParts[0], nodeWorldPositionParts[1] / 2);
            mapNodeVm.transform.position = worldPosition;
            mapNodeVm.Node = hexNode;

            var edges     = _sector.Map.Edges.Where(x => x.Nodes.Contains(node)).ToArray();
            var neighbors = (from edge in edges
                             from neighbor in edge.Nodes
                             where neighbor != node
                             select neighbor).Cast <HexNode>().ToArray();

            mapNodeVm.Edges     = edges;
            mapNodeVm.Neighbors = neighbors;

            if (_sector.ExitNodes.Contains(node))
            {
                mapNodeVm.IsExit = true;
            }

            mapNodeVm.OnSelect += MapNodeVm_OnSelect;

            nodeVMs.Add(mapNodeVm);
        }

        return(nodeVMs);
    }
Beispiel #3
0
        private void Actor_UsedProp(object?sender, UsedPropEventArgs e)
        {
            var serviceScope                = ((LivGame)_game).ServiceProvider;
            var animationBlockerService     = serviceScope.GetRequiredService <IAnimationBlockerService>();
            var visualizationContentStorage = serviceScope.GetRequiredService <IGameObjectVisualizationContentStorage>();

            var consumableType = e.UsedProp.Scheme.Sid switch
            {
                "med-kit" => ConsumeEffectType.Heal,
                "water-bottle" => ConsumeEffectType.Drink,
                "packed-food" => ConsumeEffectType.Eat,
                _ => ConsumeEffectType.UseCommon
            };

            var soundEffect = _personSoundStorage.GetConsumePropSound(consumableType);

            _actorStateEngine = new ActorCommonActionMoveEngine(_graphicsRoot.RootSprite, animationBlockerService,
                                                                soundEffect?.CreateInstance());

            var hexSize   = MapMetrics.UnitSize / 2;
            var actorNode = (HexNode)(Actor.Node);
            var playerActorWorldCoords = HexHelper.ConvertToWorld(actorNode.OffsetCoords.X, actorNode.OffsetCoords.Y);
            var actorPosition          = new Vector2(
                (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                playerActorWorldCoords[1] * hexSize * 2 / 2
                );

            const int START_EFFECT_Y = 24;
            var       consumeEffect  = new ConsumingEffect(visualizationContentStorage,
                                                           actorPosition - (Vector2.UnitY * START_EFFECT_Y),
                                                           consumableType
                                                           );

            _sectorViewModelContext.EffectManager.VisualEffects.Add(consumeEffect);
        }
    public void Update()
    {
        var moveCommand = (MoveCommand)_moveCommand;
        var path        = moveCommand.Path;

        foreach (Transform visualizationItem in transform)
        {
            Destroy(visualizationItem.gameObject);
        }

        if (_playerState.HoverViewModel is IMapNodeViewModel)
        {
            _lastPath = path;

            if (_lastPath != null)
            {
                foreach (var pathNode in _lastPath)
                {
                    var hexPathNode   = (HexNode)pathNode;
                    var worldPosition = HexHelper.ConvertToWorld(hexPathNode.OffsetCoords);

                    var item = Instantiate(VisualizationItemPrefab, transform);
                    item.transform.position = new Vector3(worldPosition[0], worldPosition[1] / 2);
                }
            }
        }
    }
Beispiel #5
0
    private List <MapNodeVM> InitNodeViewModels()
    {
        var map     = _humanPlayer.SectorNode.Sector.Map;
        var nodeVMs = new List <MapNodeVM>();

        foreach (var node in map.Nodes)
        {
            var mapNodeObj = _container.InstantiatePrefab(MapNodePrefab, transform);

            var mapNodeVm = mapNodeObj.GetComponent <MapNodeVM>();

            var hexNode = (HexNode)node;
            var nodeWorldPositionParts = HexHelper.ConvertToWorld(hexNode.OffsetCoords);
            var worldPosition          = new Vector3(nodeWorldPositionParts[0], nodeWorldPositionParts[1] / 2);
            mapNodeVm.transform.position = worldPosition;
            mapNodeVm.Node            = hexNode;
            mapNodeVm.Neighbors       = map.GetNext(node).Cast <HexNode>().ToArray();
            mapNodeVm.LocaltionScheme = _sectorManager.CurrentSector.Scheme;

            if (map.Transitions.ContainsKey(node))
            {
                mapNodeVm.IsExit = true;
            }

            mapNodeVm.OnSelect   += MapNodeVm_OnSelect;
            mapNodeVm.MouseEnter += MapNodeVm_MouseEnter;

            nodeVMs.Add(mapNodeVm);
        }

        return(nodeVMs);
    }
Beispiel #6
0
    public void Start()
    {
        var map = new LazyHexMap(100);

        foreach (var node in map.Nodes)
        {
            var hexObject = Instantiate(HexPrefab, Parent);

            var position = HexHelper.ConvertToWorld(node.Offset.X, node.Offset.Y);

            hexObject.transform.position = new Vector3(position[0], position[1] / 2, 0);

            var clientModel = hexObject.GetComponent <GlobalTerrainNode>();
            clientModel.Init(node);

            _nodeModels.Add(clientModel);

            clientModel.Clicked += ClientModel_Clicked;
        }

        var army       = new Army(map.Nodes.First());
        var armyObject = Instantiate(ArmyPrefab, Parent);

        armyObject.Init(army);
        armyObject.Clicked += ArmyObject_Clicked;

        _armyModels.Add(armyObject);
    }
Beispiel #7
0
        public StaticObjectViewModel(Game game, IStaticObject staticObject, SpriteBatch spriteBatch)
        {
            _game        = game;
            StaticObject = staticObject;
            _spriteBatch = spriteBatch;

            var graphics = new StaticObjectGraphics(game, staticObject);

            var worldCoords = HexHelper.ConvertToWorld(((HexNode)StaticObject.Node).OffsetCoords);

            var hexSize = MapMetrics.UnitSize / 2;
            var staticObjectPosition = new Vector2(
                (int)Math.Round(worldCoords[0] * hexSize * Math.Sqrt(3), MidpointRounding.ToEven),
                (int)Math.Round(worldCoords[1] * hexSize * 2 / 2, MidpointRounding.ToEven)
                );

            _rootSprite = new SpriteContainer
            {
                Position = staticObjectPosition
            };

            var shadowTexture = _game.Content.Load <Texture2D>("Sprites/game-objects/simple-object-shadow");

            _rootSprite.AddChild(new Sprite(shadowTexture)
            {
                Position = new Vector2(0, 0),
                Origin   = new Vector2(0.5f, 0.5f),
                Color    = new Color(Color.White, 0.5f)
            });

            _rootSprite.AddChild(graphics);
        }
Beispiel #8
0
    private void Actor_Moved(object sender, EventArgs e)
    {
        _moveCounter = 0;
        var actorNode          = (HexNode)Actor.Node;
        var worldPositionParts = HexHelper.ConvertToWorld(actorNode.OffsetX, actorNode.OffsetY);

        _targetPosition = new Vector3(worldPositionParts[0], worldPositionParts[1] / 2, -1);
    }
Beispiel #9
0
    private void Actor_OpenedContainer(object sender, OpenContainerEventArgs e)
    {
        var containerNode      = (HexNode)e.Container.Node;
        var worldPositionParts = HexHelper.ConvertToWorld(containerNode.OffsetX, containerNode.OffsetY);
        var targetPosition     = new Vector3(worldPositionParts[0], worldPositionParts[1] / 2, -1);

        GraphicRoot.ProcessInteractive(targetPosition);
    }
        private void Actor_Moved(object?sender, ActorMoveEventArgs e)
        {
            var hexSize = MapMetrics.UnitSize / 2;
            var playerActorWorldCoords = HexHelper.ConvertToWorld(((HexNode)Actor.Node).OffsetCoords);
            var newPosition            = new Vector2(
                (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                playerActorWorldCoords[1] * hexSize * 2 / 2
                );

            if (!CanDraw)
            {
                _rootSprite.Position = newPosition;

                return;
            }

            var serviceScope = ((LivGame)_game).ServiceProvider;

            var animationBlockerService = serviceScope.GetRequiredService <IAnimationBlockerService>();

            SoundEffectInstance?moveSoundEffectInstance = null;

            var player = serviceScope.GetRequiredService <IPlayer>();

            if (sender is IActor actor && actor.Person == player.MainPerson)
            {
                // Sound steps of main person only to prevent infinite steps loop.
                var moveSoundEffect = _personSoundStorage.GetActivitySound(PersonActivityEffectType.Move);
                moveSoundEffectInstance = moveSoundEffect?.CreateInstance();
            }

            if (!e.Forced)
            {
                var moveEngine = new ActorMoveEngine(
                    _rootSprite,
                    _graphicsRoot.RootSprite,
                    _shadowSprite,
                    newPosition,
                    animationBlockerService,
                    moveSoundEffectInstance);

                AddStateEngine(moveEngine);
            }
            else
            {
                var pushEngine = new ActorPushEngine(
                    _rootSprite,
                    _graphicsRoot.RootSprite,
                    _shadowSprite,
                    newPosition,
                    animationBlockerService,
                    moveSoundEffectInstance);

                AddStateEngine(pushEngine);
            }
        }
Beispiel #11
0
        public float[] ConvertToWorldTest(int offsetX, int offsetY)
        {
            // ARRANGE

            // ACT
            var factCubeCoords = HexHelper.ConvertToWorld(offsetX, offsetY);

            // ASSERT
            return(factCubeCoords);
        }
Beispiel #12
0
        private void Actor_UsedAct(object?sender, UsedActEventArgs e)
        {
            var stats = e.TacticalAct.Stats;

            if (stats is null)
            {
                throw new InvalidOperationException("The act has no stats to select visualization.");
            }

            Debug.WriteLine(e.TacticalAct);

            if (CanDraw)
            {
                if (stats.Effect == TacticalActEffectType.Damage && stats.IsMelee)
                {
                    var serviceScope = ((LivGame)_game).ServiceProvider;

                    var animationBlockerService = serviceScope.GetRequiredService <IAnimationBlockerService>();

                    var hexSize = MapMetrics.UnitSize / 2;
                    var playerActorWorldCoords = HexHelper.ConvertToWorld(((HexNode)e.TargetNode).OffsetCoords);
                    var newPosition            = new Vector2(
                        (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                        playerActorWorldCoords[1] * hexSize * 2 / 2
                        );

                    var targetSpritePosition = newPosition;

                    var attackSoundEffectInstance = GetSoundEffect(e.TacticalAct.Stats);
                    _actorStateEngine =
                        new ActorMeleeAttackEngine(
                            _rootSprite,
                            targetSpritePosition,
                            animationBlockerService,
                            attackSoundEffectInstance);

                    // Selection actors only prevent error when monster stays on loot bag.
                    var targetGameObject =
                        _sectorViewModelContext.GameObjects.SingleOrDefault(x =>
                                                                            x is IActorViewModel && x.Node == e.TargetNode);
                    if (targetGameObject is null)
                    {
                        // This means the attacker is miss.
                        // This situation can be then the target actor moved before the attack reaches the target.
                    }
                    else
                    {
                        var hitEffect = new HitEffect((LivGame)_game,
                                                      targetSpritePosition + targetGameObject.HitEffectPosition,
                                                      targetSpritePosition - _rootSprite.Position);
                        _sectorViewModelContext.EffectManager.VisualEffects.Add(hitEffect);
                    }
                }
            }
        }
Beispiel #13
0
    private void Actor_Moved(object sender, EventArgs e)
    {
        _moveCounter = 0;
        var actorHexNode       = (HexNode)Actor.Node;
        var worldPositionParts = HexHelper.ConvertToWorld(actorHexNode.OffsetX, actorHexNode.OffsetY);

        _targetPosition     = new Vector3(worldPositionParts[0], worldPositionParts[1] / 2, actorHexNode.OffsetY - 0.26f);
        _moveCommandBlocker = new MoveCommandBlocker();
        _commandBlockerService.AddBlocker(_moveCommandBlocker);
        GraphicRoot.ProcessMove(_targetPosition);
    }
        private void ResetActorRootSpritePosition()
        {
            var hexSize = MapMetrics.UnitSize / 2;
            var playerActorWorldCoords = HexHelper.ConvertToWorld(((HexNode)Actor.Node).OffsetCoords);
            var newPosition            = new Vector2(
                (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                playerActorWorldCoords[1] * hexSize * 2 / 2
                );

            _rootSprite.Position = newPosition;
        }
Beispiel #15
0
        private void UpdateTargetPosition(IActorViewModel target)
        {
            var playerActorWorldCoords = HexHelper.ConvertToWorld(((HexNode)target.Actor.Node).OffsetCoords);

            var hexSize       = MapMetrics.UnitSize / 2;
            var actorPosition = new Vector2(
                (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                playerActorWorldCoords[1] * hexSize * 2 / 2
                );

            _targetPosition = actorPosition;
        }
        public StaticObjectViewModel(Game game, IStaticObject staticObject, SpriteBatch spriteBatch,
                                     bool createHighlighted = false)
        {
            _game        = game;
            StaticObject = staticObject;
            _spriteBatch = spriteBatch;

            var graphics = new StaticObjectGraphics(game, staticObject);

            var worldCoords = HexHelper.ConvertToWorld(((HexNode)StaticObject.Node).OffsetCoords);

            var hexSize = MapMetrics.UnitSize / 2;
            var staticObjectPosition = new Vector2(
                (int)Math.Round(worldCoords[0] * hexSize * Math.Sqrt(HIGHLIGHT_DURATION_SECONDS),
                                MidpointRounding.ToEven),
                (int)Math.Round(worldCoords[1] * hexSize * 2 / 2, MidpointRounding.ToEven)
                );

            _rootSprite = new SpriteContainer
            {
                Position = staticObjectPosition
            };

            var hasNoShadow = StaticObject.Purpose == PropContainerPurpose.Puddle ||
                              StaticObject.Purpose == PropContainerPurpose.Pit;

            if (!hasNoShadow)
            {
                var shadowTexture = _game.Content.Load <Texture2D>("Sprites/game-objects/simple-object-shadow");
                _rootSprite.AddChild(new Sprite(shadowTexture)
                {
                    Position = new Vector2(0, 0),
                    Origin   = new Vector2(0.5f, 0.5f),
                    Color    = new Color(Color.White, 0.5f)
                });
            }

            _rootSprite.AddChild(graphics);

            if (createHighlighted)
            {
                _highlightCounter = HIGHLIGHT_DURATION_SECONDS;
            }
        }
        public void RunDamageReceivedAnimation(IGraphNode attackerNode)
        {
            var serviceScope            = ((LivGame)_game).ServiceProvider;
            var animationBlockerService = serviceScope.GetRequiredService <IAnimationBlockerService>();

            var soundEffectInstance = GetPersonImpactSoundEffect(Actor.Person);

            var hexSize = MapMetrics.UnitSize / 2;
            var playerActorWorldCoords = HexHelper.ConvertToWorld(((HexNode)attackerNode).OffsetCoords);
            var attackerPosition       = new Vector2(
                (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                playerActorWorldCoords[1] * hexSize * 2 / 2
                );

            var moveEngine = new ActorDamagedEngine(_graphicsRoot, _rootSprite, attackerPosition,
                                                    animationBlockerService,
                                                    soundEffectInstance);

            AddStateEngine(moveEngine);
        }
Beispiel #18
0
        private static void DrawAllNodes(IEnumerable <HexNode> nodes, Bitmap bitmap, ImageInfo info)
        {
            using (var graphics = Graphics.FromImage(bitmap))
            {
                Clear(bitmap, graphics);

                DrawAxisNumbers(info, graphics);

                foreach (var node in nodes)
                {
                    var coords = HexHelper.ConvertToWorld(node.OffsetCoords);

                    var x = (coords[0] - info.LeftCoord) * CELLSIZE;
                    var y = (coords[1] - info.BottomCoord) * CELLSIZE;

                    var cellBrush = Brushes.White;

                    graphics.FillEllipse(cellBrush, x + MARGIN, y + MARGIN, CELLSIZE, CELLSIZE);
                }
            }
        }
Beispiel #19
0
        public void Follow(IActorViewModel target, Game game)
        {
            var playerActorWorldCoords = HexHelper.ConvertToWorld(((HexNode)target.Actor.Node).OffsetCoords);

            var hexSize       = MapMetrics.UnitSize / 2;
            var actorPosition = new Vector2(
                (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                playerActorWorldCoords[1] * hexSize * 2 / 2
                );

            var position = Matrix.CreateTranslation(
                -actorPosition.X,
                -actorPosition.Y,
                0);

            var offset = Matrix.CreateTranslation(
                (float)game.GraphicsDevice.Viewport.Width / 2,
                (float)game.GraphicsDevice.Viewport.Height / 2,
                0);

            Transform = position * offset;
        }
Beispiel #20
0
        public override void Update(GameTime gameTime)
        {
            if (_actorStateEngine != null)
            {
                _actorStateEngine.Update(gameTime);
                if (_actorStateEngine.IsComplete)
                {
                    _actorStateEngine = new ActorIdleEngine(_graphicsRoot.RootSprite);

                    var hexSize = MapMetrics.UnitSize / 2;
                    var playerActorWorldCoords = HexHelper.ConvertToWorld(((HexNode)Actor.Node).OffsetCoords);
                    var newPosition            = new Vector2(
                        (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                        playerActorWorldCoords[1] * hexSize * 2 / 2
                        );

                    _rootSprite.Position = newPosition;
                }
            }

            var keyboard = Keyboard.GetState();

            _graphicsRoot.ShowOutlined = keyboard.IsKeyDown(Keys.LeftAlt);
        }
Beispiel #21
0
        private void UpdateSpriteMatrix(GameTime gameTime)
        {
            _updateCounter -= gameTime.ElapsedGameTime.TotalSeconds;
            if (_updateCounter > 0)
            {
                return;
            }

            _updateCounter = MAP_UPDATE_DELAY_SECONDS;

            if (_player.MainPerson is null)
            {
                throw new InvalidOperationException();
            }

            var fowData            = _player.MainPerson.GetModule <IFowData>();
            var visibleFowNodeData = fowData.GetSectorFowData(_sector);

            if (visibleFowNodeData is null)
            {
                throw new InvalidOperationException();
            }

            var materializedNodes = visibleFowNodeData.Nodes.ToArray();

            Parallel.ForEach(materializedNodes, fowNode =>
            {
                var node = (HexNode)fowNode.Node;

                Color nodeColor;
                if (_uiState.HoverViewModel != null && node == _uiState.HoverViewModel.Item)
                {
                    nodeColor = Color.CornflowerBlue;
                }
                else
                {
                    nodeColor = Color.White;
                }

                if (fowNode.State != SectorMapNodeFowState.Observing)
                {
                    nodeColor = Color.Lerp(nodeColor, new Color(0, 0, 0, 0), 0.5f);
                }

                if (!_hexSprites.TryGetValue(node.OffsetCoords, out var currentHexSprite))
                {
                    var worldCoords = HexHelper.ConvertToWorld(node.OffsetCoords);
                    var hexSize     = MapMetrics.UnitSize / 2;

                    var hexTextureIndex  = node.GetHashCode() % 4;
                    var hexTextureIndexX = hexTextureIndex / 2;
                    var hexTextureIndexY = hexTextureIndex % 2;

                    // Remember. Hex width is less that size (radius).
                    // It equals R*Sqrt(3)/2. So sprite width is R*Sqrt(3)/2*2 or R*Sqrt(3). It's about 28 pixels.
                    // You should make sprite 28*16.
                    var hexSprite = new Sprite(_hexSprite)
                    {
                        SourceRectangle = new Rectangle(hexTextureIndexX * 28, hexTextureIndexY * 16, 28, 16)
                    };

                    var hexSpriteContainer = new SpriteContainer
                    {
                        Position = new Vector2(
                            (float)(worldCoords[0] * hexSize * Math.Sqrt(3)),
                            worldCoords[1] * hexSize * 2 / 2
                            )
                    };
                    hexSpriteContainer.AddChild(hexSprite);

                    if (_sector.Map.Transitions.TryGetValue(fowNode.Node, out var transition))
                    {
                        if (transition.SectorNode.Biome.LocationScheme.Sid == "dungeon" ||
                            transition.SectorNode.Biome.LocationScheme.Sid == "elder-temple")
                        {
                            var transitionMarkerSprite = new Sprite(_hexMarkerTextures)
                            {
                                SourceRectangle = new Rectangle(28, 0, 28, 16)
                            };
                            hexSpriteContainer.AddChild(transitionMarkerSprite);
                        }
                        else
                        {
                            var transitionMarkerSprite = new Sprite(_hexMarkerTextures)
                            {
                                SourceRectangle = new Rectangle(0, 0, 28, 16)
                            };
                            hexSpriteContainer.AddChild(transitionMarkerSprite);
                        }
                    }

                    _hexSprites.AddOrUpdate(node.OffsetCoords, hexSpriteContainer,
                                            (offsetCoords, sprite) => { return(sprite); });
                    currentHexSprite = hexSpriteContainer;
                }

                currentHexSprite.Color = nodeColor;
            });
        }
Beispiel #22
0
    private void Start()
    {
        var map = new FixedMap(25);

        foreach (var node in map.Nodes)
        {
            var hexObject = Instantiate(HexPrefab, Parent);

            var position = HexHelper.ConvertToWorld(node.Offset.X, node.Offset.Y);

            hexObject.transform.position = new Vector3(position[0] * 20, position[1] * 20, 0);

            hexObject.Init(node);

            _nodeModels.Add(hexObject);

            hexObject.Clicked += HexObject_Clicked;

            if (UnityEngine.Random.Range(0, 100) > 70)
            {
                for (var i = 0; i < UnityEngine.Random.Range(3, 7); i++)
                {
                    var wallObject = Instantiate(WallPrefab, hexObject.transform);
                    wallObject.transform.localPosition = UnityEngine.Random.insideUnitCircle * 5;
                }
            }
        }

        var nameGenerator = new IdNameGenerator();

        for (var i = 0; i < 6; i++)
        {
            var node       = map.Nodes.Skip(i * 3 + 1).First();
            var personList = new List <ICombatPerson>();

            for (var j = 0; j < 5; j++)
            {
                var person = new CombatPerson(nameGenerator);
                personList.Add(person);
            }

            var squad = new CombatSquad(node, personList.ToArray(), i < 2 ? Player.Human : Player.Cpu);
            _combatService.SquadManager.Add(squad);

            var squadObject     = Instantiate(CombatSquadPrefab, Parent);
            var personModelList = new List <CombatPersonModel>();
            var formationSize   = Mathf.Sqrt(squad.Persons.Length) + 1;
            var personX         = 0;
            var personY         = 0;
            foreach (var combatPerson in squad.Persons)
            {
                var combatPersonModel = Instantiate(CompatPersonPrefab, squadObject.transform);
                personModelList.Add(combatPersonModel);

                personX++;
                if (personX >= formationSize)
                {
                    personX = 0;
                    personY++;
                }

                combatPersonModel.transform.position = new Vector3(personX * 1.5f, personY * 1.5f);

                combatPersonModel.Clicked    += CombatPersonModelOnClicked;
                combatPersonModel.HoverEnter += CombatPersonModelOnHoverEnter;
                combatPersonModel.HoverExit  += CombatPersonModelOnHoverExit;

                combatPersonModel.Init(combatPerson);
            }

            squadObject.Init(squad, personModelList.ToArray());
            _squadModels.Add(squadObject);
        }

        _combatEventBus.EventRegistered += CombatEventBusOnEventRegistered;
    }
    private async void Start()
    {
        if (_globeManager.Globe == null)
        {
            _globeManager.Globe = await _globeGenerator.GenerateGlobeAsync();

            var firstLocality = _globeManager.Globe.Localities.First();

            _player.Terrain = firstLocality.Cell;

            var createdRegion = await _globeGenerator.GenerateRegionAsync(_globeManager.Globe, firstLocality.Cell);

            _globeManager.Regions[_player.Terrain] = createdRegion;

            var firstNode = (GlobeRegionNode)createdRegion.Nodes.First();

            _player.GlobeNode = firstNode;
        }

        var currentGlobeCell = _player.Terrain;

        _region = _globeManager.Regions[currentGlobeCell];

        _locationNodeViewModels = new List <MapLocation>(100);
        foreach (GlobeRegionNode globeRegionNode in _region.Nodes)
        {
            var worldCoords = HexHelper.ConvertToWorld(globeRegionNode.OffsetX, globeRegionNode.OffsetY);

            var locationObject = _container.InstantiatePrefab(LocationPrefab, transform);
            locationObject.transform.position = new Vector3(worldCoords[0], worldCoords[1], 0);
            var locationViewModel = locationObject.GetComponent <MapLocation>();
            locationViewModel.Node = globeRegionNode;
            _locationNodeViewModels.Add(locationViewModel);

            locationViewModel.OnSelect += LocationViewModel_OnSelect;
            locationViewModel.OnHover  += LocationViewModel_OnHover;
        }

        var openNodeViewModels = new List <MapLocation>(_locationNodeViewModels);

        while (openNodeViewModels.Any())
        {
            var currentNodeViewModel = openNodeViewModels[0];
            openNodeViewModels.Remove(currentNodeViewModel);

            var neighbors          = _region.GetNext(currentNodeViewModel.Node);
            var neighborViewModels = openNodeViewModels.Where(x => neighbors.Contains(x.Node)).ToArray();
            foreach (var neibourNodeViewModel in neighborViewModels)
            {
                var connectorObject    = _container.InstantiatePrefab(ConnectorPrefab, transform);
                var connectorViewModel = connectorObject.GetComponent <MapLocationConnector>();
                connectorViewModel.gameObject1 = currentNodeViewModel.gameObject;
                connectorViewModel.gameObject2 = neibourNodeViewModel.gameObject;
            }
        }

        var playerGroupNodeViewModel = _locationNodeViewModels.Single(x => x.Node == _player.GlobeNode);
        var groupObject = _container.InstantiatePrefab(HumanGroupPrefab, transform);

        _groupViewModel = groupObject.GetComponent <GroupVM>();
        _groupViewModel.CurrentLocation = playerGroupNodeViewModel;
        groupObject.transform.position  = playerGroupNodeViewModel.transform.position;
        Camera.Target = groupObject;

        _player.GlobeNodeChanged += HumanPlayer_GlobeNodeChanged;
        MoveGroupViewModel(_player.GlobeNode);
    }
    private async void Start()
    {
        //TODO Разобраться, почему остаются блоки от перемещения при использовании перехода
        _commandBlockerService.DropBlockers();

        if (_globeManager.Globe == null)
        {
            if (!_progressStorageService.LoadGlobe())
            {
                var globeGenerationResult = await _globeGenerator.GenerateGlobeAsync();

                _globeManager.Globe = globeGenerationResult.Globe;
                _globeManager.GlobeGenerationHistory = globeGenerationResult.History;

                var startCell = _globeManager.Globe.StartProvince;

                _player.Terrain = startCell;

                var createdRegion = await CreateRegionAsync(_globeManager.Globe, _player.Terrain, _globeGenerator, _progressStorageService);
                await CreateNeighborRegionsAsync(_player.Terrain.Coords, _globeManager, _globeGenerator, _progressStorageService);

                _globeManager.Regions[_player.Terrain] = createdRegion;


                var startNode = createdRegion.RegionNodes.Single(x => x.IsStart);

                _player.GlobeNode = startNode;

                startNode.ObservedState = GlobeNodeObservedState.Visited;

                _globeModalManager.ShowHistoryBookModal();
            }
            else
            {
                _globeManager.GlobeGenerationHistory = new GlobeGenerationHistory();

                if (!_progressStorageService.LoadPlayer())
                {
                    var startCell = _globeManager.Globe.StartProvince;

                    _player.Terrain = startCell;
                }
            }
        }

        var currentGlobeCell = _player.Terrain;

        if (!_globeManager.Regions.TryGetValue(currentGlobeCell, out var currentRegion))
        {
            var createdRegion = await CreateRegionAsync(_globeManager.Globe, currentGlobeCell, _globeGenerator, _progressStorageService);

            _globeManager.Regions[_player.Terrain] = createdRegion;
        }

        // Создание соседних регионов
        await CreateNeighborRegionsAsync(_player.Terrain.Coords, _globeManager, _globeGenerator, _progressStorageService);

        Debug.Log($"Current: {currentGlobeCell}");
        Debug.Log($"Current: {_globeManager.Globe.HomeProvince}");

        _region = currentRegion;

        // Создание визуализации узлов провинции.
        _locationNodeViewModels = new List <MapLocation>(100);
        foreach (GlobeRegionNode globeRegionNode in _region.Nodes)
        {
            var worldCoords = HexHelper.ConvertToWorld(globeRegionNode.OffsetX + _player.Terrain.Coords.X * 20,
                                                       globeRegionNode.OffsetY + _player.Terrain.Coords.Y * 20);

            var locationObject = _container.InstantiatePrefab(LocationPrefab, transform);
            locationObject.transform.position = new Vector3(worldCoords[0], worldCoords[1], 0);
            var locationViewModel = locationObject.GetComponent <MapLocation>();
            locationViewModel.Node         = globeRegionNode;
            locationViewModel.ParentRegion = _region;
            _locationNodeViewModels.Add(locationViewModel);

            locationViewModel.OnSelect += LocationViewModel_OnSelect;
            locationViewModel.OnHover  += LocationViewModel_OnHover;
        }

        // Создание коннекторов между узлами провинции.
        var openNodeViewModels = new List <MapLocation>(_locationNodeViewModels);

        while (openNodeViewModels.Any())
        {
            var currentNodeViewModel = openNodeViewModels[0];
            openNodeViewModels.Remove(currentNodeViewModel);

            var neighbors          = _region.GetNext(currentNodeViewModel.Node);
            var neighborViewModels = openNodeViewModels.Where(x => neighbors.Contains(x.Node)).ToArray();
            foreach (var neibourNodeViewModel in neighborViewModels)
            {
                CreateConnector(currentNodeViewModel, neibourNodeViewModel);
            }
        }

        // Создание визуализаций соседних провинций
        var currentRegionBorders = _region.RegionNodes.Where(x => x.IsBorder).ToArray();

        // TODO в открытые можно помещать только бордюр для экономии.
        openNodeViewModels = new List <MapLocation>(_locationNodeViewModels);
        var currentBorderNodes = currentRegion.Nodes.OfType <GlobeRegionNode>().Where(x => x.IsBorder).ToArray();

        for (var offsetX = -1; offsetX <= 1; offsetX++)
        {
            for (var offsetY = -1; offsetY <= 1; offsetY++)
            {
                if (offsetX == 0 && offsetY == 0)
                {
                    // Это нулевое смещение от текущего элемента.
                    // Пропускаем, т.к. текущий элемент уже есть.
                    continue;
                }

                var terrainX = _player.Terrain.Coords.X + offsetX;
                var terrainY = _player.Terrain.Coords.Y + offsetY;

                if (_globeManager.Globe.Terrain.GetLowerBound(0) <= terrainX &&
                    terrainX <= _globeManager.Globe.Terrain.GetUpperBound(0) &&
                    _globeManager.Globe.Terrain[0].GetLowerBound(0) <= terrainY &&
                    terrainY <= _globeManager.Globe.Terrain[0].GetUpperBound(0))
                {
                    var terrainCell = _globeManager.Globe.Terrain[terrainX][terrainY];

                    var neighborRegion = _globeManager.Regions[terrainCell];

                    // Ищем узел текущей провинции, являющийся соседним с узлом соседней провинции.
                    var neighborBorderNodes = neighborRegion.Nodes.OfType <GlobeRegionNode>().Where(x => x.IsBorder);

                    foreach (var neighborBorderNode in neighborBorderNodes)
                    {
                        var transitionNodes = RegionTransitionHelper.GetNeighborBorderNodes(neighborBorderNode,
                                                                                            terrainCell,
                                                                                            currentRegionBorders,
                                                                                            _player.Terrain);

                        if (!transitionNodes.Any())
                        {
                            // Этот узел соседней провинции не имеет переходов в текущую.
                            // Соответственно, из текущей провинции никто не будет иметь переходов в этот узел соседней провинции.
                            // Значит его можно вообще не отрисовывать.
                            continue;
                        }

                        var worldCoords = HexHelper.ConvertToWorld(neighborBorderNode.OffsetX + terrainX * 20,
                                                                   neighborBorderNode.OffsetY + terrainY * 20);

                        var locationObject = _container.InstantiatePrefab(LocationPrefab, transform);
                        locationObject.transform.position = new Vector3(worldCoords[0], worldCoords[1], 0);
                        var locationViewModel = locationObject.GetComponent <MapLocation>();
                        locationViewModel.Node         = neighborBorderNode;
                        locationViewModel.ParentRegion = neighborRegion;
                        locationViewModel.OtherRegion  = true;
                        _locationNodeViewModels.Add(locationViewModel);

                        locationViewModel.OnSelect += LocationViewModel_OnSelect;
                        locationViewModel.OnHover  += LocationViewModel_OnHover;

                        // Создаём коннекторы от всех пограничных узлов,
                        // имеющий переходв в текущий узел соседней провинции.
                        var openTransitionNodes = openNodeViewModels.Where(x => transitionNodes.Contains(x.Node));
                        foreach (var openTransitionNode in openTransitionNodes)
                        {
                            CreateConnector(openTransitionNode, locationViewModel);
                        }
                    }
                }
            }
        }

        if (_player.MainPerson == null)
        {
            if (!_progressStorageService.LoadPerson())
            {
                _player.MainPerson = _humanPersonFactory.Create();
            }
        }

        var playerGroupNodeViewModel = _locationNodeViewModels.Single(x => x.Node == _player.GlobeNode);
        var groupObject = _container.InstantiatePrefab(HumanGroupPrefab, transform);

        _groupViewModel = groupObject.GetComponent <GroupVM>();
        _groupViewModel.CurrentLocation = playerGroupNodeViewModel;
        groupObject.transform.position  = playerGroupNodeViewModel.transform.position;
        Camera.Target = groupObject;
        Camera.GetComponent <GlobalFollowCamera>().SetPosition(groupObject.transform);

        var nodes = _locationNodeViewModels.Select(x => x.Node);
        var centerLocationNode          = GlobeHelper.GetCenterLocationNode(nodes);
        var centerLocationNodeViewModel = _locationNodeViewModels
                                          .Single(nodeViewModel => nodeViewModel.Node.OffsetX == centerLocationNode.OffsetX &&
                                                  nodeViewModel.Node.OffsetY == centerLocationNode.OffsetY);

        MapBackground.transform.position = centerLocationNodeViewModel.transform.position;

        _player.GlobeNodeChanged += HumanPlayer_GlobeNodeChanged;
        MoveGroupViewModel(_player.GlobeNode);
    }
        public ActorViewModel(
            IActor actor,
            GameObjectParams gameObjectParams)
        {
            Actor = actor;

            _game = gameObjectParams.Game ??
                    throw new ArgumentException($"{nameof(gameObjectParams.Game)} is not defined.",
                                                nameof(gameObjectParams));
            _sectorViewModelContext = gameObjectParams.SectorViewModelContext ??
                                      throw new ArgumentException(
                                                $"{nameof(gameObjectParams.SectorViewModelContext)} is not defined.",
                                                nameof(gameObjectParams));
            _personSoundStorage = gameObjectParams.PersonSoundStorage ??
                                  throw new ArgumentException(
                                            $"{nameof(gameObjectParams.PersonSoundStorage)} is not defined.",
                                            nameof(gameObjectParams));

            _gameObjectVisualizationContentStorage = gameObjectParams.GameObjectVisualizationContentStorage ??
                                                     throw new ArgumentException(
                                                               $"{nameof(gameObjectParams.GameObjectVisualizationContentStorage)} is not defined.",
                                                               nameof(gameObjectParams));

            _spriteBatch = gameObjectParams.SpriteBatch ??
                           throw new ArgumentException($"{nameof(gameObjectParams.SpriteBatch)} is not defined.",
                                                       nameof(gameObjectParams));

            if (gameObjectParams.PersonVisualizationContentStorage is null)
            {
                throw new ArgumentException(
                          $"{nameof(gameObjectParams.PersonVisualizationContentStorage)} is not defined.",
                          nameof(gameObjectParams));
            }

            var equipmentModule = Actor.Person.GetModuleSafe <IEquipmentModule>();

            var shadowTexture = _game.Content.Load <Texture2D>("Sprites/game-objects/simple-object-shadow");

            _rootSprite   = new SpriteContainer();
            _shadowSprite = new Sprite(shadowTexture)
            {
                Position = new Vector2(0, 0),
                Origin   = new Vector2(0.5f, 0.5f),
                Color    = new Color(Color.White, 0.5f)
            };

            _rootSprite.AddChild(_shadowSprite);

            var isHumanGraphics = Actor.Person is HumanPerson;

            if (isHumanGraphics)
            {
                if (equipmentModule is not null)
                {
                    var graphicsRoot = new HumanoidGraphics(equipmentModule,
                                                            gameObjectParams.PersonVisualizationContentStorage);

                    _rootSprite.AddChild(graphicsRoot);

                    _graphicsRoot = graphicsRoot;
                }
                else
                {
                    // There is no cases when human person hasn't equipment module.
                    // It can be empty module to show "naked" person in the future.
                    throw new InvalidOperationException("Person has no IEquipmentModule.");
                }
            }
            else
            {
                var             monsterPerson = Actor.Person as MonsterPerson;
                SpriteContainer?graphics      = null;
                switch (monsterPerson.Scheme.Sid)
                {
                case "predator":
                    graphics = new AnimalGraphics(gameObjectParams.PersonVisualizationContentStorage);
                    break;

                case "warthog":
                    graphics = new MonoGraphics("warthog", gameObjectParams.PersonVisualizationContentStorage);
                    break;

                case "predator-meat":
                    graphics = new AnimalGraphics(gameObjectParams.PersonVisualizationContentStorage);
                    break;

                case "skeleton":
                    graphics = new MonoGraphics("skeleton", gameObjectParams.PersonVisualizationContentStorage);
                    break;

                case "skeleton-equipment":
                    graphics = new MonoGraphics("skeleton-equipment",
                                                gameObjectParams.PersonVisualizationContentStorage);
                    break;

                case "gallbladder":
                    graphics = new MonoGraphics("gallbladder", gameObjectParams.PersonVisualizationContentStorage);
                    break;
                }

                _rootSprite.AddChild(graphics);

                _graphicsRoot = (IActorGraphics)graphics;
            }

            var hexSize = MapMetrics.UnitSize / 2;
            var playerActorWorldCoords = HexHelper.ConvertToWorld(((HexNode)Actor.Node).OffsetCoords);
            var newPosition            = new Vector2(
                (float)(playerActorWorldCoords[0] * hexSize * Math.Sqrt(3)),
                playerActorWorldCoords[1] * hexSize * 2 / 2
                );

            _rootSprite.Position = newPosition;

            Actor.Moved    += Actor_Moved;
            Actor.UsedProp += Actor_UsedProp;
            Actor.PropTransferPerformed        += Actor_PropTransferPerformed;
            Actor.BeginTransitionToOtherSector += Actor_BeginTransitionToOtherSector;
            if (Actor.Person.HasModule <IEquipmentModule>())
            {
                Actor.Person.GetModule <IEquipmentModule>().EquipmentChanged += PersonEquipmentModule_EquipmentChanged;
            }

            if (Actor.Person.HasModule <ISurvivalModule>())
            {
                Actor.Person.GetModule <ISurvivalModule>().Dead += PersonSurvivalModule_Dead;
            }

            _actorStateEngineList = new List <IActorStateEngine> {
                new ActorIdleEngine(_graphicsRoot.RootSprite)
            };
        }