Nanotrasen Block Game is here (#2131)
* tetris!
* softdropping & left,right key holding
* started work on the ui
* playable state
* there you go exp
* multiuser rework
* ui update refactor
* blockgame™️
* highscores, keybindings, ui refactor
* speed adjusts
leveling
* highscorebackground tweak
speed tweak
* NULLABLE
* yes
This commit is contained in:
691
Content.Client/Arcade/BlockGameMenu.cs
Normal file
691
Content.Client/Arcade/BlockGameMenu.cs
Normal file
@@ -0,0 +1,691 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Mime;
|
||||||
|
using Content.Client.GameObjects.Components.Arcade;
|
||||||
|
using Content.Client.Utility;
|
||||||
|
using Content.Shared.Arcade;
|
||||||
|
using Content.Shared.GameObjects.Components.Arcade;
|
||||||
|
using Content.Shared.Input;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Graphics.Drawing;
|
||||||
|
using Robust.Client.Interfaces.ResourceManagement;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Shared.Input;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Arcade
|
||||||
|
{
|
||||||
|
public class BlockGameMenu : SS14Window
|
||||||
|
{
|
||||||
|
|
||||||
|
private static Color overlayBackgroundColor = new Color(74,74,81,180);
|
||||||
|
private static Color overlayShadowColor = new Color(0,0,0,83);
|
||||||
|
|
||||||
|
private static Vector2 blockSize = new Vector2(15,15);
|
||||||
|
|
||||||
|
private BlockGameBoundUserInterface _owner;
|
||||||
|
|
||||||
|
private PanelContainer _mainPanel;
|
||||||
|
|
||||||
|
private VBoxContainer _gameRootContainer;
|
||||||
|
private GridContainer _gameGrid;
|
||||||
|
private GridContainer _nextBlockGrid;
|
||||||
|
private GridContainer _holdBlockGrid;
|
||||||
|
private Label _pointsLabel;
|
||||||
|
private Label _levelLabel;
|
||||||
|
private Button _pauseButton;
|
||||||
|
|
||||||
|
private PanelContainer _menuRootContainer;
|
||||||
|
private Button _unpauseButton;
|
||||||
|
private Control _unpauseButtonMargin;
|
||||||
|
private Button _newGameButton;
|
||||||
|
private Button _scoreBoardButton;
|
||||||
|
|
||||||
|
private PanelContainer _gameOverRootContainer;
|
||||||
|
private Label _finalScoreLabel;
|
||||||
|
private Button _finalNewGameButton;
|
||||||
|
|
||||||
|
private PanelContainer _highscoresRootContainer;
|
||||||
|
private Label _localHighscoresLabel;
|
||||||
|
private Label _globalHighscoresLabel;
|
||||||
|
private Button _highscoreBackButton;
|
||||||
|
|
||||||
|
private bool _isPlayer = false;
|
||||||
|
private bool _gameOver = false;
|
||||||
|
|
||||||
|
public BlockGameMenu(BlockGameBoundUserInterface owner)
|
||||||
|
{
|
||||||
|
Title = "Nanotrasen Block Game";
|
||||||
|
_owner = owner;
|
||||||
|
|
||||||
|
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||||
|
var backgroundTexture = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||||
|
|
||||||
|
_mainPanel = new PanelContainer();
|
||||||
|
|
||||||
|
SetupGameMenu(backgroundTexture);
|
||||||
|
_mainPanel.AddChild(_gameRootContainer);
|
||||||
|
|
||||||
|
SetupPauseMenu(backgroundTexture);
|
||||||
|
|
||||||
|
SetupGameoverScreen(backgroundTexture);
|
||||||
|
|
||||||
|
SetupHighScoreScreen(backgroundTexture);
|
||||||
|
|
||||||
|
Contents.AddChild(_mainPanel);
|
||||||
|
|
||||||
|
CanKeyboardFocus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void SetupHighScoreScreen(Texture backgroundTexture)
|
||||||
|
{
|
||||||
|
var rootBack = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = backgroundTexture,
|
||||||
|
Modulate = overlayShadowColor
|
||||||
|
};
|
||||||
|
rootBack.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
_highscoresRootContainer = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = rootBack,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
var c = new Color(overlayBackgroundColor.R,overlayBackgroundColor.G,overlayBackgroundColor.B,220);
|
||||||
|
var innerBack = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = backgroundTexture,
|
||||||
|
Modulate = c
|
||||||
|
};
|
||||||
|
innerBack.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
var menuInnerPanel = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = innerBack,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
_highscoresRootContainer.AddChild(menuInnerPanel);
|
||||||
|
|
||||||
|
var menuContainer = new VBoxContainer()
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
menuContainer.AddChild(new Label{Text = "Highscores"});
|
||||||
|
menuContainer.AddChild(new Control{CustomMinimumSize = new Vector2(1,10)});
|
||||||
|
|
||||||
|
var highScoreBox = new HBoxContainer();
|
||||||
|
|
||||||
|
_localHighscoresLabel = new Label
|
||||||
|
{
|
||||||
|
Align = Label.AlignMode.Center
|
||||||
|
};
|
||||||
|
highScoreBox.AddChild(_localHighscoresLabel);
|
||||||
|
highScoreBox.AddChild(new Control{CustomMinimumSize = new Vector2(40,1)});
|
||||||
|
_globalHighscoresLabel = new Label
|
||||||
|
{
|
||||||
|
Align = Label.AlignMode.Center
|
||||||
|
};
|
||||||
|
highScoreBox.AddChild(_globalHighscoresLabel);
|
||||||
|
menuContainer.AddChild(highScoreBox);
|
||||||
|
menuContainer.AddChild(new Control{CustomMinimumSize = new Vector2(1,10)});
|
||||||
|
_highscoreBackButton = new Button
|
||||||
|
{
|
||||||
|
Text = "Back",
|
||||||
|
TextAlign = Label.AlignMode.Center
|
||||||
|
};
|
||||||
|
_highscoreBackButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.Pause);
|
||||||
|
menuContainer.AddChild(_highscoreBackButton);
|
||||||
|
|
||||||
|
menuInnerPanel.AddChild(menuContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupGameoverScreen(Texture backgroundTexture)
|
||||||
|
{
|
||||||
|
var rootBack = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = backgroundTexture,
|
||||||
|
Modulate = overlayShadowColor
|
||||||
|
};
|
||||||
|
rootBack.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
_gameOverRootContainer = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = rootBack,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
var innerBack = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = backgroundTexture,
|
||||||
|
Modulate = overlayBackgroundColor
|
||||||
|
};
|
||||||
|
innerBack.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
var menuInnerPanel = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = innerBack,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
_gameOverRootContainer.AddChild(menuInnerPanel);
|
||||||
|
|
||||||
|
var menuContainer = new VBoxContainer
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
menuContainer.AddChild(new Label{Text = "Gameover!",Align = Label.AlignMode.Center});
|
||||||
|
menuContainer.AddChild(new Control{CustomMinimumSize = new Vector2(1,10)});
|
||||||
|
|
||||||
|
|
||||||
|
_finalScoreLabel = new Label{Align = Label.AlignMode.Center};
|
||||||
|
menuContainer.AddChild(_finalScoreLabel);
|
||||||
|
menuContainer.AddChild(new Control{CustomMinimumSize = new Vector2(1,10)});
|
||||||
|
|
||||||
|
_finalNewGameButton = new Button
|
||||||
|
{
|
||||||
|
Text = "New Game",
|
||||||
|
TextAlign = Label.AlignMode.Center
|
||||||
|
};
|
||||||
|
_finalNewGameButton.OnPressed += (e) =>
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.NewGame);
|
||||||
|
};
|
||||||
|
menuContainer.AddChild(_finalNewGameButton);
|
||||||
|
|
||||||
|
menuInnerPanel.AddChild(menuContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupPauseMenu(Texture backgroundTexture)
|
||||||
|
{
|
||||||
|
var rootBack = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = backgroundTexture,
|
||||||
|
Modulate = overlayShadowColor
|
||||||
|
};
|
||||||
|
rootBack.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
_menuRootContainer = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = rootBack,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
var innerBack = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = backgroundTexture,
|
||||||
|
Modulate = overlayBackgroundColor
|
||||||
|
};
|
||||||
|
innerBack.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
var menuInnerPanel = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = innerBack,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
_menuRootContainer.AddChild(menuInnerPanel);
|
||||||
|
|
||||||
|
|
||||||
|
var menuContainer = new VBoxContainer
|
||||||
|
{
|
||||||
|
SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
|
||||||
|
SizeFlagsVertical = SizeFlags.ShrinkCenter
|
||||||
|
};
|
||||||
|
|
||||||
|
_newGameButton = new Button
|
||||||
|
{
|
||||||
|
Text = "New Game",
|
||||||
|
TextAlign = Label.AlignMode.Center
|
||||||
|
};
|
||||||
|
_newGameButton.OnPressed += (e) =>
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.NewGame);
|
||||||
|
};
|
||||||
|
menuContainer.AddChild(_newGameButton);
|
||||||
|
menuContainer.AddChild(new Control{CustomMinimumSize = new Vector2(1,10)});
|
||||||
|
|
||||||
|
_scoreBoardButton = new Button
|
||||||
|
{
|
||||||
|
Text = "Scoreboard",
|
||||||
|
TextAlign = Label.AlignMode.Center
|
||||||
|
};
|
||||||
|
_scoreBoardButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.ShowHighscores);
|
||||||
|
menuContainer.AddChild(_scoreBoardButton);
|
||||||
|
_unpauseButtonMargin = new Control {CustomMinimumSize = new Vector2(1, 10), Visible = false};
|
||||||
|
menuContainer.AddChild(_unpauseButtonMargin);
|
||||||
|
|
||||||
|
_unpauseButton = new Button
|
||||||
|
{
|
||||||
|
Text = "Unpause",
|
||||||
|
TextAlign = Label.AlignMode.Center,
|
||||||
|
Visible = false
|
||||||
|
};
|
||||||
|
_unpauseButton.OnPressed += (e) =>
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.Unpause);
|
||||||
|
};
|
||||||
|
menuContainer.AddChild(_unpauseButton);
|
||||||
|
|
||||||
|
menuInnerPanel.AddChild(menuContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetUsability(bool isPlayer)
|
||||||
|
{
|
||||||
|
_isPlayer = isPlayer;
|
||||||
|
UpdateUsability();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUsability()
|
||||||
|
{
|
||||||
|
_pauseButton.Disabled = !_isPlayer;
|
||||||
|
_newGameButton.Disabled = !_isPlayer;
|
||||||
|
_scoreBoardButton.Disabled = !_isPlayer;
|
||||||
|
_unpauseButton.Disabled = !_isPlayer;
|
||||||
|
_finalNewGameButton.Disabled = !_isPlayer;
|
||||||
|
_highscoreBackButton.Disabled = !_isPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupGameMenu(Texture backgroundTexture)
|
||||||
|
{
|
||||||
|
// building the game container
|
||||||
|
_gameRootContainer = new VBoxContainer();
|
||||||
|
|
||||||
|
_levelLabel = new Label
|
||||||
|
{
|
||||||
|
Align = Label.AlignMode.Center,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand
|
||||||
|
};
|
||||||
|
_gameRootContainer.AddChild(_levelLabel);
|
||||||
|
_gameRootContainer.AddChild(new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = new Vector2(1,5)
|
||||||
|
});
|
||||||
|
|
||||||
|
_pointsLabel = new Label
|
||||||
|
{
|
||||||
|
Align = Label.AlignMode.Center,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand
|
||||||
|
};
|
||||||
|
_gameRootContainer.AddChild(_pointsLabel);
|
||||||
|
_gameRootContainer.AddChild(new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = new Vector2(1,10)
|
||||||
|
});
|
||||||
|
|
||||||
|
var gameBox = new HBoxContainer();
|
||||||
|
gameBox.AddChild(SetupHoldBox(backgroundTexture));
|
||||||
|
gameBox.AddChild(new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = new Vector2(10,1)
|
||||||
|
});
|
||||||
|
gameBox.AddChild(SetupGameGrid(backgroundTexture));
|
||||||
|
gameBox.AddChild(new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = new Vector2(10,1)
|
||||||
|
});
|
||||||
|
gameBox.AddChild(SetupNextBox(backgroundTexture));
|
||||||
|
|
||||||
|
_gameRootContainer.AddChild(gameBox);
|
||||||
|
|
||||||
|
_gameRootContainer.AddChild(new Control
|
||||||
|
{
|
||||||
|
CustomMinimumSize = new Vector2(1,10)
|
||||||
|
});
|
||||||
|
|
||||||
|
_pauseButton = new Button
|
||||||
|
{
|
||||||
|
Text = "Pause",
|
||||||
|
TextAlign = Label.AlignMode.Center
|
||||||
|
};
|
||||||
|
_pauseButton.OnPressed += (e) => TryPause();
|
||||||
|
_gameRootContainer.AddChild(_pauseButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Control SetupGameGrid(Texture panelTex)
|
||||||
|
{
|
||||||
|
_gameGrid = new GridContainer
|
||||||
|
{
|
||||||
|
Columns = 10,
|
||||||
|
HSeparationOverride = 1,
|
||||||
|
VSeparationOverride = 1
|
||||||
|
};
|
||||||
|
UpdateBlocks(new BlockGameBlock[0]);
|
||||||
|
|
||||||
|
var back = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = panelTex,
|
||||||
|
Modulate = Color.FromHex("#4a4a51"),
|
||||||
|
};
|
||||||
|
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
|
||||||
|
var gamePanel = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = back,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 60
|
||||||
|
};
|
||||||
|
var backgroundPanel = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = new StyleBoxFlat{BackgroundColor = Color.FromHex("#86868d")}
|
||||||
|
};
|
||||||
|
backgroundPanel.AddChild(_gameGrid);
|
||||||
|
gamePanel.AddChild(backgroundPanel);
|
||||||
|
return gamePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Control SetupNextBox(Texture panelTex)
|
||||||
|
{
|
||||||
|
var previewBack = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = panelTex,
|
||||||
|
Modulate = Color.FromHex("#4a4a51")
|
||||||
|
};
|
||||||
|
previewBack.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
|
||||||
|
var grid = new GridContainer
|
||||||
|
{
|
||||||
|
Columns = 1,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 20
|
||||||
|
};
|
||||||
|
|
||||||
|
var nextBlockPanel = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = previewBack,
|
||||||
|
CustomMinimumSize = blockSize * 6.5f,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.None,
|
||||||
|
SizeFlagsVertical = SizeFlags.None
|
||||||
|
};
|
||||||
|
var nextCenterContainer = new CenterContainer();
|
||||||
|
_nextBlockGrid = new GridContainer
|
||||||
|
{
|
||||||
|
HSeparationOverride = 1,
|
||||||
|
VSeparationOverride = 1
|
||||||
|
};
|
||||||
|
nextCenterContainer.AddChild(_nextBlockGrid);
|
||||||
|
nextBlockPanel.AddChild(nextCenterContainer);
|
||||||
|
grid.AddChild(nextBlockPanel);
|
||||||
|
|
||||||
|
grid.AddChild(new Label{Text = "Next", Align = Label.AlignMode.Center});
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Control SetupHoldBox(Texture panelTex)
|
||||||
|
{
|
||||||
|
var previewBack = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = panelTex,
|
||||||
|
Modulate = Color.FromHex("#4a4a51")
|
||||||
|
};
|
||||||
|
previewBack.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
|
||||||
|
var grid = new GridContainer
|
||||||
|
{
|
||||||
|
Columns = 1,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||||
|
SizeFlagsStretchRatio = 20
|
||||||
|
};
|
||||||
|
|
||||||
|
var holdBlockPanel = new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = previewBack,
|
||||||
|
CustomMinimumSize = blockSize * 6.5f,
|
||||||
|
SizeFlagsHorizontal = SizeFlags.None,
|
||||||
|
SizeFlagsVertical = SizeFlags.None
|
||||||
|
};
|
||||||
|
var holdCenterContainer = new CenterContainer();
|
||||||
|
_holdBlockGrid = new GridContainer
|
||||||
|
{
|
||||||
|
HSeparationOverride = 1,
|
||||||
|
VSeparationOverride = 1
|
||||||
|
};
|
||||||
|
holdCenterContainer.AddChild(_holdBlockGrid);
|
||||||
|
holdBlockPanel.AddChild(holdCenterContainer);
|
||||||
|
grid.AddChild(holdBlockPanel);
|
||||||
|
|
||||||
|
grid.AddChild(new Label{Text = "Hold", Align = Label.AlignMode.Center});
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FocusExited()
|
||||||
|
{
|
||||||
|
if (!IsOpen) return;
|
||||||
|
if(_gameOver) return;
|
||||||
|
TryPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryPause()
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.Pause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetStarted()
|
||||||
|
{
|
||||||
|
_gameOver = false;
|
||||||
|
_unpauseButton.Visible = true;
|
||||||
|
_unpauseButtonMargin.Visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetScreen(BlockGameMessages.BlockGameScreen screen)
|
||||||
|
{
|
||||||
|
if (_gameOver) return;
|
||||||
|
|
||||||
|
switch (screen)
|
||||||
|
{
|
||||||
|
case BlockGameMessages.BlockGameScreen.Game:
|
||||||
|
GrabKeyboardFocus();
|
||||||
|
CloseMenus();
|
||||||
|
_pauseButton.Disabled = !_isPlayer;
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameScreen.Pause:
|
||||||
|
//ReleaseKeyboardFocus();
|
||||||
|
CloseMenus();
|
||||||
|
_mainPanel.AddChild(_menuRootContainer);
|
||||||
|
_pauseButton.Disabled = true;
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameScreen.Gameover:
|
||||||
|
_gameOver = true;
|
||||||
|
_pauseButton.Disabled = true;
|
||||||
|
//ReleaseKeyboardFocus();
|
||||||
|
CloseMenus();
|
||||||
|
_mainPanel.AddChild(_gameOverRootContainer);
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameScreen.Highscores:
|
||||||
|
//ReleaseKeyboardFocus();
|
||||||
|
CloseMenus();
|
||||||
|
_mainPanel.AddChild(_highscoresRootContainer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CloseMenus()
|
||||||
|
{
|
||||||
|
if(_mainPanel.Children.Contains(_menuRootContainer)) _mainPanel.RemoveChild(_menuRootContainer);
|
||||||
|
if(_mainPanel.Children.Contains(_gameOverRootContainer)) _mainPanel.RemoveChild(_gameOverRootContainer);
|
||||||
|
if(_mainPanel.Children.Contains(_highscoresRootContainer)) _mainPanel.RemoveChild(_highscoresRootContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetGameoverInfo(int amount, int? localPlacement, int? globalPlacement)
|
||||||
|
{
|
||||||
|
var globalPlacementText = globalPlacement == null ? "-" : $"#{globalPlacement}";
|
||||||
|
var localPlacementText = localPlacement == null ? "-" : $"#{localPlacement}";
|
||||||
|
_finalScoreLabel.Text = $"Global: {globalPlacementText}\nLocal: {localPlacementText}\nPoints: {amount}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePoints(int points)
|
||||||
|
{
|
||||||
|
_pointsLabel.Text = $"Points: {points}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateLevel(int level)
|
||||||
|
{
|
||||||
|
_levelLabel.Text = $"Level {level + 1}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateHighscores(List<BlockGameMessages.HighScoreEntry> localHighscores,
|
||||||
|
List<BlockGameMessages.HighScoreEntry> globalHighscores)
|
||||||
|
{
|
||||||
|
var localHighscoreText = "Station:\n";
|
||||||
|
var globalHighscoreText = "Nanotrasen:\n";
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
localHighscoreText += $"#{i + 1} " + (localHighscores.Count > i
|
||||||
|
? $"{localHighscores[i].Name} - {localHighscores[i].Score}\n" : "??? - 0\n");
|
||||||
|
globalHighscoreText += $"#{i + 1} " + (globalHighscores.Count > i
|
||||||
|
? $"{globalHighscores[i].Name} - {globalHighscores[i].Score}\n" : "??? - 0\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
_localHighscoresLabel.Text = localHighscoreText;
|
||||||
|
_globalHighscoresLabel.Text = globalHighscoreText;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
if(!_isPlayer) return;
|
||||||
|
|
||||||
|
if (args.Function == ContentKeyFunctions.ArcadeLeft)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.StartLeft);
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.ArcadeRight)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.StartRight);
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.ArcadeUp)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.Rotate);
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.Arcade3)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.CounterRotate);
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.ArcadeDown)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.SoftdropStart);
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.Arcade2)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.Hold);
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.Arcade1)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.Harddrop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
if(!_isPlayer) return;
|
||||||
|
|
||||||
|
if (args.Function == ContentKeyFunctions.ArcadeLeft)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.EndLeft);
|
||||||
|
}
|
||||||
|
else if (args.Function == ContentKeyFunctions.ArcadeRight)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.EndRight);
|
||||||
|
}else if (args.Function == ContentKeyFunctions.ArcadeDown)
|
||||||
|
{
|
||||||
|
_owner.SendAction(BlockGamePlayerAction.SoftdropEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateNextBlock(BlockGameBlock[] blocks)
|
||||||
|
{
|
||||||
|
_nextBlockGrid.RemoveAllChildren();
|
||||||
|
if (blocks.Length == 0) return;
|
||||||
|
var columnCount = blocks.Max(b => b.Position.X) + 1;
|
||||||
|
var rowCount = blocks.Max(b => b.Position.Y) + 1;
|
||||||
|
_nextBlockGrid.Columns = columnCount;
|
||||||
|
for (int y = 0; y < rowCount; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < columnCount; x++)
|
||||||
|
{
|
||||||
|
var c = GetColorForPosition(blocks, x, y);
|
||||||
|
_nextBlockGrid.AddChild(new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = new StyleBoxFlat {BackgroundColor = c},
|
||||||
|
CustomMinimumSize = blockSize,
|
||||||
|
RectDrawClipMargin = 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateHeldBlock(BlockGameBlock[] blocks)
|
||||||
|
{
|
||||||
|
_holdBlockGrid.RemoveAllChildren();
|
||||||
|
if (blocks.Length == 0) return;
|
||||||
|
var columnCount = blocks.Max(b => b.Position.X) + 1;
|
||||||
|
var rowCount = blocks.Max(b => b.Position.Y) + 1;
|
||||||
|
_holdBlockGrid.Columns = columnCount;
|
||||||
|
for (int y = 0; y < rowCount; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < columnCount; x++)
|
||||||
|
{
|
||||||
|
var c = GetColorForPosition(blocks, x, y);
|
||||||
|
_holdBlockGrid.AddChild(new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = new StyleBoxFlat {BackgroundColor = c},
|
||||||
|
CustomMinimumSize = blockSize,
|
||||||
|
RectDrawClipMargin = 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateBlocks(BlockGameBlock[] blocks)
|
||||||
|
{
|
||||||
|
_gameGrid.RemoveAllChildren();
|
||||||
|
for (int y = 0; y < 20; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 10; x++)
|
||||||
|
{
|
||||||
|
var c = GetColorForPosition(blocks, x, y);
|
||||||
|
_gameGrid.AddChild(new PanelContainer
|
||||||
|
{
|
||||||
|
PanelOverride = new StyleBoxFlat {BackgroundColor = c},
|
||||||
|
CustomMinimumSize = blockSize,
|
||||||
|
RectDrawClipMargin = 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color GetColorForPosition(BlockGameBlock[] blocks, int x, int y)
|
||||||
|
{
|
||||||
|
Color c = Color.Transparent;
|
||||||
|
var matchingBlock = blocks.FirstOrNull(b => b.Position.X == x && b.Position.Y == y);
|
||||||
|
if (matchingBlock.HasValue)
|
||||||
|
{
|
||||||
|
c = matchingBlock.Value.GameBlockColor switch
|
||||||
|
{
|
||||||
|
BlockGameBlock.BlockGameBlockColor.Red => Color.Red,
|
||||||
|
BlockGameBlock.BlockGameBlockColor.Orange => Color.Orange,
|
||||||
|
BlockGameBlock.BlockGameBlockColor.Yellow => Color.Yellow,
|
||||||
|
BlockGameBlock.BlockGameBlockColor.Green => Color.LimeGreen,
|
||||||
|
BlockGameBlock.BlockGameBlockColor.Blue => Color.Blue,
|
||||||
|
BlockGameBlock.BlockGameBlockColor.Purple => Color.Purple,
|
||||||
|
BlockGameBlock.BlockGameBlockColor.LightBlue => Color.LightBlue,
|
||||||
|
_ => Color.Olive //olive is error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Client.Arcade;
|
||||||
|
using Content.Shared.Arcade;
|
||||||
|
using Content.Shared.GameObjects.Components.Arcade;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Arcade
|
||||||
|
{
|
||||||
|
public class BlockGameBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
private BlockGameMenu _menu;
|
||||||
|
|
||||||
|
public BlockGameBoundUserInterface([NotNull] ClientUserInterfaceComponent owner, [NotNull] object uiKey) : base(owner, uiKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
|
_menu = new BlockGameMenu(this);
|
||||||
|
_menu.OnClose += () => SendMessage(new BlockGameMessages.BlockGameUserUnregisterMessage());
|
||||||
|
_menu.OnClose += Close;
|
||||||
|
_menu.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||||
|
{
|
||||||
|
switch (message)
|
||||||
|
{
|
||||||
|
case BlockGameMessages.BlockGameVisualUpdateMessage updateMessage:
|
||||||
|
switch (updateMessage.GameVisualType)
|
||||||
|
{
|
||||||
|
case BlockGameMessages.BlockGameVisualType.GameField:
|
||||||
|
_menu?.UpdateBlocks(updateMessage.Blocks);
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameVisualType.HoldBlock:
|
||||||
|
_menu?.UpdateHeldBlock(updateMessage.Blocks);
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameVisualType.NextBlock:
|
||||||
|
_menu?.UpdateNextBlock(updateMessage.Blocks);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameScoreUpdateMessage scoreUpdate:
|
||||||
|
_menu?.UpdatePoints(scoreUpdate.Points);
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameUserStatusMessage userMessage:
|
||||||
|
_menu?.SetUsability(userMessage.IsPlayer);
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameSetScreenMessage statusMessage:
|
||||||
|
if (statusMessage.isStarted) _menu?.SetStarted();
|
||||||
|
_menu?.SetScreen(statusMessage.Screen);
|
||||||
|
if (statusMessage is BlockGameMessages.BlockGameGameOverScreenMessage gameOverScreenMessage)
|
||||||
|
_menu?.SetGameoverInfo(gameOverScreenMessage.FinalScore, gameOverScreenMessage.LocalPlacement, gameOverScreenMessage.GlobalPlacement);
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameHighScoreUpdateMessage highScoreUpdateMessage:
|
||||||
|
_menu?.UpdateHighscores(highScoreUpdateMessage.LocalHighscores,
|
||||||
|
highScoreUpdateMessage.GlobalHighscores);
|
||||||
|
break;
|
||||||
|
case BlockGameMessages.BlockGameLevelUpdateMessage levelUpdateMessage:
|
||||||
|
_menu?.UpdateLevel(levelUpdateMessage.Level);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendAction(BlockGamePlayerAction action)
|
||||||
|
{
|
||||||
|
SendMessage(new BlockGameMessages.BlockGamePlayerActionMessage(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if(!disposing) { return; }
|
||||||
|
_menu?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -174,6 +174,7 @@
|
|||||||
"Flammable",
|
"Flammable",
|
||||||
"CreamPie",
|
"CreamPie",
|
||||||
"CreamPied",
|
"CreamPied",
|
||||||
|
"BlockGameArcade"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,13 @@ namespace Content.Client.Input
|
|||||||
human.AddFunction(ContentKeyFunctions.MouseMiddle);
|
human.AddFunction(ContentKeyFunctions.MouseMiddle);
|
||||||
human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
|
human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
|
||||||
human.AddFunction(ContentKeyFunctions.WideAttack);
|
human.AddFunction(ContentKeyFunctions.WideAttack);
|
||||||
|
human.AddFunction(ContentKeyFunctions.ArcadeUp);
|
||||||
|
human.AddFunction(ContentKeyFunctions.ArcadeDown);
|
||||||
|
human.AddFunction(ContentKeyFunctions.ArcadeLeft);
|
||||||
|
human.AddFunction(ContentKeyFunctions.ArcadeRight);
|
||||||
|
human.AddFunction(ContentKeyFunctions.Arcade1);
|
||||||
|
human.AddFunction(ContentKeyFunctions.Arcade2);
|
||||||
|
human.AddFunction(ContentKeyFunctions.Arcade3);
|
||||||
|
|
||||||
var ghost = contexts.New("ghost", "common");
|
var ghost = contexts.New("ghost", "common");
|
||||||
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
||||||
|
|||||||
@@ -0,0 +1,859 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Server.Utility;
|
||||||
|
using Content.Shared.Arcade;
|
||||||
|
using Content.Shared.GameObjects;
|
||||||
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
|
using Content.Shared.Interfaces.GameObjects.Components;
|
||||||
|
using Robust.Server.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Server.Interfaces.GameObjects;
|
||||||
|
using Robust.Server.Interfaces.Player;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Interfaces.Random;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.Components.Arcade
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
[ComponentReference(typeof(IActivate))]
|
||||||
|
public class BlockGameArcadeComponent : Component, IActivate
|
||||||
|
{
|
||||||
|
[Dependency] private IRobustRandom _random = null!;
|
||||||
|
|
||||||
|
public override string Name => "BlockGameArcade";
|
||||||
|
public override uint? NetID => ContentNetIDs.BLOCKGAME_ARCADE;
|
||||||
|
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||||
|
private BoundUserInterface? UserInterface => Owner.GetUIOrNull(BlockGameUiKey.Key);
|
||||||
|
|
||||||
|
private BlockGame _game = null!;
|
||||||
|
|
||||||
|
private IPlayerSession? _player;
|
||||||
|
private List<IPlayerSession> _spectators = new List<IPlayerSession>();
|
||||||
|
|
||||||
|
public void Activate(ActivateEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
if(!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Powered)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!ActionBlockerSystem.CanInteract(Owner)) return;
|
||||||
|
|
||||||
|
UserInterface?.Toggle(actor.playerSession);
|
||||||
|
RegisterPlayerSession(actor.playerSession);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterPlayerSession(IPlayerSession session)
|
||||||
|
{
|
||||||
|
if (_player == null) _player = session;
|
||||||
|
else _spectators.Add(session);
|
||||||
|
|
||||||
|
UpdatePlayerStatus(session);
|
||||||
|
_game.UpdateNewPlayerUI(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeactivePlayer(IPlayerSession session)
|
||||||
|
{
|
||||||
|
if (_player != session) return;
|
||||||
|
|
||||||
|
var temp = _player;
|
||||||
|
_player = null;
|
||||||
|
if (_spectators.Count != 0)
|
||||||
|
{
|
||||||
|
_player = _spectators[0];
|
||||||
|
_spectators.Remove(_player);
|
||||||
|
UpdatePlayerStatus(_player);
|
||||||
|
}
|
||||||
|
_spectators.Add(temp);
|
||||||
|
|
||||||
|
UpdatePlayerStatus(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnRegisterPlayerSession(IPlayerSession session)
|
||||||
|
{
|
||||||
|
if (_player == session)
|
||||||
|
{
|
||||||
|
DeactivePlayer(_player);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_spectators.Remove(session);
|
||||||
|
UpdatePlayerStatus(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePlayerStatus(IPlayerSession session)
|
||||||
|
{
|
||||||
|
UserInterface?.SendMessage(new BlockGameMessages.BlockGameUserStatusMessage(_player == session), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
if (UserInterface != null)
|
||||||
|
{
|
||||||
|
UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||||
|
}
|
||||||
|
_game = new BlockGame(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||||
|
{
|
||||||
|
if (obj.Message is BlockGameMessages.BlockGameUserUnregisterMessage unregisterMessage)
|
||||||
|
{
|
||||||
|
UnRegisterPlayerSession(obj.Session);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (obj.Session != _player) return;
|
||||||
|
|
||||||
|
if (!ActionBlockerSystem.CanInteract(Owner))
|
||||||
|
{
|
||||||
|
DeactivePlayer(obj.Session);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(obj.Message is BlockGameMessages.BlockGamePlayerActionMessage message)) return;
|
||||||
|
if (message.PlayerAction == BlockGamePlayerAction.NewGame)
|
||||||
|
{
|
||||||
|
if(_game.Started) _game = new BlockGame(this);
|
||||||
|
_game.StartGame();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_game.ProcessInput(message.PlayerAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoGameTick(float frameTime)
|
||||||
|
{
|
||||||
|
_game.GameTick(frameTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BlockGame
|
||||||
|
{
|
||||||
|
//note: field is 10(0 -> 9) wide and 20(0 -> 19) high
|
||||||
|
|
||||||
|
private BlockGameArcadeComponent _component;
|
||||||
|
|
||||||
|
private List<BlockGameBlock> _field = new List<BlockGameBlock>();
|
||||||
|
|
||||||
|
private BlockGamePiece _currentPiece;
|
||||||
|
|
||||||
|
private BlockGamePiece _nextPiece
|
||||||
|
{
|
||||||
|
get => _internalNextPiece;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_internalNextPiece = value;
|
||||||
|
SendNextPieceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private BlockGamePiece _internalNextPiece;
|
||||||
|
|
||||||
|
private void SendNextPieceUpdate()
|
||||||
|
{
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_nextPiece.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.NextBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendNextPieceUpdate(IPlayerSession session)
|
||||||
|
{
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_nextPiece.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.NextBlock), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _holdBlock = false;
|
||||||
|
private BlockGamePiece? _heldPiece
|
||||||
|
{
|
||||||
|
get => _internalHeldPiece;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_internalHeldPiece = value;
|
||||||
|
SendHoldPieceUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockGamePiece? _internalHeldPiece = null;
|
||||||
|
|
||||||
|
private void SendHoldPieceUpdate()
|
||||||
|
{
|
||||||
|
if(_heldPiece.HasValue) _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_heldPiece.Value.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.HoldBlock));
|
||||||
|
else _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(new BlockGameBlock[0], BlockGameMessages.BlockGameVisualType.HoldBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendHoldPieceUpdate(IPlayerSession session)
|
||||||
|
{
|
||||||
|
if(_heldPiece.HasValue) _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_heldPiece.Value.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.HoldBlock), session);
|
||||||
|
else _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(new BlockGameBlock[0], BlockGameMessages.BlockGameVisualType.HoldBlock), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2i _currentPiecePosition;
|
||||||
|
private BlockGamePieceRotation _currentRotation;
|
||||||
|
private float _softDropOverride = 0.1f;
|
||||||
|
|
||||||
|
private float Speed => !_softDropPressed
|
||||||
|
? -0.03f * Level + 1
|
||||||
|
: _softDropOverride;
|
||||||
|
|
||||||
|
private float _pressCheckSpeed = 0.08f;
|
||||||
|
|
||||||
|
private bool _running;
|
||||||
|
public bool Paused => !(_running && _started);
|
||||||
|
private bool _started;
|
||||||
|
public bool Started => _started;
|
||||||
|
private bool _gameOver;
|
||||||
|
|
||||||
|
private bool _leftPressed;
|
||||||
|
private bool _rightPressed;
|
||||||
|
private bool _softDropPressed;
|
||||||
|
|
||||||
|
private int Points
|
||||||
|
{
|
||||||
|
get => _internalPoints;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_internalPoints == value) return;
|
||||||
|
_internalPoints = value;
|
||||||
|
SendPointsUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private int _internalPoints;
|
||||||
|
|
||||||
|
private BlockGameSystem.HighScorePlacement? _highScorePlacement = null;
|
||||||
|
|
||||||
|
private void SendPointsUpdate()
|
||||||
|
{
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameScoreUpdateMessage(Points));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendPointsUpdate(IPlayerSession session)
|
||||||
|
{
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameScoreUpdateMessage(Points));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Level
|
||||||
|
{
|
||||||
|
get => _level;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_level = value;
|
||||||
|
SendLevelUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private int _level = 0;
|
||||||
|
private void SendLevelUpdate()
|
||||||
|
{
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameLevelUpdateMessage(Level));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendLevelUpdate(IPlayerSession session)
|
||||||
|
{
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameLevelUpdateMessage(Level));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ClearedLines
|
||||||
|
{
|
||||||
|
get => _clearedLines;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_clearedLines = value;
|
||||||
|
|
||||||
|
if (_clearedLines < LevelRequirement) return;
|
||||||
|
|
||||||
|
_clearedLines -= LevelRequirement;
|
||||||
|
Level++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _clearedLines = 0;
|
||||||
|
private int LevelRequirement => Math.Min(100, Math.Max(Level * 10 - 50, 10));
|
||||||
|
|
||||||
|
public BlockGame(BlockGameArcadeComponent component)
|
||||||
|
{
|
||||||
|
_component = component;
|
||||||
|
_internalNextPiece = BlockGamePiece.GetRandom(_component._random);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendHighscoreUpdate()
|
||||||
|
{
|
||||||
|
var entitySystem = EntitySystem.Get<BlockGameSystem>();
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameHighScoreUpdateMessage(entitySystem.GetLocalHighscores(), entitySystem.GetGlobalHighscores()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendHighscoreUpdate(IPlayerSession session)
|
||||||
|
{
|
||||||
|
var entitySystem = EntitySystem.Get<BlockGameSystem>();
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameHighScoreUpdateMessage(entitySystem.GetLocalHighscores(), entitySystem.GetGlobalHighscores()), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartGame()
|
||||||
|
{
|
||||||
|
InitializeNewBlock();
|
||||||
|
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game));
|
||||||
|
|
||||||
|
FullUpdate();
|
||||||
|
|
||||||
|
_running = true;
|
||||||
|
_started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FullUpdate()
|
||||||
|
{
|
||||||
|
UpdateAllFieldUI();
|
||||||
|
SendHoldPieceUpdate();
|
||||||
|
SendNextPieceUpdate();
|
||||||
|
SendPointsUpdate();
|
||||||
|
SendHighscoreUpdate();
|
||||||
|
SendLevelUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FullUpdate(IPlayerSession session)
|
||||||
|
{
|
||||||
|
UpdateFieldUI(session);
|
||||||
|
SendPointsUpdate(session);
|
||||||
|
SendNextPieceUpdate(session);
|
||||||
|
SendHoldPieceUpdate(session);
|
||||||
|
SendHighscoreUpdate(session);
|
||||||
|
SendLevelUpdate(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GameTick(float frameTime)
|
||||||
|
{
|
||||||
|
if (!_running) return;
|
||||||
|
|
||||||
|
InputTick(frameTime);
|
||||||
|
|
||||||
|
FieldTick(frameTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float _accumulatedLeftPressTime;
|
||||||
|
private float _accumulatedRightPressTime;
|
||||||
|
private void InputTick(float frameTime)
|
||||||
|
{
|
||||||
|
bool anythingChanged = false;
|
||||||
|
if (_leftPressed)
|
||||||
|
{
|
||||||
|
_accumulatedLeftPressTime += frameTime;
|
||||||
|
|
||||||
|
if (_accumulatedLeftPressTime >= _pressCheckSpeed)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_currentPiece.Positions(_currentPiecePosition.AddToX(-1), _currentRotation)
|
||||||
|
.All(MoveCheck))
|
||||||
|
{
|
||||||
|
_currentPiecePosition = _currentPiecePosition.AddToX(-1);
|
||||||
|
anythingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_accumulatedLeftPressTime -= _pressCheckSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_rightPressed)
|
||||||
|
{
|
||||||
|
_accumulatedRightPressTime += frameTime;
|
||||||
|
|
||||||
|
if (_accumulatedRightPressTime >= _pressCheckSpeed)
|
||||||
|
{
|
||||||
|
if (_currentPiece.Positions(_currentPiecePosition.AddToX(1), _currentRotation)
|
||||||
|
.All(MoveCheck))
|
||||||
|
{
|
||||||
|
_currentPiecePosition = _currentPiecePosition.AddToX(1);
|
||||||
|
anythingChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_accumulatedRightPressTime -= _pressCheckSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(anythingChanged) UpdateAllFieldUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private float _accumulatedFieldFrameTime;
|
||||||
|
private void FieldTick(float frameTime)
|
||||||
|
{
|
||||||
|
_accumulatedFieldFrameTime += frameTime;
|
||||||
|
|
||||||
|
var checkTime = Speed;
|
||||||
|
|
||||||
|
if (_accumulatedFieldFrameTime < checkTime) return;
|
||||||
|
|
||||||
|
if(_softDropPressed) AddPoints(1);
|
||||||
|
|
||||||
|
InternalFieldTick();
|
||||||
|
|
||||||
|
_accumulatedFieldFrameTime -= checkTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InternalFieldTick()
|
||||||
|
{
|
||||||
|
if (_currentPiece.Positions(_currentPiecePosition.AddToY(1), _currentRotation)
|
||||||
|
.All(DropCheck))
|
||||||
|
{
|
||||||
|
_currentPiecePosition = _currentPiecePosition.AddToY(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var blocks = _currentPiece.Blocks(_currentPiecePosition, _currentRotation);
|
||||||
|
_field.AddRange(blocks);
|
||||||
|
|
||||||
|
//check loose conditions
|
||||||
|
if (IsGameOver)
|
||||||
|
{
|
||||||
|
InvokeGameover();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeNewBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckField();
|
||||||
|
|
||||||
|
UpdateAllFieldUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckField()
|
||||||
|
{
|
||||||
|
int pointsToAdd = 0;
|
||||||
|
int consecutiveLines = 0;
|
||||||
|
int clearedLines = 0;
|
||||||
|
for (int y = 0; y < 20; y++)
|
||||||
|
{
|
||||||
|
if (CheckLine(y))
|
||||||
|
{
|
||||||
|
//line was cleared
|
||||||
|
y--;
|
||||||
|
consecutiveLines++;
|
||||||
|
clearedLines++;
|
||||||
|
}
|
||||||
|
else if(consecutiveLines != 0)
|
||||||
|
{
|
||||||
|
var mod = consecutiveLines switch
|
||||||
|
{
|
||||||
|
1 => 40,
|
||||||
|
2 => 100,
|
||||||
|
3 => 300,
|
||||||
|
4 => 1200,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
pointsToAdd += mod * (_level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearedLines += clearedLines;
|
||||||
|
AddPoints(pointsToAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CheckLine(int y)
|
||||||
|
{
|
||||||
|
for (var x = 0; x < 10; x++)
|
||||||
|
{
|
||||||
|
if (!_field.Any(b => b.Position.X == x && b.Position.Y == y)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//clear line
|
||||||
|
_field.RemoveAll(b => b.Position.Y == y);
|
||||||
|
//move everything down
|
||||||
|
FillLine(y);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddPoints(int amount)
|
||||||
|
{
|
||||||
|
if (amount == 0) return;
|
||||||
|
|
||||||
|
Points += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillLine(int y)
|
||||||
|
{
|
||||||
|
for (int c_y = y; c_y > 0; c_y--)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < _field.Count; j++)
|
||||||
|
{
|
||||||
|
if(_field[j].Position.Y != c_y-1) continue;
|
||||||
|
|
||||||
|
_field[j] = new BlockGameBlock(_field[j].Position.AddToY(1), _field[j].GameBlockColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeNewBlock()
|
||||||
|
{
|
||||||
|
InitializeNewBlock(_nextPiece);
|
||||||
|
_nextPiece = BlockGamePiece.GetRandom(_component._random);
|
||||||
|
_holdBlock = false;
|
||||||
|
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_nextPiece.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.NextBlock));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeNewBlock(BlockGamePiece piece)
|
||||||
|
{
|
||||||
|
_currentPiecePosition = new Vector2i(5,0);
|
||||||
|
|
||||||
|
_currentRotation = BlockGamePieceRotation.North;
|
||||||
|
|
||||||
|
_currentPiece = piece;
|
||||||
|
UpdateAllFieldUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool LowerBoundCheck(Vector2i position) => position.Y < 20;
|
||||||
|
private bool BorderCheck(Vector2i position) => position.X >= 0 && position.X < 10;
|
||||||
|
private bool ClearCheck(Vector2i position) => _field.All(block => !position.Equals(block.Position));
|
||||||
|
|
||||||
|
private bool DropCheck(Vector2i position) => LowerBoundCheck(position) && ClearCheck(position);
|
||||||
|
private bool MoveCheck(Vector2i position) => BorderCheck(position) && ClearCheck(position);
|
||||||
|
private bool RotateCheck(Vector2i position) => BorderCheck(position) && LowerBoundCheck(position) && ClearCheck(position);
|
||||||
|
|
||||||
|
public void ProcessInput(BlockGamePlayerAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case BlockGamePlayerAction.StartLeft:
|
||||||
|
_leftPressed = true;
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.EndLeft:
|
||||||
|
_leftPressed = false;
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.StartRight:
|
||||||
|
_rightPressed = true;
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.EndRight:
|
||||||
|
_rightPressed = false;
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.Rotate:
|
||||||
|
TrySetRotation(Next(_currentRotation, false));
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.CounterRotate:
|
||||||
|
TrySetRotation(Next(_currentRotation, true));
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.SoftdropStart:
|
||||||
|
_softDropPressed = true;
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.SoftdropEnd:
|
||||||
|
_softDropPressed = false;
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.Harddrop:
|
||||||
|
PerformHarddrop();
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.Pause:
|
||||||
|
_running = false;
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause));
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.Unpause:
|
||||||
|
if (!_gameOver)
|
||||||
|
{
|
||||||
|
_running = true;
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.Hold:
|
||||||
|
HoldPiece();
|
||||||
|
break;
|
||||||
|
case BlockGamePlayerAction.ShowHighscores:
|
||||||
|
_running = false;
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Highscores));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TrySetRotation(BlockGamePieceRotation rotation)
|
||||||
|
{
|
||||||
|
if(!_running) return;
|
||||||
|
|
||||||
|
if (!_currentPiece.CanSpin) return;
|
||||||
|
|
||||||
|
if (!_currentPiece.Positions(_currentPiecePosition, rotation)
|
||||||
|
.All(RotateCheck))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentRotation = rotation;
|
||||||
|
UpdateAllFieldUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HoldPiece()
|
||||||
|
{
|
||||||
|
if (!_running) return;
|
||||||
|
|
||||||
|
if (_holdBlock) return;
|
||||||
|
|
||||||
|
var tempHeld = _heldPiece;
|
||||||
|
_heldPiece = _currentPiece;
|
||||||
|
_holdBlock = true;
|
||||||
|
|
||||||
|
if (!tempHeld.HasValue)
|
||||||
|
{
|
||||||
|
InitializeNewBlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeNewBlock(tempHeld.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PerformHarddrop()
|
||||||
|
{
|
||||||
|
int spacesDropped = 0;
|
||||||
|
while (_currentPiece.Positions(_currentPiecePosition.AddToY(1), _currentRotation)
|
||||||
|
.All(DropCheck))
|
||||||
|
{
|
||||||
|
_currentPiecePosition = _currentPiecePosition.AddToY(1);
|
||||||
|
spacesDropped++;
|
||||||
|
}
|
||||||
|
AddPoints(spacesDropped * 2);
|
||||||
|
|
||||||
|
InternalFieldTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateAllFieldUI()
|
||||||
|
{
|
||||||
|
if (!_started) return;
|
||||||
|
|
||||||
|
var computedField = ComputeField();
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(computedField.ToArray(), BlockGameMessages.BlockGameVisualType.GameField));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateFieldUI(IPlayerSession session)
|
||||||
|
{
|
||||||
|
if (!_started) return;
|
||||||
|
|
||||||
|
var computedField = ComputeField();
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(computedField.ToArray(), BlockGameMessages.BlockGameVisualType.GameField), session);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsGameOver => _field.Any(block => block.Position.Y == 0);
|
||||||
|
private void InvokeGameover()
|
||||||
|
{
|
||||||
|
_running = false;
|
||||||
|
_gameOver = true;
|
||||||
|
|
||||||
|
if (_component._player?.AttachedEntity != null)
|
||||||
|
{
|
||||||
|
var blockGameSystem = EntitySystem.Get<BlockGameSystem>();
|
||||||
|
|
||||||
|
_highScorePlacement = blockGameSystem.RegisterHighScore(_component._player.AttachedEntity.Name, Points);
|
||||||
|
SendHighscoreUpdate();
|
||||||
|
}
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameGameOverScreenMessage(Points, _highScorePlacement?.LocalPlacement, _highScorePlacement?.GlobalPlacement));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateNewPlayerUI(IPlayerSession session)
|
||||||
|
{
|
||||||
|
if (_gameOver)
|
||||||
|
{
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameGameOverScreenMessage(Points, _highScorePlacement?.LocalPlacement, _highScorePlacement?.GlobalPlacement), session);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Paused)
|
||||||
|
{
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause, Started), session);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game, Started), session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FullUpdate(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BlockGameBlock> ComputeField()
|
||||||
|
{
|
||||||
|
var result = new List<BlockGameBlock>();
|
||||||
|
result.AddRange(_field);
|
||||||
|
result.AddRange(_currentPiece.Blocks(_currentPiecePosition, _currentRotation));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum BlockGamePieceType
|
||||||
|
{
|
||||||
|
I,
|
||||||
|
L,
|
||||||
|
LInverted,
|
||||||
|
S,
|
||||||
|
SInverted,
|
||||||
|
T,
|
||||||
|
O
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum BlockGamePieceRotation
|
||||||
|
{
|
||||||
|
North,
|
||||||
|
East,
|
||||||
|
South,
|
||||||
|
West
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BlockGamePieceRotation Next(BlockGamePieceRotation rotation, bool inverted)
|
||||||
|
{
|
||||||
|
return rotation switch
|
||||||
|
{
|
||||||
|
BlockGamePieceRotation.North => inverted ? BlockGamePieceRotation.West : BlockGamePieceRotation.East,
|
||||||
|
BlockGamePieceRotation.East => inverted ? BlockGamePieceRotation.North : BlockGamePieceRotation.South,
|
||||||
|
BlockGamePieceRotation.South => inverted ? BlockGamePieceRotation.East : BlockGamePieceRotation.West,
|
||||||
|
BlockGamePieceRotation.West => inverted ? BlockGamePieceRotation.South : BlockGamePieceRotation.North,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(rotation), rotation, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct BlockGamePiece
|
||||||
|
{
|
||||||
|
public Vector2i[] Offsets;
|
||||||
|
private BlockGameBlock.BlockGameBlockColor _gameBlockColor;
|
||||||
|
public bool CanSpin;
|
||||||
|
|
||||||
|
public Vector2i[] Positions(Vector2i center,
|
||||||
|
BlockGamePieceRotation rotation)
|
||||||
|
{
|
||||||
|
return RotatedOffsets(rotation).Select(v => center + v).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2i[] RotatedOffsets(BlockGamePieceRotation rotation)
|
||||||
|
{
|
||||||
|
Vector2i[] rotatedOffsets = (Vector2i[])Offsets.Clone();
|
||||||
|
//until i find a better algo
|
||||||
|
var amount = rotation switch
|
||||||
|
{
|
||||||
|
BlockGamePieceRotation.North => 0,
|
||||||
|
BlockGamePieceRotation.East => 1,
|
||||||
|
BlockGamePieceRotation.South => 2,
|
||||||
|
BlockGamePieceRotation.West => 3,
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < amount; i++)
|
||||||
|
{
|
||||||
|
for (var j = 0; j < rotatedOffsets.Length; j++)
|
||||||
|
{
|
||||||
|
rotatedOffsets[j] = rotatedOffsets[j].Rotate90DegreesAsOffset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotatedOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockGameBlock[] Blocks(Vector2i center,
|
||||||
|
BlockGamePieceRotation rotation)
|
||||||
|
{
|
||||||
|
var positions = Positions(center, rotation);
|
||||||
|
var result = new BlockGameBlock[positions.Length];
|
||||||
|
var i = 0;
|
||||||
|
foreach (var position in positions)
|
||||||
|
{
|
||||||
|
result[i++] = position.ToBlockGameBlock(_gameBlockColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockGameBlock[] BlocksForPreview()
|
||||||
|
{
|
||||||
|
var xOffset = 0;
|
||||||
|
var yOffset = 0;
|
||||||
|
foreach (var offset in Offsets)
|
||||||
|
{
|
||||||
|
if (offset.X < xOffset) xOffset = offset.X;
|
||||||
|
if (offset.Y < yOffset) yOffset = offset.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Blocks(new Vector2i(-xOffset, -yOffset), BlockGamePieceRotation.North);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockGamePiece GetRandom(IRobustRandom random)
|
||||||
|
{
|
||||||
|
var pieces = (BlockGamePieceType[])Enum.GetValues(typeof(BlockGamePieceType));
|
||||||
|
var choice = random.Pick(pieces);
|
||||||
|
return GetPiece(choice);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockGamePiece GetPiece(BlockGamePieceType type)
|
||||||
|
{
|
||||||
|
//switch statement, hardcoded offsets
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
BlockGamePieceType.I => new BlockGamePiece
|
||||||
|
{
|
||||||
|
Offsets = new[]
|
||||||
|
{
|
||||||
|
new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(0, 1), new Vector2i(0, 2),
|
||||||
|
},
|
||||||
|
_gameBlockColor = BlockGameBlock.BlockGameBlockColor.LightBlue,
|
||||||
|
CanSpin = true
|
||||||
|
},
|
||||||
|
BlockGamePieceType.L => new BlockGamePiece
|
||||||
|
{
|
||||||
|
Offsets = new[]
|
||||||
|
{
|
||||||
|
new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(0, 1), new Vector2i(1, 1),
|
||||||
|
},
|
||||||
|
_gameBlockColor = BlockGameBlock.BlockGameBlockColor.Orange,
|
||||||
|
CanSpin = true
|
||||||
|
},
|
||||||
|
BlockGamePieceType.LInverted => new BlockGamePiece
|
||||||
|
{
|
||||||
|
Offsets = new[]
|
||||||
|
{
|
||||||
|
new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(-1, 1),
|
||||||
|
new Vector2i(0, 1),
|
||||||
|
},
|
||||||
|
_gameBlockColor = BlockGameBlock.BlockGameBlockColor.Blue,
|
||||||
|
CanSpin = true
|
||||||
|
},
|
||||||
|
BlockGamePieceType.S => new BlockGamePiece
|
||||||
|
{
|
||||||
|
Offsets = new[]
|
||||||
|
{
|
||||||
|
new Vector2i(0, -1), new Vector2i(1, -1), new Vector2i(-1, 0),
|
||||||
|
new Vector2i(0, 0),
|
||||||
|
},
|
||||||
|
_gameBlockColor = BlockGameBlock.BlockGameBlockColor.Green,
|
||||||
|
CanSpin = true
|
||||||
|
},
|
||||||
|
BlockGamePieceType.SInverted => new BlockGamePiece
|
||||||
|
{
|
||||||
|
Offsets = new[]
|
||||||
|
{
|
||||||
|
new Vector2i(-1, -1), new Vector2i(0, -1), new Vector2i(0, 0),
|
||||||
|
new Vector2i(1, 0),
|
||||||
|
},
|
||||||
|
_gameBlockColor = BlockGameBlock.BlockGameBlockColor.Red,
|
||||||
|
CanSpin = true
|
||||||
|
},
|
||||||
|
BlockGamePieceType.T => new BlockGamePiece
|
||||||
|
{
|
||||||
|
Offsets = new[]
|
||||||
|
{
|
||||||
|
new Vector2i(0, -1),
|
||||||
|
new Vector2i(-1, 0), new Vector2i(0, 0), new Vector2i(1, 0),
|
||||||
|
},
|
||||||
|
_gameBlockColor = BlockGameBlock.BlockGameBlockColor.Purple,
|
||||||
|
CanSpin = true
|
||||||
|
},
|
||||||
|
BlockGamePieceType.O => new BlockGamePiece
|
||||||
|
{
|
||||||
|
Offsets = new[]
|
||||||
|
{
|
||||||
|
new Vector2i(0, -1), new Vector2i(1, -1), new Vector2i(0, 0),
|
||||||
|
new Vector2i(1, 0),
|
||||||
|
},
|
||||||
|
_gameBlockColor = BlockGameBlock.BlockGameBlockColor.Yellow,
|
||||||
|
CanSpin = false
|
||||||
|
},
|
||||||
|
_ => new BlockGamePiece {Offsets = new[] {new Vector2i(0, 0)}}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
Content.Server/GameObjects/EntitySystems/BlockGameSystem.cs
Normal file
84
Content.Server/GameObjects/EntitySystems/BlockGameSystem.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Server.GameObjects.Components.Arcade;
|
||||||
|
using Content.Shared.Arcade;
|
||||||
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.GameObjects.EntitySystems
|
||||||
|
{
|
||||||
|
// ReSharper disable once ClassNeverInstantiated.Global
|
||||||
|
public class BlockGameSystem : EntitySystem
|
||||||
|
{
|
||||||
|
private readonly List<BlockGameMessages.HighScoreEntry> _roundHighscores = new List<BlockGameMessages.HighScoreEntry>();
|
||||||
|
private readonly List<BlockGameMessages.HighScoreEntry> _globalHighscores = new List<BlockGameMessages.HighScoreEntry>();
|
||||||
|
|
||||||
|
public HighScorePlacement RegisterHighScore(string name, int score)
|
||||||
|
{
|
||||||
|
var entry = new BlockGameMessages.HighScoreEntry(name, score);
|
||||||
|
return new HighScorePlacement(TryInsertIntoList(_roundHighscores, entry), TryInsertIntoList(_globalHighscores, entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BlockGameMessages.HighScoreEntry> GetLocalHighscores() => GetSortedHighscores(_roundHighscores);
|
||||||
|
|
||||||
|
public List<BlockGameMessages.HighScoreEntry> GetGlobalHighscores() => GetSortedHighscores(_globalHighscores);
|
||||||
|
|
||||||
|
private List<BlockGameMessages.HighScoreEntry> GetSortedHighscores(List<BlockGameMessages.HighScoreEntry> highScoreEntries)
|
||||||
|
{
|
||||||
|
var result = highScoreEntries.ShallowClone();
|
||||||
|
result.Sort((p1, p2) => p2.Score.CompareTo(p1.Score));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int? TryInsertIntoList(List<BlockGameMessages.HighScoreEntry> highScoreEntries, BlockGameMessages.HighScoreEntry entry)
|
||||||
|
{
|
||||||
|
if (highScoreEntries.Count < 5)
|
||||||
|
{
|
||||||
|
highScoreEntries.Add(entry);
|
||||||
|
return GetPlacement(highScoreEntries, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (highScoreEntries.Min(e => e.Score) >= entry.Score) return null;
|
||||||
|
|
||||||
|
var lowestHighscore = highScoreEntries.Min();
|
||||||
|
highScoreEntries.Remove(lowestHighscore);
|
||||||
|
highScoreEntries.Add(entry);
|
||||||
|
return GetPlacement(highScoreEntries, entry);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int? GetPlacement(List<BlockGameMessages.HighScoreEntry> highScoreEntries, BlockGameMessages.HighScoreEntry entry)
|
||||||
|
{
|
||||||
|
int? placement = null;
|
||||||
|
if (highScoreEntries.Contains(entry))
|
||||||
|
{
|
||||||
|
highScoreEntries.Sort((p1,p2) => p2.Score.CompareTo(p1.Score));
|
||||||
|
placement = 1 + highScoreEntries.IndexOf(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return placement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
foreach (var comp in ComponentManager.EntityQuery<BlockGameArcadeComponent>())
|
||||||
|
{
|
||||||
|
comp.DoGameTick(frameTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct HighScorePlacement
|
||||||
|
{
|
||||||
|
public readonly int? GlobalPlacement;
|
||||||
|
public readonly int? LocalPlacement;
|
||||||
|
|
||||||
|
public HighScorePlacement(int? globalPlacement, int? localPlacement)
|
||||||
|
{
|
||||||
|
GlobalPlacement = globalPlacement;
|
||||||
|
LocalPlacement = localPlacement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
Content.Shared/Arcade/BlockGameBlock.cs
Normal file
53
Content.Shared/Arcade/BlockGameBlock.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Arcade
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public struct BlockGameBlock
|
||||||
|
{
|
||||||
|
public Vector2i Position;
|
||||||
|
public readonly BlockGameBlockColor GameBlockColor;
|
||||||
|
|
||||||
|
public BlockGameBlock(Vector2i position, BlockGameBlockColor gameBlockColor)
|
||||||
|
{
|
||||||
|
Position = position;
|
||||||
|
GameBlockColor = gameBlockColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum BlockGameBlockColor
|
||||||
|
{
|
||||||
|
Red,
|
||||||
|
Orange,
|
||||||
|
Yellow,
|
||||||
|
Green,
|
||||||
|
Blue,
|
||||||
|
LightBlue,
|
||||||
|
Purple
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BlockGameVector2Extensions{
|
||||||
|
public static BlockGameBlock ToBlockGameBlock(this Vector2i vector2, BlockGameBlock.BlockGameBlockColor gameBlockColor)
|
||||||
|
{
|
||||||
|
return new BlockGameBlock(vector2, gameBlockColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2i AddToX(this Vector2i vector2, int amount)
|
||||||
|
{
|
||||||
|
return new Vector2i(vector2.X + amount, vector2.Y);
|
||||||
|
}
|
||||||
|
public static Vector2i AddToY(this Vector2i vector2, int amount)
|
||||||
|
{
|
||||||
|
return new Vector2i(vector2.X, vector2.Y + amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2i Rotate90DegreesAsOffset(this Vector2i vector)
|
||||||
|
{
|
||||||
|
return new Vector2i(-vector.Y, vector.X);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
141
Content.Shared/Arcade/BlockGameMessages.cs
Normal file
141
Content.Shared/Arcade/BlockGameMessages.cs
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#nullable enable
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Arcade
|
||||||
|
{
|
||||||
|
public static class BlockGameMessages
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class BlockGamePlayerActionMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly BlockGamePlayerAction PlayerAction;
|
||||||
|
public BlockGamePlayerActionMessage(BlockGamePlayerAction playerAction)
|
||||||
|
{
|
||||||
|
PlayerAction = playerAction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class BlockGameVisualUpdateMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly BlockGameVisualType GameVisualType;
|
||||||
|
public readonly BlockGameBlock[] Blocks;
|
||||||
|
public BlockGameVisualUpdateMessage(BlockGameBlock[] blocks, BlockGameVisualType gameVisualType)
|
||||||
|
{
|
||||||
|
Blocks = blocks;
|
||||||
|
GameVisualType = gameVisualType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum BlockGameVisualType
|
||||||
|
{
|
||||||
|
GameField,
|
||||||
|
HoldBlock,
|
||||||
|
NextBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class BlockGameScoreUpdateMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly int Points;
|
||||||
|
public BlockGameScoreUpdateMessage(int points)
|
||||||
|
{
|
||||||
|
Points = points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class BlockGameUserStatusMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly bool IsPlayer;
|
||||||
|
|
||||||
|
public BlockGameUserStatusMessage(bool isPlayer)
|
||||||
|
{
|
||||||
|
IsPlayer = isPlayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class BlockGameUserUnregisterMessage : BoundUserInterfaceMessage{}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class BlockGameSetScreenMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly BlockGameScreen Screen;
|
||||||
|
public readonly bool isStarted;
|
||||||
|
public BlockGameSetScreenMessage(BlockGameScreen screen, bool isStarted = true)
|
||||||
|
{
|
||||||
|
Screen = screen;
|
||||||
|
this.isStarted = isStarted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class BlockGameGameOverScreenMessage : BlockGameSetScreenMessage
|
||||||
|
{
|
||||||
|
public readonly int FinalScore;
|
||||||
|
public readonly int? LocalPlacement;
|
||||||
|
public readonly int? GlobalPlacement;
|
||||||
|
public BlockGameGameOverScreenMessage(int finalScore, int? localPlacement, int? globalPlacement) : base(BlockGameScreen.Gameover)
|
||||||
|
{
|
||||||
|
FinalScore = finalScore;
|
||||||
|
LocalPlacement = localPlacement;
|
||||||
|
GlobalPlacement = globalPlacement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum BlockGameScreen
|
||||||
|
{
|
||||||
|
Game,
|
||||||
|
Pause,
|
||||||
|
Gameover,
|
||||||
|
Highscores
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class BlockGameHighScoreUpdateMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public List<HighScoreEntry> LocalHighscores;
|
||||||
|
public List<HighScoreEntry> GlobalHighscores;
|
||||||
|
|
||||||
|
public BlockGameHighScoreUpdateMessage(List<HighScoreEntry> localHighscores, List<HighScoreEntry> globalHighscores)
|
||||||
|
{
|
||||||
|
LocalHighscores = localHighscores;
|
||||||
|
GlobalHighscores = globalHighscores;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class HighScoreEntry : IComparable
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public int Score;
|
||||||
|
|
||||||
|
public HighScoreEntry(string name, int score)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Score = score;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(object? obj)
|
||||||
|
{
|
||||||
|
if (!(obj is HighScoreEntry entry)) return 0;
|
||||||
|
return Score.CompareTo(entry.Score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class BlockGameLevelUpdateMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly int Level;
|
||||||
|
public BlockGameLevelUpdateMessage(int level)
|
||||||
|
{
|
||||||
|
Level = level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Content.Shared/Arcade/BlockGamePlayerAction.cs
Normal file
24
Content.Shared/Arcade/BlockGamePlayerAction.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Arcade
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum BlockGamePlayerAction
|
||||||
|
{
|
||||||
|
NewGame,
|
||||||
|
StartLeft,
|
||||||
|
EndLeft,
|
||||||
|
StartRight,
|
||||||
|
EndRight,
|
||||||
|
Rotate,
|
||||||
|
CounterRotate,
|
||||||
|
SoftdropStart,
|
||||||
|
SoftdropEnd,
|
||||||
|
Harddrop,
|
||||||
|
Pause,
|
||||||
|
Unpause,
|
||||||
|
Hold,
|
||||||
|
ShowHighscores
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Content.Shared/Arcade/BlockGameUiKey.cs
Normal file
11
Content.Shared/Arcade/BlockGameUiKey.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Arcade
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum BlockGameUiKey
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,7 @@
|
|||||||
public const uint MOB_STATE_MANAGER = 1070;
|
public const uint MOB_STATE_MANAGER = 1070;
|
||||||
public const uint SLIP = 1071;
|
public const uint SLIP = 1071;
|
||||||
public const uint SPACE_VILLAIN_ARCADE = 1072;
|
public const uint SPACE_VILLAIN_ARCADE = 1072;
|
||||||
|
public const uint BLOCKGAME_ARCADE = 1073;
|
||||||
|
|
||||||
// Net IDs for integration tests.
|
// Net IDs for integration tests.
|
||||||
public const uint PREDICTION_TEST = 10001;
|
public const uint PREDICTION_TEST = 10001;
|
||||||
|
|||||||
@@ -34,5 +34,12 @@ namespace Content.Shared.Input
|
|||||||
public static readonly BoundKeyFunction TakeScreenshot = "TakeScreenshot";
|
public static readonly BoundKeyFunction TakeScreenshot = "TakeScreenshot";
|
||||||
public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI";
|
public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI";
|
||||||
public static readonly BoundKeyFunction Point = "Point";
|
public static readonly BoundKeyFunction Point = "Point";
|
||||||
|
public static readonly BoundKeyFunction ArcadeUp = "ArcadeUp";
|
||||||
|
public static readonly BoundKeyFunction ArcadeDown = "ArcadeDown";
|
||||||
|
public static readonly BoundKeyFunction ArcadeLeft = "ArcadeLeft";
|
||||||
|
public static readonly BoundKeyFunction ArcadeRight = "ArcadeRight";
|
||||||
|
public static readonly BoundKeyFunction Arcade1 = "Arcade1";
|
||||||
|
public static readonly BoundKeyFunction Arcade2 = "Arcade2";
|
||||||
|
public static readonly BoundKeyFunction Arcade3 = "Arcade3";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,3 +30,35 @@
|
|||||||
type: SpaceVillainArcadeBoundUserInterface
|
type: SpaceVillainArcadeBoundUserInterface
|
||||||
- key: enum.WiresUiKey.Key
|
- key: enum.WiresUiKey.Key
|
||||||
type: WiresBoundUserInterface
|
type: WiresBoundUserInterface
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: BlockGameArcade
|
||||||
|
name: blockGameArcade
|
||||||
|
parent: ComputerBase
|
||||||
|
components:
|
||||||
|
- type: Icon
|
||||||
|
state: arcade
|
||||||
|
- type: PowerReceiver
|
||||||
|
- type: Sprite
|
||||||
|
layers:
|
||||||
|
- state: arcade
|
||||||
|
map: ["enum.ComputerVisualizer+Layers.Body"]
|
||||||
|
- state: invaders
|
||||||
|
shader: unshaded
|
||||||
|
map: ["enum.ComputerVisualizer+Layers.Screen"]
|
||||||
|
- type: Appearance
|
||||||
|
visuals:
|
||||||
|
- type: ComputerVisualizer
|
||||||
|
screen: invaders
|
||||||
|
key: ""
|
||||||
|
body: arcade
|
||||||
|
bodyBroken: arcade
|
||||||
|
- type: Anchorable
|
||||||
|
- type: Pullable
|
||||||
|
- type: BlockGameArcade
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.BlockGameUiKey.Key
|
||||||
|
type: BlockGameBoundUserInterface
|
||||||
|
- key: enum.WiresUiKey.Key
|
||||||
|
type: WiresBoundUserInterface
|
||||||
|
|||||||
@@ -280,3 +280,24 @@ binds:
|
|||||||
type: State
|
type: State
|
||||||
key: MouseMiddle
|
key: MouseMiddle
|
||||||
mod1: Shift
|
mod1: Shift
|
||||||
|
- function: ArcadeUp
|
||||||
|
type: State
|
||||||
|
key: Up
|
||||||
|
- function: ArcadeDown
|
||||||
|
type: State
|
||||||
|
key: Down
|
||||||
|
- function: ArcadeLeft
|
||||||
|
type: State
|
||||||
|
key: Left
|
||||||
|
- function: ArcadeRight
|
||||||
|
type: State
|
||||||
|
key: Right
|
||||||
|
- function: Arcade1
|
||||||
|
type: State
|
||||||
|
key: Space
|
||||||
|
- function: Arcade2
|
||||||
|
type: State
|
||||||
|
key: C
|
||||||
|
- function: Arcade3
|
||||||
|
type: State
|
||||||
|
key: Z
|
||||||
|
|||||||
Reference in New Issue
Block a user