using Content.Server.Power.Components; using Content.Server.UserInterface; using Content.Shared.Arcade; using Content.Shared.Sound; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; namespace Content.Server.Arcade.Components { [RegisterComponent] public sealed class SpaceVillainArcadeComponent : SharedSpaceVillainArcadeComponent { [Dependency] private readonly IRobustRandom _random = null!; [Dependency] private readonly IEntityManager _entityManager = default!; private bool Powered => _entityManager.TryGetComponent(Owner, out var powerReceiverComponent) && powerReceiverComponent.Powered; [ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(SpaceVillainArcadeUiKey.Key); [ViewVariables] public bool OverflowFlag; [ViewVariables] public bool PlayerInvincibilityFlag; [ViewVariables] public bool EnemyInvincibilityFlag; [ViewVariables] public SpaceVillainGame Game = null!; [DataField("newGameSound")] private SoundSpecifier _newGameSound = new SoundPathSpecifier("/Audio/Effects/Arcade/newgame.ogg"); [DataField("playerAttackSound")] private SoundSpecifier _playerAttackSound = new SoundPathSpecifier("/Audio/Effects/Arcade/player_attack.ogg"); [DataField("playerHealSound")] private SoundSpecifier _playerHealSound = new SoundPathSpecifier("/Audio/Effects/Arcade/player_heal.ogg"); [DataField("playerChargeSound")] private SoundSpecifier _playerChargeSound = new SoundPathSpecifier("/Audio/Effects/Arcade/player_charge.ogg"); [DataField("winSound")] private SoundSpecifier _winSound = new SoundPathSpecifier("/Audio/Effects/Arcade/win.ogg"); [DataField("gameOverSound")] private SoundSpecifier _gameOverSound = new SoundPathSpecifier("/Audio/Effects/Arcade/gameover.ogg"); [ViewVariables(VVAccess.ReadWrite)] [DataField("possibleFightVerbs")] private List _possibleFightVerbs = new List() {"Defeat", "Annihilate", "Save", "Strike", "Stop", "Destroy", "Robust", "Romance", "Pwn", "Own"}; [ViewVariables(VVAccess.ReadWrite)] [DataField("possibleFirstEnemyNames")] private List _possibleFirstEnemyNames = new List(){ "the Automatic", "Farmer", "Lord", "Professor", "the Cuban", "the Evil", "the Dread King", "the Space", "Lord", "the Great", "Duke", "General" }; [ViewVariables(VVAccess.ReadWrite)] [DataField("possibleLastEnemyNames")] private List _possibleLastEnemyNames = new List() { "Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn" }; [ViewVariables(VVAccess.ReadWrite)] [DataField("possibleRewards", customTypeSerializer:typeof(PrototypeIdListSerializer))] private List _possibleRewards = new List() { "ToyMouse", "ToyAi", "ToyNuke", "ToyAssistant", "ToyGriffin", "ToyHonk", "ToyIan", "ToyMarauder", "ToyMauler", "ToyGygax", "ToyOdysseus", "ToyOwlman", "ToyDeathRipley", "ToyPhazon", "ToyFireRipley", "ToyReticence", "ToyRipley", "ToySeraph", "ToyDurand", "ToySkeleton" }; protected override void Initialize() { base.Initialize(); if (UserInterface != null) { UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; } } public void OnPowerStateChanged(PowerChangedEvent e) { if (e.Powered) return; UserInterface?.CloseAll(); } private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg) { if (!Powered) return; if (serverMsg.Message is not SpaceVillainArcadePlayerActionMessage msg) return; switch (msg.PlayerAction) { case PlayerAction.Attack: Game?.ExecutePlayerAction(msg.PlayerAction); break; case PlayerAction.Heal: Game?.ExecutePlayerAction(msg.PlayerAction); break; case PlayerAction.Recharge: Game?.ExecutePlayerAction(msg.PlayerAction); break; case PlayerAction.NewGame: SoundSystem.Play(Filter.Pvs(Owner), _newGameSound.GetSound(), Owner, AudioParams.Default.WithVolume(-4f)); Game = new SpaceVillainGame(this); UserInterface?.SendMessage(Game.GenerateMetaDataMessage()); break; case PlayerAction.RequestData: UserInterface?.SendMessage(Game.GenerateMetaDataMessage()); break; } } /// /// Called when the user wins the game. /// public void ProcessWin() { var entityManager = IoCManager.Resolve(); entityManager.SpawnEntity(_random.Pick(_possibleRewards), entityManager.GetComponent(Owner).MapPosition); } /// /// Picks a fight-verb from the list of possible Verbs. /// /// A fight-verb. public string GenerateFightVerb() { return _random.Pick(_possibleFightVerbs); } /// /// Generates an enemy-name comprised of a first- and last-name. /// /// An enemy-name. public string GenerateEnemyName() { return $"{_random.Pick(_possibleFirstEnemyNames)} {_random.Pick(_possibleLastEnemyNames)}"; } /// /// A Class to handle all the game-logic of the SpaceVillain-game. /// public sealed class SpaceVillainGame { [Dependency] private readonly IRobustRandom _random = default!; [ViewVariables] private readonly SpaceVillainArcadeComponent _owner; [ViewVariables] public string Name => $"{_fightVerb} {_enemyName}"; [ViewVariables(VVAccess.ReadWrite)] private int _playerHp = 30; [ViewVariables(VVAccess.ReadWrite)] private int _playerHpMax = 30; [ViewVariables(VVAccess.ReadWrite)] private int _playerMp = 10; [ViewVariables(VVAccess.ReadWrite)] private int _playerMpMax = 10; [ViewVariables(VVAccess.ReadWrite)] private int _enemyHp = 45; [ViewVariables(VVAccess.ReadWrite)] private int _enemyHpMax = 45; [ViewVariables(VVAccess.ReadWrite)] private int _enemyMp = 20; [ViewVariables(VVAccess.ReadWrite)] private int _enemyMpMax = 20; [ViewVariables(VVAccess.ReadWrite)] private int _turtleTracker; [ViewVariables(VVAccess.ReadWrite)] private readonly string _fightVerb; [ViewVariables(VVAccess.ReadWrite)] private readonly string _enemyName; [ViewVariables] private bool _running = true; private string _latestPlayerActionMessage = ""; private string _latestEnemyActionMessage = ""; public SpaceVillainGame(SpaceVillainArcadeComponent owner) : this(owner, owner.GenerateFightVerb(), owner.GenerateEnemyName()) { } public SpaceVillainGame(SpaceVillainArcadeComponent owner, string fightVerb, string enemyName) { IoCManager.InjectDependencies(this); _owner = owner; //todo defeat the curse secret game mode _fightVerb = fightVerb; _enemyName = enemyName; } /// /// Validates all vars incase they overshoot their max-values. /// Does not check if vars surpass 0. /// private void ValidateVars() { if (_owner.OverflowFlag) return; if (_playerHp > _playerHpMax) _playerHp = _playerHpMax; if (_playerMp > _playerMpMax) _playerMp = _playerMpMax; if (_enemyHp > _enemyHpMax) _enemyHp = _enemyHpMax; if (_enemyMp > _enemyMpMax) _enemyMp = _enemyMpMax; } /// /// Called by the SpaceVillainArcadeComponent when Userinput is received. /// /// The action the user picked. public void ExecutePlayerAction(PlayerAction action) { if (!_running) return; switch (action) { case PlayerAction.Attack: var attackAmount = _random.Next(2, 6); _latestPlayerActionMessage = Loc.GetString("space-villain-game-player-attack-message", ("enemyName", _enemyName), ("attackAmount", attackAmount)); SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._playerAttackSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f)); if (!_owner.EnemyInvincibilityFlag) _enemyHp -= attackAmount; _turtleTracker -= _turtleTracker > 0 ? 1 : 0; break; case PlayerAction.Heal: var pointAmount = _random.Next(1, 3); var healAmount = _random.Next(6, 8); _latestPlayerActionMessage = Loc.GetString("space-villain-game-player-heal-message", ("magicPointAmount", pointAmount), ("healAmount", healAmount)); SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._playerHealSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f)); if (!_owner.PlayerInvincibilityFlag) _playerMp -= pointAmount; _playerHp += healAmount; _turtleTracker++; break; case PlayerAction.Recharge: var chargeAmount = _random.Next(4, 7); _latestPlayerActionMessage = Loc.GetString("space-villain-game-player-recharge-message", ("regainedPoints", chargeAmount)); SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._playerChargeSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f)); _playerMp += chargeAmount; _turtleTracker -= _turtleTracker > 0 ? 1 : 0; break; } if (!CheckGameConditions()) { return; } ValidateVars(); ExecuteAiAction(); if (!CheckGameConditions()) { return; } ValidateVars(); UpdateUi(); } /// /// Checks the Game conditions and Updates the Ui & Plays a sound accordingly. /// /// A bool indicating if the game should continue. private bool CheckGameConditions() { if ((_playerHp > 0 && _playerMp > 0) && (_enemyHp <= 0 || _enemyMp <= 0)) { _running = false; UpdateUi(Loc.GetString("space-villain-game-player-wins-message"), Loc.GetString("space-villain-game-enemy-dies-message", ("enemyName", _enemyName)), true); SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._winSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f)); _owner.ProcessWin(); return false; } if (_playerHp > 0 && _playerMp > 0) return true; if ((_enemyHp > 0 && _enemyMp > 0)) { _running = false; UpdateUi(Loc.GetString("space-villain-game-player-loses-message"), Loc.GetString("space-villain-game-enemy-cheers-message", ("enemyName", _enemyName)), true); SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._gameOverSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f)); return false; } if (_enemyHp <= 0 || _enemyMp <= 0) { _running = false; UpdateUi(Loc.GetString("space-villain-game-player-loses-message"), Loc.GetString("space-villain-game-enemy-dies-with-player-message ", ("enemyName", _enemyName)), true); SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._gameOverSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f)); return false; } return true; } /// /// Updates the UI. /// private void UpdateUi(bool metadata = false) { _owner.UserInterface?.SendMessage(metadata ? GenerateMetaDataMessage() : GenerateUpdateMessage()); } private void UpdateUi(string message1, string message2, bool metadata = false) { _latestPlayerActionMessage = message1; _latestEnemyActionMessage = message2; UpdateUi(metadata); } /// /// Handles the logic of the AI /// /// An Enemyaction-message. private void ExecuteAiAction() { if (_turtleTracker >= 4) { var boomAmount = _random.Next(5, 10); _latestEnemyActionMessage = Loc.GetString("space-villain-game-enemy-throws-bomb-message", ("enemyName", _enemyName), ("damageReceived", boomAmount)); if (_owner.PlayerInvincibilityFlag) return; _playerHp -= boomAmount; _turtleTracker--; } else if (_enemyMp <= 5 && _random.Prob(0.7f)) { var stealAmount = _random.Next(2, 3); _latestEnemyActionMessage = Loc.GetString("space-villain-game-enemy-steals-player-power-message", ("enemyName", _enemyName), ("stolenAmount", stealAmount)); if (_owner.PlayerInvincibilityFlag) return; _playerMp -= stealAmount; _enemyMp += stealAmount; } else if (_enemyHp <= 10 && _enemyMp > 4) { _enemyHp += 4; _enemyMp -= 4; _latestEnemyActionMessage = Loc.GetString("space-villain-game-enemy-heals-message", ("enemyName", _enemyName), ("healedAmount", 4)); } else { var attackAmount = _random.Next(3, 6); _latestEnemyActionMessage = Loc.GetString("space-villain-game-enemy-attacks-message", ("enemyName", _enemyName), ("damageDealt", attackAmount)); if (_owner.PlayerInvincibilityFlag) return; _playerHp -= attackAmount; } } /// /// Generates a Metadata-message based on the objects values. /// /// A Metadata-message. public SpaceVillainArcadeMetaDataUpdateMessage GenerateMetaDataMessage() { return new(_playerHp, _playerMp, _enemyHp, _enemyMp, _latestPlayerActionMessage, _latestEnemyActionMessage, Name, _enemyName, !_running); } /// /// Creates an Update-message based on the objects values. /// /// An Update-Message. public SpaceVillainArcadeDataUpdateMessage GenerateUpdateMessage() { return new(_playerHp, _playerMp, _enemyHp, _enemyMp, _latestPlayerActionMessage, _latestEnemyActionMessage); } } } }