(61d00a474) v0.9.7.1
This commit is contained in:
11
.gitignore
vendored
11
.gitignore
vendored
@@ -11,12 +11,23 @@ bld/
|
||||
[Oo]bj/
|
||||
[Dd]ebug*/
|
||||
[Rr]elease*/
|
||||
*.o
|
||||
|
||||
# Misc vs crap
|
||||
*.v12.suo
|
||||
*.suo
|
||||
*.csproj.user
|
||||
*.shproj.user
|
||||
*.vcxproj.user
|
||||
|
||||
# Platform-specific webm_mem_playback files
|
||||
Libraries/webm_mem_playback/libvpx_x64_linux/
|
||||
Libraries/webm_mem_playback/libvpx_x64_vs15/
|
||||
Libraries/webm_mem_playback/libvpx_x86_vs15/
|
||||
Libraries/webm_mem_playback/libwebm_x64_linux/
|
||||
Libraries/webm_mem_playback/libwebm_x64_vs19/
|
||||
Libraries/webm_mem_playback/libwebm_x86_vs19/
|
||||
Libraries/webm_mem_playback/opus_x64_linux/
|
||||
|
||||
#performance reports & sessions
|
||||
*.vsp
|
||||
|
||||
Binary file not shown.
@@ -1,35 +0,0 @@
|
||||
#!/bin/bash
|
||||
# MonoKickstart Shell Script
|
||||
# Written by Ethan "flibitijibibo" Lee
|
||||
|
||||
# Move to script's directory
|
||||
cd "`dirname "$0"`"
|
||||
|
||||
# Get the system architecture
|
||||
UNAME=`uname`
|
||||
ARCH=`uname -m`
|
||||
|
||||
# MonoKickstart picks the right libfolder, so just execute the right binary.
|
||||
if [ "$UNAME" == "Darwin" ]; then
|
||||
# ... Except on OSX.
|
||||
export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:./osx/
|
||||
|
||||
# El Capitan is a total idiot and wipes this variable out, making the
|
||||
# Steam overlay disappear. This sidesteps "System Integrity Protection"
|
||||
# and resets the variable with Valve's own variable (they provided this
|
||||
# fix by the way, thanks Valve!). Note that you will need to update your
|
||||
# launch configuration to the script location, NOT just the app location
|
||||
# (i.e. Kick.app/Contents/MacOS/Kick, not just Kick.app).
|
||||
# -flibit
|
||||
if [ "$STEAM_DYLD_INSERT_LIBRARIES" != "" ] && [ "$DYLD_INSERT_LIBRARIES" == "" ]; then
|
||||
export DYLD_INSERT_LIBRARIES="$STEAM_DYLD_INSERT_LIBRARIES"
|
||||
fi
|
||||
|
||||
./Barotrauma.bin.osx $@
|
||||
else
|
||||
if [ "$ARCH" == "x86_64" ]; then
|
||||
./Barotrauma.bin.x86_64 $@
|
||||
else
|
||||
./Barotrauma.bin.x86 $@
|
||||
fi
|
||||
fi
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,242 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
<HasSharedItems>true</HasSharedItems>
|
||||
<SharedGUID>51aff563-4982-474d-a92f-50b06db05b3d</SharedGUID>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<Import_RootNamespace>ClientCode</Import_RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Camera.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AICharacter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\AIController.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\AITarget.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\EnemyAIController.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\AI\HumanAIController.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Animation\Ragdoll.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Attack.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Character.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\CharacterHealth.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterHUD.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterInfo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterNetworking.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterSound.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\AfflictionHusk.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\AfflictionPsychosis.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Health\DamageModifier.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\HUDProgressBar.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Jobs\JobPrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Limb.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\DebugConsole.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\EventInput\EventInput.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\EventInput\KeyboardDispatcher.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Events\EventManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Events\Missions\CombatMission.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Events\Missions\Mission.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Events\Missions\MissionMode.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Events\Missions\MissionPrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Fonts\ScalableFont.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameMain.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\CrewManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameMode.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\CampaignMode.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\MultiPlayerCampaign.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\SinglePlayerCampaign.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\BasicTutorial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\CaptainTutorial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\DoctorTutorial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\EditorTutorial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\EngineerTutorial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\MechanicTutorial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\OfficerTutorial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\ScenarioTutorial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\Tutorial.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameModes\Tutorials\TutorialMode.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\GameSession.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSession\RoundSummary.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GameSettings.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\ChatBox.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\ComponentStyle.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\Graph.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUI.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIButton.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUICanvas.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUICustomComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIDropDown.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIFrame.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIImage.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUILayoutGroup.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIListBox.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIMessage.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIMessageBox.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUINumberInput.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIProgressBar.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIRadioButtonGroup.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIScrollBar.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUIStyle.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUITextBlock.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUITextBox.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\GUITickBox.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\HUDLayoutSettings.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\LoadingScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\ParamsEditor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\RectTransform.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\ShapeExtensions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\UISprite.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\VideoPlayer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\GUI\Widget.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\CharacterInventory.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Door.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\ElectricalDischarger.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\ItemComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\ItemContainer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\ItemLabel.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\LevelResource.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\LightComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Machines\Controller.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Machines\Deconstructor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Machines\Engine.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Machines\Fabricator.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Machines\MiniMap.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Machines\Pump.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Machines\Reactor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Machines\Sonar.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Machines\Steering.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Power\PowerContainer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Power\Powered.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Power\PowerTransfer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Repairable.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\RepairTool.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Signal\Connection.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Signal\ConnectionPanel.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Signal\CustomInterface.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Signal\MotionSensor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Signal\WifiComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Signal\Wire.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\StatusHUD.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Turret.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\DockingPort.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Inventory.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Item.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\ItemInventory.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\ItemPrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Rope.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Explosion.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\FireSource.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Gap.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Hull.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\ItemAssemblyPrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\BackgroundCreatures\BackgroundCreature.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\BackgroundCreatures\BackgroundCreatureManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\BackgroundCreatures\BackgroundCreaturePrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\CaveGenerator.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\Level.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\LevelObjects\LevelObject.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\LevelObjects\LevelObjectManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\LevelObjects\LevelObjectPrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\LevelObjects\LevelTrigger.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\LevelRenderer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\LevelWall.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\Ruins\RuinGenerator.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Levels\WaterRenderer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Lights\ConvexHull.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Lights\LightManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Lights\LightSource.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\LinkedSubmarine.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\MapEntity.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\MapEntityPrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Map\Location.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Map\Map.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Structure.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\StructurePrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\Submarine.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\SubmarineBody.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Map\WayPoint.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Media\Video.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\BanList.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ChatMessage.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Client.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\EntitySpawner.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\FileTransfer\FileReceiver.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\GameClient.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\KarmaManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetEntityEvent\ClientEntityEventManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetEntityEvent\NetEntityEvent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetStats.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\OrderChatMessage.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Peers\ClientPeer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Peers\LidgrenClientPeer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Peers\SteamP2PClientPeer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Peers\SteamP2POwnerPeer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\RespawnManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ServerInfo.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ServerLog.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ServerSettings.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\SteamManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Voip\VoipCapture.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Voip\VoipClient.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Voip\VoipConfig.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Voting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\WhiteList.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Particles\Decal.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Particles\DecalManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Particles\DecalPrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Particles\Particle.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Particles\ParticleEmitter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Particles\ParticleManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Particles\ParticlePrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Physics\PhysicsBody.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\PlayerInput.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Program.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\BlurEffect.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\CampaignSetupUI.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\CampaignUI.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\CharacterEditor\CharacterEditorScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\CharacterEditor\Wizard.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\CreditsPlayer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\GameScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\LevelEditorScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\LobbyScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\MainMenuScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\NetLobbyScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\ParticleEditorScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\Screen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\ServerListScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\SpriteEditorScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\SteamWorkshopScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Screens\SubEditorScreen.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Serialization\SerializableEntityEditor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\OggSound.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\OpenAL\Al.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\OpenAL\Alc.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\Sound.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\SoundChannel.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\SoundFilters.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\SoundManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\SoundPlayer.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\VideoSound.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sounds\VoipSound.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\ConditionalSprite.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DecorativeSprite.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DeformableSprite.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DeformAnimations\CustomDeformation.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DeformAnimations\Inflate.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DeformAnimations\JointBendDeformation.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DeformAnimations\NoiseDeformation.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DeformAnimations\PositionalDeformation.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\DeformAnimations\SpriteDeformation.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\Sprite.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Sprite\SpriteSheet.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\StatusEffects\StatusEffect.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Traitors\TraitorMissionPrefab.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\CrossThread.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\LocalizationCSVtoXML.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\MathUtils.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\OpenFileDialog.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\TextureLoader.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Source\Utils\ToolBox.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>51aff563-4982-474d-a92f-50b06db05b3d</ProjectGuid>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<ReleaseVersion>0.9.0.0</ReleaseVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
|
||||
<PropertyGroup />
|
||||
<Import Project="ClientCode.projitems" Label="Shared" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
|
||||
</Project>
|
||||
409
Barotrauma/BarotraumaClient/ClientSource/Camera.cs
Normal file
409
Barotrauma/BarotraumaClient/ClientSource/Camera.cs
Normal file
@@ -0,0 +1,409 @@
|
||||
using Barotrauma.Networking;
|
||||
using Lidgren.Network;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class Camera
|
||||
{
|
||||
public static bool FollowSub = true;
|
||||
|
||||
private float? defaultZoom;
|
||||
public float DefaultZoom
|
||||
{
|
||||
get { return defaultZoom ?? (GameMain.Config == null || GameMain.Config.EnableMouseLook ? 1.3f : 1.0f); }
|
||||
set
|
||||
{
|
||||
defaultZoom = MathHelper.Clamp(value, 0.5f, 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private float zoomSmoothness = 8.0f;
|
||||
public float ZoomSmoothness
|
||||
{
|
||||
get { return zoomSmoothness; }
|
||||
set { zoomSmoothness = Math.Max(value, 0.01f); }
|
||||
}
|
||||
private float moveSmoothness = 8.0f;
|
||||
public float MoveSmoothness
|
||||
{
|
||||
get { return moveSmoothness; }
|
||||
set { moveSmoothness = Math.Max(value, 0.01f); }
|
||||
}
|
||||
|
||||
private float minZoom = 0.1f;
|
||||
public float MinZoom
|
||||
{
|
||||
get { return minZoom;}
|
||||
set { minZoom = MathHelper.Clamp(value, 0.01f, 10.0f); }
|
||||
}
|
||||
|
||||
private float maxZoom = 2.0f;
|
||||
public float MaxZoom
|
||||
{
|
||||
get { return maxZoom; }
|
||||
set { maxZoom = MathHelper.Clamp(value, 1.0f, 10.0f); }
|
||||
}
|
||||
|
||||
private float zoom;
|
||||
|
||||
private float offsetAmount;
|
||||
|
||||
private Matrix transform, shaderTransform, viewMatrix;
|
||||
private Vector2 position;
|
||||
private float rotation;
|
||||
|
||||
private float angularVelocity;
|
||||
private float angularDamping;
|
||||
private float angularSpring;
|
||||
|
||||
private Vector2 prevPosition;
|
||||
private float prevZoom;
|
||||
|
||||
public float Shake;
|
||||
private Vector2 shakePosition;
|
||||
private float shakeTimer;
|
||||
|
||||
//the area of the world inside the camera view
|
||||
private Rectangle worldView;
|
||||
|
||||
private float globalZoomScale = 1.0f;
|
||||
|
||||
private Point resolution;
|
||||
|
||||
private Vector2 targetPos;
|
||||
|
||||
//used to smooth out the movement when in freecam
|
||||
private float targetZoom;
|
||||
private Vector2 velocity;
|
||||
|
||||
public float Zoom
|
||||
{
|
||||
get { return zoom; }
|
||||
set
|
||||
{
|
||||
zoom = MathHelper.Clamp(value, GameMain.DebugDraw ? 0.01f : MinZoom, MaxZoom);
|
||||
|
||||
Vector2 center = WorldViewCenter;
|
||||
float newWidth = resolution.X / zoom;
|
||||
float newHeight = resolution.Y / zoom;
|
||||
|
||||
worldView = new Rectangle(
|
||||
(int)(center.X - newWidth / 2.0f),
|
||||
(int)(center.Y + newHeight / 2.0f),
|
||||
(int)newWidth,
|
||||
(int)newHeight);
|
||||
|
||||
//UpdateTransform();
|
||||
}
|
||||
}
|
||||
|
||||
public float Rotation
|
||||
{
|
||||
get { return rotation; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
rotation = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float AngularVelocity
|
||||
{
|
||||
get { return angularVelocity; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value)) return;
|
||||
angularVelocity = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float OffsetAmount
|
||||
{
|
||||
get { return offsetAmount; }
|
||||
set { offsetAmount = value; }
|
||||
}
|
||||
|
||||
public Point Resolution
|
||||
{
|
||||
get { return resolution; }
|
||||
}
|
||||
|
||||
public Rectangle WorldView
|
||||
{
|
||||
get { return worldView; }
|
||||
}
|
||||
|
||||
public Vector2 WorldViewCenter
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector2(
|
||||
worldView.X + worldView.Width / 2.0f,
|
||||
worldView.Y - worldView.Height / 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public Matrix Transform
|
||||
{
|
||||
get { return transform; }
|
||||
}
|
||||
|
||||
public Matrix ShaderTransform
|
||||
{
|
||||
get { return shaderTransform; }
|
||||
}
|
||||
|
||||
public Camera()
|
||||
{
|
||||
zoom = prevZoom = targetZoom = 1.0f;
|
||||
rotation = 0.0f;
|
||||
position = Vector2.Zero;
|
||||
|
||||
CreateMatrices();
|
||||
GameMain.Instance.OnResolutionChanged += () => { CreateMatrices(); };
|
||||
|
||||
UpdateTransform(false);
|
||||
}
|
||||
|
||||
public Vector2 TargetPos
|
||||
{
|
||||
get { return targetPos; }
|
||||
set { targetPos = value; }
|
||||
}
|
||||
|
||||
public Vector2 GetPosition()
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
// Auxiliary function to move the camera
|
||||
public void Translate(Vector2 amount)
|
||||
{
|
||||
position += amount;
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg)
|
||||
{
|
||||
if (Character.Controlled != null && !Character.Controlled.IsDead) { return; }
|
||||
|
||||
msg.Write((byte)ClientNetObject.SPECTATING_POS);
|
||||
msg.Write(position.X);
|
||||
msg.Write(position.Y);
|
||||
}
|
||||
|
||||
private void CreateMatrices()
|
||||
{
|
||||
resolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
worldView = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
viewMatrix = Matrix.CreateTranslation(new Vector3(GameMain.GraphicsWidth / 2.0f, GameMain.GraphicsHeight / 2.0f, 0));
|
||||
|
||||
globalZoomScale = (float)Math.Pow(new Vector2(resolution.X, resolution.Y).Length() / new Vector2(1920, 1080).Length(), 2);
|
||||
}
|
||||
|
||||
public void UpdateTransform(bool interpolate = true)
|
||||
{
|
||||
Vector2 interpolatedPosition = interpolate ? Timing.Interpolate(prevPosition, position) : position;
|
||||
|
||||
float interpolatedZoom = interpolate ? Timing.Interpolate(prevZoom, zoom) : zoom;
|
||||
|
||||
worldView.X = (int)(interpolatedPosition.X - worldView.Width / 2.0);
|
||||
worldView.Y = (int)(interpolatedPosition.Y + worldView.Height / 2.0);
|
||||
|
||||
transform = Matrix.CreateTranslation(
|
||||
new Vector3(-interpolatedPosition.X, interpolatedPosition.Y, 0)) *
|
||||
Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) *
|
||||
Matrix.CreateRotationZ(rotation) * viewMatrix;
|
||||
|
||||
shaderTransform = Matrix.CreateTranslation(
|
||||
new Vector3(
|
||||
-interpolatedPosition.X - resolution.X / interpolatedZoom / 2.0f,
|
||||
-interpolatedPosition.Y - resolution.Y / interpolatedZoom / 2.0f, 0)) *
|
||||
Matrix.CreateScale(new Vector3(interpolatedZoom, interpolatedZoom, 1)) *
|
||||
|
||||
viewMatrix * Matrix.CreateRotationZ(-rotation);
|
||||
|
||||
if (Character.Controlled == null)
|
||||
{
|
||||
GameMain.SoundManager.ListenerPosition = new Vector3(WorldViewCenter.X, WorldViewCenter.Y, -(100.0f / zoom));
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.SoundManager.ListenerPosition = new Vector3(Character.Controlled.WorldPosition.X, Character.Controlled.WorldPosition.Y, -(100.0f / zoom));
|
||||
}
|
||||
|
||||
|
||||
if (!interpolate)
|
||||
{
|
||||
prevPosition = position;
|
||||
prevZoom = zoom;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 previousOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Resets to false each time the MoveCamera method is called.
|
||||
/// </summary>
|
||||
public bool Freeze { get; set; }
|
||||
|
||||
public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true)
|
||||
{
|
||||
prevPosition = position;
|
||||
prevZoom = zoom;
|
||||
|
||||
float moveSpeed = 20.0f / zoom;
|
||||
|
||||
Vector2 moveCam = Vector2.Zero;
|
||||
if (targetPos == Vector2.Zero)
|
||||
{
|
||||
Vector2 moveInput = Vector2.Zero;
|
||||
if (allowMove && GUI.KeyboardDispatcher.Subscriber == null)
|
||||
{
|
||||
if (PlayerInput.KeyDown(Keys.LeftShift)) moveSpeed *= 2.0f;
|
||||
if (PlayerInput.KeyDown(Keys.LeftControl)) moveSpeed *= 0.5f;
|
||||
|
||||
if (GameMain.Config.KeyBind(InputType.Left).IsDown()) moveInput.X -= 1.0f;
|
||||
if (GameMain.Config.KeyBind(InputType.Right).IsDown()) moveInput.X += 1.0f;
|
||||
if (GameMain.Config.KeyBind(InputType.Down).IsDown()) moveInput.Y -= 1.0f;
|
||||
if (GameMain.Config.KeyBind(InputType.Up).IsDown()) moveInput.Y += 1.0f;
|
||||
}
|
||||
|
||||
velocity = Vector2.Lerp(velocity, moveInput, deltaTime * 10.0f);
|
||||
moveCam = velocity * moveSpeed * deltaTime * 60.0f;
|
||||
|
||||
if (Screen.Selected == GameMain.GameScreen && FollowSub)
|
||||
{
|
||||
var closestSub = Submarine.FindClosest(WorldViewCenter);
|
||||
if (closestSub != null)
|
||||
{
|
||||
moveCam += FarseerPhysics.ConvertUnits.ToDisplayUnits(closestSub.Velocity * deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowZoom && GUI.MouseOn == null)
|
||||
{
|
||||
Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition);
|
||||
Vector2 diffViewCenter;
|
||||
diffViewCenter = ((mouseInWorld - Position) * Zoom);
|
||||
targetZoom = MathHelper.Clamp(
|
||||
targetZoom + (PlayerInput.ScrollWheelSpeed / 1000.0f) * zoom,
|
||||
GameMain.DebugDraw ? MinZoom * 0.1f : MinZoom,
|
||||
MaxZoom);
|
||||
|
||||
Zoom = MathHelper.Lerp(Zoom, targetZoom, deltaTime * 10.0f);
|
||||
if (!PlayerInput.KeyDown(Keys.F)) Position = mouseInWorld - (diffViewCenter / Zoom);
|
||||
}
|
||||
}
|
||||
else if (allowMove)
|
||||
{
|
||||
Vector2 mousePos = PlayerInput.MousePosition;
|
||||
Vector2 offset = mousePos - resolution.ToVector2() / 2;
|
||||
offset.X = offset.X / (resolution.X * 0.4f);
|
||||
offset.Y = -offset.Y / (resolution.Y * 0.3f);
|
||||
if (offset.LengthSquared() > 1.0f) offset.Normalize();
|
||||
offset *= offsetAmount;
|
||||
// Freeze the camera movement by default, when the cursor is on top of an ui element.
|
||||
// Setting a positive value to the OffsetAmount, will override this behaviour.
|
||||
if (GUI.MouseOn != null && offsetAmount > 0)
|
||||
{
|
||||
Freeze = true;
|
||||
}
|
||||
if (CharacterHealth.OpenHealthWindow != null || CrewManager.IsCommandInterfaceOpen)
|
||||
{
|
||||
offset *= 0;
|
||||
Freeze = false;
|
||||
}
|
||||
if (Freeze)
|
||||
{
|
||||
offset = previousOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
previousOffset = offset;
|
||||
}
|
||||
|
||||
//how much to zoom out (zoom completely out when offset is 1000)
|
||||
float zoomOutAmount = Math.Min(offset.Length() / 1000.0f, 1.0f);
|
||||
//zoom amount when resolution is not taken into account
|
||||
float unscaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount);
|
||||
//zoom with resolution taken into account (zoom further out on smaller resolutions)
|
||||
float scaledZoom = unscaledZoom * globalZoomScale;
|
||||
|
||||
//an ad-hoc way of allowing the players to have roughly the same maximum view distance regardless of the resolution,
|
||||
//while still keeping the zoom around 1.0 when not looking further away (because otherwise we'd always be downsampling
|
||||
//on lower resolutions, which doesn't look that good)
|
||||
float newZoom = MathHelper.Lerp(unscaledZoom, scaledZoom,
|
||||
(GameMain.Config == null || GameMain.Config.EnableMouseLook) ? (float)Math.Sqrt(zoomOutAmount) : 0.3f);
|
||||
|
||||
Zoom += (newZoom - zoom) / ZoomSmoothness;
|
||||
|
||||
//force targetzoom to the current zoom value, so the camera stays at the same zoom when switching to freecam
|
||||
targetZoom = Zoom;
|
||||
|
||||
Vector2 diff = (targetPos + offset) - position;
|
||||
|
||||
moveCam = diff / MoveSmoothness;
|
||||
}
|
||||
rotation += angularVelocity * deltaTime;
|
||||
angularVelocity *= (1.0f - angularDamping);
|
||||
angularVelocity += -rotation * angularSpring;
|
||||
|
||||
angularDamping = 0.05f;
|
||||
angularSpring = 0.2f;
|
||||
|
||||
if (Shake < 0.01f)
|
||||
{
|
||||
shakePosition = Vector2.Zero;
|
||||
shakeTimer = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
shakeTimer += deltaTime * 5.0f;
|
||||
Vector2 noisePos = new Vector2((float)PerlinNoise.CalculatePerlin(shakeTimer, shakeTimer, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(shakeTimer, shakeTimer, 0.5f) - 0.5f);
|
||||
|
||||
shakePosition = noisePos * Shake * 2.0f;
|
||||
Shake = MathHelper.Lerp(Shake, 0.0f, deltaTime * 2.0f);
|
||||
}
|
||||
|
||||
Translate(moveCam + shakePosition);
|
||||
Freeze = false;
|
||||
}
|
||||
|
||||
public void StopMovement()
|
||||
{
|
||||
targetZoom = zoom;
|
||||
velocity = Vector2.Zero;
|
||||
angularVelocity = 0.0f;
|
||||
rotation = 0.0f;
|
||||
}
|
||||
|
||||
public Vector2 Position
|
||||
{
|
||||
get { return position; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
position = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 ScreenToWorld(Vector2 coords)
|
||||
{
|
||||
Vector2 worldCoords = Vector2.Transform(coords, Matrix.Invert(transform));
|
||||
return new Vector2(worldCoords.X, -worldCoords.Y);
|
||||
}
|
||||
|
||||
public Vector2 WorldToScreen(Vector2 coords)
|
||||
{
|
||||
coords.Y = -coords.Y;
|
||||
//Vector2 screenCoords = Vector2.Transform(coords, transform);
|
||||
return Vector2.Transform(coords, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class AITarget
|
||||
{
|
||||
public static bool ShowAITargets;
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!ShowAITargets) { return; }
|
||||
var pos = new Vector2(WorldPosition.X, -WorldPosition.Y);
|
||||
if (soundRange > 0.0f)
|
||||
{
|
||||
Color color;
|
||||
if (Entity is Character)
|
||||
{
|
||||
color = Color.Yellow;
|
||||
}
|
||||
else if (Entity is Item)
|
||||
{
|
||||
color = Color.Orange;
|
||||
}
|
||||
else
|
||||
{
|
||||
color = Color.OrangeRed;
|
||||
}
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, SoundRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, 3, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
|
||||
GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SoundRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1);
|
||||
}
|
||||
if (sightRange > 0.0f)
|
||||
{
|
||||
Color color;
|
||||
if (Entity is Character)
|
||||
{
|
||||
color = Color.CornflowerBlue;
|
||||
}
|
||||
else if (Entity is Item)
|
||||
{
|
||||
color = Color.CadetBlue;
|
||||
}
|
||||
else
|
||||
{
|
||||
//color = Color.WhiteSmoke;
|
||||
// disable the indicators for structures, because they clutter the debug view
|
||||
return;
|
||||
}
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, SightRange, 100, color, thickness: 1 / Screen.Selected.Cam.Zoom);
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, 6, 8, color, thickness: 2 / Screen.Selected.Cam.Zoom);
|
||||
GUI.DrawLine(spriteBatch, pos, pos + Vector2.UnitY * SightRange, color, width: (int)(1 / Screen.Selected.Cam.Zoom) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class EnemyAIController : AIController
|
||||
{
|
||||
public override void DebugDraw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (Character.IsDead) return;
|
||||
|
||||
Vector2 pos = Character.WorldPosition;
|
||||
pos.Y = -pos.Y;
|
||||
|
||||
if (State == AIState.Idle && PreviousState == AIState.Attack)
|
||||
{
|
||||
var target = _selectedAiTarget ?? _lastAiTarget;
|
||||
if (target != null && target.Entity != null)
|
||||
{
|
||||
var memory = GetTargetMemory(target);
|
||||
Vector2 targetPos = memory.Location;
|
||||
targetPos.Y = -targetPos.Y;
|
||||
GUI.DrawLine(spriteBatch, pos, targetPos, Color.White * 0.5f, 0, 4);
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{target.Entity.ToString()} ({memory.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
}
|
||||
else if (SelectedAiTarget?.Entity != null)
|
||||
{
|
||||
Vector2 targetPos = SelectedAiTarget.WorldPosition;
|
||||
if (State == AIState.Attack)
|
||||
{
|
||||
targetPos = attackWorldPos;
|
||||
}
|
||||
targetPos.Y = -targetPos.Y;
|
||||
GUI.DrawLine(spriteBatch, pos, targetPos, GUI.Style.Red * 0.5f, 0, 4);
|
||||
if (wallTarget != null)
|
||||
{
|
||||
Vector2 wallTargetPos = wallTarget.Position;
|
||||
if (wallTarget.Structure.Submarine != null) { wallTargetPos += wallTarget.Structure.Submarine.Position; }
|
||||
wallTargetPos.Y = -wallTargetPos.Y;
|
||||
GUI.DrawRectangle(spriteBatch, wallTargetPos - new Vector2(10.0f, 10.0f), new Vector2(20.0f, 20.0f), Color.Orange, false);
|
||||
GUI.DrawLine(spriteBatch, pos, wallTargetPos, Color.Orange * 0.5f, 0, 5);
|
||||
}
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 60.0f, $"{SelectedAiTarget.Entity.ToString()} ({GetTargetMemory(SelectedAiTarget).Priority.FormatZeroDecimal()})", GUI.Style.Red, Color.Black);
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 40.0f, $"({targetValue.FormatZeroDecimal()})", GUI.Style.Red, Color.Black);
|
||||
}
|
||||
|
||||
/*GUI.Font.DrawString(spriteBatch, targetValue.ToString(), pos - Vector2.UnitY * 80.0f, GUI.Style.Red);
|
||||
GUI.Font.DrawString(spriteBatch, "updatetargets: " + MathUtils.Round(updateTargetsTimer, 0.1f), pos - Vector2.UnitY * 100.0f, GUI.Style.Red);
|
||||
GUI.Font.DrawString(spriteBatch, "cooldown: " + MathUtils.Round(coolDownTimer, 0.1f), pos - Vector2.UnitY * 120.0f, GUI.Style.Red);*/
|
||||
|
||||
Color stateColor = Color.White;
|
||||
switch (State)
|
||||
{
|
||||
case AIState.Attack:
|
||||
stateColor = IsCoolDownRunning ? Color.Orange : GUI.Style.Red;
|
||||
break;
|
||||
case AIState.Escape:
|
||||
stateColor = Color.LightBlue;
|
||||
break;
|
||||
case AIState.Flee:
|
||||
stateColor = Color.White;
|
||||
break;
|
||||
case AIState.Eat:
|
||||
stateColor = Color.Brown;
|
||||
break;
|
||||
}
|
||||
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black);
|
||||
|
||||
if (LatchOntoAI != null)
|
||||
{
|
||||
foreach (Joint attachJoint in LatchOntoAI.AttachJoints)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch,
|
||||
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorA.X, -attachJoint.WorldAnchorA.Y)),
|
||||
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), GUI.Style.Green, 0, 4);
|
||||
}
|
||||
|
||||
if (LatchOntoAI.WallAttachPos.HasValue)
|
||||
{
|
||||
//GUI.DrawLine(spriteBatch, pos,
|
||||
// ConvertUnits.ToDisplayUnits(new Vector2(LatchOntoAI.WallAttachPos.Value.X, -LatchOntoAI.WallAttachPos.Value.Y)), GUI.Style.Green, 0, 3);
|
||||
}
|
||||
}
|
||||
|
||||
if (steeringManager is IndoorsSteeringManager pathSteering)
|
||||
{
|
||||
var path = pathSteering.CurrentPath;
|
||||
if (path != null)
|
||||
{
|
||||
if (path.CurrentNode != null)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch, pos,
|
||||
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
|
||||
Color.DarkViolet, 0, 3);
|
||||
|
||||
GUI.DrawString(spriteBatch, pos - new Vector2(0, 100), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
|
||||
}
|
||||
for (int i = 1; i < path.Nodes.Count; i++)
|
||||
{
|
||||
var previousNode = path.Nodes[i - 1];
|
||||
var currentNode = path.Nodes[i];
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y),
|
||||
new Vector2(previousNode.DrawPosition.X, -previousNode.DrawPosition.Y),
|
||||
GUI.Style.Red * 0.5f, 0, 3);
|
||||
|
||||
GUI.SmallFont.DrawString(spriteBatch,
|
||||
currentNode.ID.ToString(),
|
||||
new Vector2(currentNode.DrawPosition.X - 10, -currentNode.DrawPosition.Y - 30),
|
||||
GUI.Style.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (steeringManager.AvoidDir.LengthSquared() > 0.0001f)
|
||||
{
|
||||
Vector2 hitPos = ConvertUnits.ToDisplayUnits(steeringManager.AvoidRayCastHitPosition);
|
||||
hitPos.Y = -hitPos.Y;
|
||||
|
||||
GUI.DrawLine(spriteBatch, hitPos, hitPos + new Vector2(steeringManager.AvoidDir.X, -steeringManager.AvoidDir.Y) * 100, GUI.Style.Red, width: 5);
|
||||
//GUI.DrawLine(spriteBatch, pos, ConvertUnits.ToDisplayUnits(steeringManager.AvoidLookAheadPos.X, -steeringManager.AvoidLookAheadPos.Y), Color.Orange, width: 4);
|
||||
}
|
||||
}
|
||||
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2);
|
||||
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using FarseerPhysics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class HumanAIController : AIController
|
||||
{
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
/*if (GameMain.GameSession != null && GameMain.GameSession.CrewManager != null)
|
||||
{
|
||||
CurrentOrder = Order.GetPrefab("dismissed");
|
||||
objectiveManager.SetOrder(CurrentOrder, "", null);
|
||||
GameMain.GameSession.CrewManager.SetCharacterOrder(Character, CurrentOrder, null, null);
|
||||
}*/
|
||||
}
|
||||
|
||||
public override void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
|
||||
{
|
||||
Vector2 pos = Character.WorldPosition;
|
||||
pos.Y = -pos.Y;
|
||||
Vector2 textOffset = new Vector2(-40, -160);
|
||||
|
||||
if (SelectedAiTarget?.Entity != null)
|
||||
{
|
||||
//GUI.DrawLine(spriteBatch, pos, new Vector2(SelectedAiTarget.WorldPosition.X, -SelectedAiTarget.WorldPosition.Y), GUI.Style.Red);
|
||||
//GUI.DrawString(spriteBatch, pos + textOffset, $"AI TARGET: {SelectedAiTarget.Entity.ToString()}", Color.White, Color.Black);
|
||||
}
|
||||
|
||||
GUI.DrawString(spriteBatch, pos + textOffset, Character.Name, Color.White, Color.Black);
|
||||
|
||||
if (ObjectiveManager != null)
|
||||
{
|
||||
var currentOrder = ObjectiveManager.CurrentOrder;
|
||||
if (currentOrder != null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"ORDER: {currentOrder.DebugTag} ({currentOrder.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
else if (ObjectiveManager.WaitTimer > 0)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + new Vector2(0, 20), $"Waiting... {ObjectiveManager.WaitTimer.FormatZeroDecimal()}", Color.White, Color.Black);
|
||||
}
|
||||
var currentObjective = ObjectiveManager.CurrentObjective;
|
||||
if (currentObjective != null)
|
||||
{
|
||||
if (currentOrder == null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 20), $"MAIN OBJECTIVE: {currentObjective.DebugTag} ({currentObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
var subObjective = currentObjective.CurrentSubObjective;
|
||||
if (subObjective != null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 40), $"SUBOBJECTIVE: {subObjective.DebugTag} ({subObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
var activeObjective = ObjectiveManager.GetActiveObjective();
|
||||
if (activeObjective != null)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 60), $"ACTIVE OBJECTIVE: {activeObjective.DebugTag} ({activeObjective.Priority.FormatZeroDecimal()})", Color.White, Color.Black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (steeringManager is IndoorsSteeringManager pathSteering)
|
||||
{
|
||||
var path = pathSteering.CurrentPath;
|
||||
if (path != null)
|
||||
{
|
||||
for (int i = 1; i < path.Nodes.Count; i++)
|
||||
{
|
||||
var previousNode = path.Nodes[i - 1];
|
||||
var currentNode = path.Nodes[i];
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(currentNode.DrawPosition.X, -currentNode.DrawPosition.Y),
|
||||
new Vector2(previousNode.DrawPosition.X, -previousNode.DrawPosition.Y),
|
||||
Color.Blue * 0.5f, 0, 3);
|
||||
|
||||
GUI.SmallFont.DrawString(spriteBatch,
|
||||
currentNode.ID.ToString(),
|
||||
new Vector2(currentNode.DrawPosition.X - 10, -currentNode.DrawPosition.Y - 30),
|
||||
Color.Blue);
|
||||
}
|
||||
if (path.CurrentNode != null)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch, pos,
|
||||
new Vector2(path.CurrentNode.DrawPosition.X, -path.CurrentNode.DrawPosition.Y),
|
||||
Color.BlueViolet, 0, 3);
|
||||
|
||||
GUI.DrawString(spriteBatch, pos + textOffset + new Vector2(0, 80), "Path cost: " + path.Cost.FormatZeroDecimal(), Color.White, Color.Black * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Character.AnimController.TargetMovement.X, -Character.AnimController.TargetMovement.Y)), Color.SteelBlue, width: 2);
|
||||
GUI.DrawLine(spriteBatch, pos, pos + ConvertUnits.ToDisplayUnits(new Vector2(Steering.X, -Steering.Y)), Color.Blue, width: 3);
|
||||
|
||||
//if (Character.IsKeyDown(InputType.Aim))
|
||||
//{
|
||||
// GUI.DrawLine(spriteBatch, pos, new Vector2(Character.CursorWorldPosition.X, -Character.CursorWorldPosition.Y), Color.Yellow, width: 4);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,576 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.SpriteDeformations;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.Particles;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
abstract partial class Ragdoll
|
||||
{
|
||||
public HashSet<SpriteDeformation> SpriteDeformations { get; protected set; } = new HashSet<SpriteDeformation>();
|
||||
|
||||
/// <summary>
|
||||
/// Inversed draw order, which is used for drawing the limbs in 3d (deformable sprites).
|
||||
/// </summary>
|
||||
protected Limb[] inversedLimbDrawOrder;
|
||||
|
||||
partial void UpdateNetPlayerPositionProjSpecific(float deltaTime, float lowestSubPos)
|
||||
{
|
||||
if (character != GameMain.Client.Character || !character.CanMove)
|
||||
{
|
||||
//remove states without a timestamp (there may still be ID-based states
|
||||
//in the list when the controlled character switches to timestamp-based interpolation)
|
||||
character.MemState.RemoveAll(m => m.Timestamp == 0.0f);
|
||||
|
||||
//use simple interpolation for other players' characters and characters that can't move
|
||||
if (character.MemState.Count > 0)
|
||||
{
|
||||
CharacterStateInfo serverPos = character.MemState.Last();
|
||||
if (!character.isSynced)
|
||||
{
|
||||
SetPosition(serverPos.Position, false);
|
||||
Collider.LinearVelocity = Vector2.Zero;
|
||||
character.MemLocalState.Clear();
|
||||
character.LastNetworkUpdateID = serverPos.ID;
|
||||
character.isSynced = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (character.MemState[0].SelectedCharacter == null || character.MemState[0].SelectedCharacter.Removed)
|
||||
{
|
||||
character.DeselectCharacter();
|
||||
}
|
||||
else if (character.MemState[0].SelectedCharacter != null)
|
||||
{
|
||||
character.SelectCharacter(character.MemState[0].SelectedCharacter);
|
||||
}
|
||||
|
||||
if (character.MemState[0].SelectedItem == null || character.MemState[0].SelectedItem.Removed)
|
||||
{
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (character.SelectedConstruction != character.MemState[0].SelectedItem)
|
||||
{
|
||||
foreach (var ic in character.MemState[0].SelectedItem.Components)
|
||||
{
|
||||
if (ic.CanBeSelected) ic.Select(character);
|
||||
}
|
||||
}
|
||||
character.SelectedConstruction = character.MemState[0].SelectedItem;
|
||||
}
|
||||
|
||||
if (character.MemState[0].Animation == AnimController.Animation.CPR)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||
}
|
||||
else if (character.AnimController.Anim == AnimController.Animation.CPR)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
}
|
||||
|
||||
Vector2 newVelocity = Collider.LinearVelocity;
|
||||
Vector2 newPosition = Collider.SimPosition;
|
||||
float newRotation = Collider.Rotation;
|
||||
float newAngularVelocity = Collider.AngularVelocity;
|
||||
Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity);
|
||||
|
||||
newVelocity = newVelocity.ClampLength(100.0f);
|
||||
if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; }
|
||||
overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero;
|
||||
|
||||
Collider.LinearVelocity = newVelocity;
|
||||
Collider.AngularVelocity = newAngularVelocity;
|
||||
|
||||
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
|
||||
float errorTolerance = character.CanMove ? 0.01f : 0.2f;
|
||||
if (distSqrd > errorTolerance)
|
||||
{
|
||||
if (distSqrd > 10.0f || !character.CanMove)
|
||||
{
|
||||
Collider.TargetRotation = newRotation;
|
||||
SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Collider.TargetRotation = newRotation;
|
||||
Collider.TargetPosition = newPosition;
|
||||
Collider.MoveToTargetPosition(true);
|
||||
}
|
||||
}
|
||||
|
||||
//immobilized characters can't correct their position using AnimController movement
|
||||
// -> we need to correct it manually
|
||||
if (!character.CanMove)
|
||||
{
|
||||
float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition);
|
||||
float mainLimbErrorTolerance = 0.1f;
|
||||
//if the main limb is roughly at the correct position and the collider isn't moving (much at least),
|
||||
//don't attempt to correct the position.
|
||||
if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f)
|
||||
{
|
||||
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
|
||||
MainLimb.PullJointEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
character.MemLocalState.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
//remove states with a timestamp (there may still timestamp-based states
|
||||
//in the list if the controlled character switches from timestamp-based interpolation to ID-based)
|
||||
character.MemState.RemoveAll(m => m.Timestamp > 0.0f);
|
||||
|
||||
for (int i = 0; i < character.MemLocalState.Count; i++)
|
||||
{
|
||||
if (character.Submarine == null)
|
||||
{
|
||||
//transform in-sub coordinates to outside coordinates
|
||||
if (character.MemLocalState[i].Position.Y > lowestSubPos)
|
||||
{
|
||||
character.MemLocalState[i].TransformInToOutside();
|
||||
}
|
||||
}
|
||||
else if (currentHull?.Submarine != null)
|
||||
{
|
||||
//transform outside coordinates to in-sub coordinates
|
||||
if (character.MemLocalState[i].Position.Y < lowestSubPos)
|
||||
{
|
||||
character.MemLocalState[i].TransformOutToInside(currentHull.Submarine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (character.MemState.Count < 1) return;
|
||||
|
||||
overrideTargetMovement = Vector2.Zero;
|
||||
|
||||
CharacterStateInfo serverPos = character.MemState.Last();
|
||||
|
||||
if (!character.isSynced)
|
||||
{
|
||||
SetPosition(serverPos.Position, false);
|
||||
Collider.LinearVelocity = Vector2.Zero;
|
||||
character.MemLocalState.Clear();
|
||||
character.LastNetworkUpdateID = serverPos.ID;
|
||||
character.isSynced = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int localPosIndex = character.MemLocalState.FindIndex(m => m.ID == serverPos.ID);
|
||||
if (localPosIndex > -1)
|
||||
{
|
||||
CharacterStateInfo localPos = character.MemLocalState[localPosIndex];
|
||||
|
||||
//the entity we're interacting with doesn't match the server's
|
||||
if (localPos.SelectedCharacter != serverPos.SelectedCharacter)
|
||||
{
|
||||
if (serverPos.SelectedCharacter == null || serverPos.SelectedCharacter.Removed)
|
||||
{
|
||||
character.DeselectCharacter();
|
||||
}
|
||||
else if (serverPos.SelectedCharacter != null)
|
||||
{
|
||||
character.SelectCharacter(serverPos.SelectedCharacter);
|
||||
}
|
||||
}
|
||||
if (localPos.SelectedItem != serverPos.SelectedItem)
|
||||
{
|
||||
if (serverPos.SelectedItem == null || serverPos.SelectedItem.Removed)
|
||||
{
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
else if (serverPos.SelectedItem != null)
|
||||
{
|
||||
if (character.SelectedConstruction != serverPos.SelectedItem)
|
||||
{
|
||||
serverPos.SelectedItem.TryInteract(character, true, true);
|
||||
}
|
||||
character.SelectedConstruction = serverPos.SelectedItem;
|
||||
}
|
||||
}
|
||||
|
||||
if (localPos.Animation != serverPos.Animation)
|
||||
{
|
||||
if (serverPos.Animation == AnimController.Animation.CPR)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||
}
|
||||
else if (character.AnimController.Anim == AnimController.Animation.CPR)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
}
|
||||
}
|
||||
|
||||
Hull serverHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(serverPos.Position), character.CurrentHull, serverPos.Position.Y < lowestSubPos);
|
||||
Hull clientHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(localPos.Position), serverHull, localPos.Position.Y < lowestSubPos);
|
||||
|
||||
if (serverHull != null && clientHull != null && serverHull.Submarine != clientHull.Submarine)
|
||||
{
|
||||
//hull subs don't match => teleport the camera to the other sub
|
||||
character.Submarine = serverHull.Submarine;
|
||||
character.CurrentHull = CurrentHull = serverHull;
|
||||
SetPosition(serverPos.Position);
|
||||
character.MemLocalState.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 positionError = serverPos.Position - localPos.Position;
|
||||
float rotationError = serverPos.Rotation.HasValue && localPos.Rotation.HasValue ?
|
||||
serverPos.Rotation.Value - localPos.Rotation.Value :
|
||||
0.0f;
|
||||
|
||||
for (int i = localPosIndex; i < character.MemLocalState.Count; i++)
|
||||
{
|
||||
Hull pointHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(character.MemLocalState[i].Position), clientHull, character.MemLocalState[i].Position.Y < lowestSubPos);
|
||||
if (pointHull != clientHull && ((pointHull == null) || (clientHull == null) || (pointHull.Submarine == clientHull.Submarine))) break;
|
||||
character.MemLocalState[i].Translate(positionError, rotationError);
|
||||
}
|
||||
|
||||
float errorMagnitude = positionError.Length();
|
||||
if (errorMagnitude > 0.5f)
|
||||
{
|
||||
character.MemLocalState.Clear();
|
||||
SetPosition(serverPos.Position, lerp: true, ignorePlatforms: false);
|
||||
}
|
||||
else if (errorMagnitude > 0.01f)
|
||||
{
|
||||
Collider.TargetPosition = Collider.SimPosition + positionError;
|
||||
Collider.TargetRotation = Collider.Rotation + rotationError;
|
||||
Collider.MoveToTargetPosition(lerp: true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (character.MemLocalState.Count > 120) character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120);
|
||||
character.MemState.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
partial void ImpactProjSpecific(float impact, Body body)
|
||||
{
|
||||
float volume = MathHelper.Clamp(impact - 3.0f, 0.5f, 1.0f);
|
||||
|
||||
if (body.UserData is Limb limb && character.Stun <= 0f)
|
||||
{
|
||||
if (impact > 3.0f) { PlayImpactSound(limb); }
|
||||
}
|
||||
else if (body.UserData is Limb || body == Collider.FarseerBody)
|
||||
{
|
||||
if (!character.IsRemotePlayer && impact > ImpactTolerance)
|
||||
{
|
||||
SoundPlayer.PlayDamageSound("LimbBlunt", strongestImpact, Collider);
|
||||
}
|
||||
}
|
||||
if (Character.Controlled == character)
|
||||
{
|
||||
GameMain.GameScreen.Cam.Shake = Math.Min(Math.Max(strongestImpact, GameMain.GameScreen.Cam.Shake), 3.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayImpactSound(Limb limb)
|
||||
{
|
||||
limb.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
if (!string.IsNullOrWhiteSpace(limb.HitSoundTag))
|
||||
{
|
||||
bool inWater = limb.inWater;
|
||||
if (character.CurrentHull != null &&
|
||||
character.CurrentHull.Surface > character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height &&
|
||||
limb.SimPosition.Y < ConvertUnits.ToSimUnits(character.CurrentHull.Rect.Y - character.CurrentHull.Rect.Height) + limb.body.GetMaxExtent())
|
||||
{
|
||||
inWater = true;
|
||||
}
|
||||
SoundPlayer.PlaySound(inWater ? "footstep_water" : limb.HitSoundTag, limb.WorldPosition, hullGuess: character.CurrentHull);
|
||||
}
|
||||
foreach (WearableSprite wearable in limb.WearingItems)
|
||||
{
|
||||
if (limb.type == wearable.Limb && !string.IsNullOrWhiteSpace(wearable.Sound))
|
||||
{
|
||||
SoundPlayer.PlaySound(wearable.Sound, limb.WorldPosition, hullGuess: character.CurrentHull);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void Splash(Limb limb, Hull limbHull)
|
||||
{
|
||||
//create a splash particle
|
||||
for (int i = 0; i < MathHelper.Clamp(Math.Abs(limb.LinearVelocity.Y), 1.0f, 5.0f); i++)
|
||||
{
|
||||
var splash = GameMain.ParticleManager.CreateParticle("watersplash",
|
||||
new Vector2(limb.WorldPosition.X, limbHull.WorldSurface),
|
||||
new Vector2(0.0f, Math.Abs(-limb.LinearVelocity.Y * 20.0f)) + Rand.Vector(Math.Abs(limb.LinearVelocity.Y * 10)),
|
||||
Rand.Range(0.0f, MathHelper.TwoPi), limbHull);
|
||||
|
||||
if (splash != null)
|
||||
{
|
||||
splash.Size *= MathHelper.Clamp(Math.Abs(limb.LinearVelocity.Y) * 0.1f, 1.0f, 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
GameMain.ParticleManager.CreateParticle("bubbles",
|
||||
new Vector2(limb.WorldPosition.X, limbHull.WorldSurface),
|
||||
limb.LinearVelocity * 0.001f,
|
||||
0.0f, limbHull);
|
||||
|
||||
//if the Character dropped into water, create a wave
|
||||
if (limb.LinearVelocity.Y < 0.0f)
|
||||
{
|
||||
if (splashSoundTimer <= 0.0f)
|
||||
{
|
||||
SoundPlayer.PlaySplashSound(limb.WorldPosition, Math.Abs(limb.LinearVelocity.Y) + Rand.Range(-5.0f, 0.0f));
|
||||
splashSoundTimer = 0.5f;
|
||||
}
|
||||
|
||||
//+ some extra bubbles to follow the character underwater
|
||||
GameMain.ParticleManager.CreateParticle("bubbles",
|
||||
new Vector2(limb.WorldPosition.X, limbHull.WorldSurface),
|
||||
limb.LinearVelocity * 10.0f,
|
||||
0.0f, limbHull);
|
||||
}
|
||||
}
|
||||
|
||||
partial void SetupDrawOrder()
|
||||
{
|
||||
//make sure every character gets drawn at a distinct "layer"
|
||||
//(instead of having some of the limbs appear behind and some in front of other characters)
|
||||
float startDepth = 0.1f;
|
||||
float increment = 0.001f;
|
||||
foreach (Character otherCharacter in Character.CharacterList)
|
||||
{
|
||||
if (otherCharacter == character) continue;
|
||||
startDepth += increment;
|
||||
}
|
||||
//make sure each limb has a distinct depth value
|
||||
List<Limb> depthSortedLimbs = Limbs.OrderBy(l => l.ActiveSprite == null ? 0.0f : l.ActiveSprite.Depth).ToList();
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb.ActiveSprite != null)
|
||||
limb.ActiveSprite.Depth = startDepth + depthSortedLimbs.IndexOf(limb) * 0.00001f;
|
||||
}
|
||||
depthSortedLimbs.Reverse();
|
||||
inversedLimbDrawOrder = depthSortedLimbs.ToArray();
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime, Camera cam)
|
||||
{
|
||||
if (!character.IsVisible) { return; }
|
||||
|
||||
LimbJoints.ForEach(j => j.UpdateDeformations(deltaTime));
|
||||
foreach (var deformation in SpriteDeformations)
|
||||
{
|
||||
if (character.IsDead && deformation.Params.StopWhenHostIsDead) { continue; }
|
||||
if (deformation.Params.UseMovementSine)
|
||||
{
|
||||
if (this is AnimController animator)
|
||||
{
|
||||
deformation.Phase = MathUtils.WrapAngleTwoPi(animator.WalkPos * deformation.Params.Frequency + MathHelper.Pi * deformation.Params.SineOffset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
deformation.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void FlipProjSpecific()
|
||||
{
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb == null || limb.IsSevered || limb.ActiveSprite == null) continue;
|
||||
|
||||
Vector2 spriteOrigin = limb.ActiveSprite.Origin;
|
||||
spriteOrigin.X = limb.ActiveSprite.SourceRect.Width - spriteOrigin.X;
|
||||
limb.ActiveSprite.Origin = spriteOrigin;
|
||||
}
|
||||
}
|
||||
|
||||
partial void SeverLimbJointProjSpecific(LimbJoint limbJoint, bool playSound)
|
||||
{
|
||||
foreach (Limb limb in new Limb[] { limbJoint.LimbA, limbJoint.LimbB })
|
||||
{
|
||||
float gibParticleAmount = MathHelper.Clamp(limb.Mass / character.AnimController.Mass, 0.1f, 1.0f);
|
||||
foreach (ParticleEmitter emitter in character.GibEmitters)
|
||||
{
|
||||
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) continue;
|
||||
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) continue;
|
||||
|
||||
emitter.Emit(1.0f, limb.WorldPosition, character.CurrentHull, amountMultiplier: gibParticleAmount);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(character.BloodDecalName))
|
||||
{
|
||||
character.CurrentHull?.AddDecal(character.BloodDecalName, limb.WorldPosition, MathHelper.Clamp(limb.Mass, 0.5f, 2.0f));
|
||||
}
|
||||
}
|
||||
|
||||
if (playSound)
|
||||
{
|
||||
SoundPlayer.PlayDamageSound("Gore", 1.0f, limbJoint.LimbA.body);
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
if (simplePhysicsEnabled) { return; }
|
||||
|
||||
Collider.UpdateDrawPosition();
|
||||
|
||||
if (Limbs == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Ragdoll.Draw:LimbsRemoved",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"Failed to draw a ragdoll, limbs have been removed. Character: \"" + character.Name + "\", removed: " + character.Removed + "\n" + Environment.StackTrace);
|
||||
return;
|
||||
}
|
||||
|
||||
Color? color = null;
|
||||
if (character.ExternalHighlight)
|
||||
{
|
||||
color = Color.Lerp(Color.White, GUI.Style.Orange, (float)Math.Sin(Timing.TotalTime * 3.5f));
|
||||
}
|
||||
|
||||
float depthOffset = GetDepthOffset();
|
||||
for (int i = 0; i < limbs.Length; i++)
|
||||
{
|
||||
if (depthOffset != 0.0f) { inversedLimbDrawOrder[i].ActiveSprite.Depth += depthOffset; }
|
||||
inversedLimbDrawOrder[i].Draw(spriteBatch, cam, color);
|
||||
if (depthOffset != 0.0f) { inversedLimbDrawOrder[i].ActiveSprite.Depth -= depthOffset; }
|
||||
}
|
||||
LimbJoints.ForEach(j => j.Draw(spriteBatch));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Offset added to the default draw depth of the character's limbs. For example, climbing on ladders affects the depth of the character to get it to render behind the ladders.
|
||||
/// </summary>
|
||||
public float GetDepthOffset()
|
||||
{
|
||||
float depthOffset = 0.0f;
|
||||
var ladder = character.SelectedConstruction?.GetComponent<Ladder>();
|
||||
if (ladder != null)
|
||||
{
|
||||
float maxDepth = 0.0f;
|
||||
float minDepth = 1.0f;
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
var activeSprite = limb.ActiveSprite;
|
||||
if (activeSprite != null)
|
||||
{
|
||||
maxDepth = Math.Max(activeSprite.Depth, maxDepth);
|
||||
minDepth = Math.Min(activeSprite.Depth, minDepth);
|
||||
}
|
||||
}
|
||||
if (character.WorldPosition.X < character.SelectedConstruction.WorldPosition.X)
|
||||
{
|
||||
//at the left side of the ladder, needs to be drawn in front of the rungs
|
||||
depthOffset = Math.Max(ladder.BackgroundSpriteDepth - 0.01f - maxDepth, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
//at the right side of the ladder, needs to be drawn behind the rungs
|
||||
depthOffset = Math.Max(ladder.BackgroundSpriteDepth + 0.01f - minDepth, 0.0f);
|
||||
}
|
||||
}
|
||||
return depthOffset;
|
||||
}
|
||||
|
||||
public void DebugDraw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!GameMain.DebugDraw || !character.Enabled) return;
|
||||
if (simplePhysicsEnabled) return;
|
||||
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb.PullJointEnabled)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(limb.PullJointWorldAnchorA);
|
||||
if (currentHull?.Submarine != null) pos += currentHull.Submarine.DrawPosition;
|
||||
pos.Y = -pos.Y;
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)pos.Y, 5, 5), GUI.Style.Red, true, 0.01f);
|
||||
}
|
||||
|
||||
limb.body.DebugDraw(spriteBatch, inWater ? (currentHull == null ? Color.Blue : Color.Cyan) : Color.White);
|
||||
}
|
||||
|
||||
Collider.DebugDraw(spriteBatch, frozen ? GUI.Style.Red : (inWater ? Color.SkyBlue : Color.Gray));
|
||||
GUI.Font.DrawString(spriteBatch, Collider.LinearVelocity.X.FormatSingleDecimal(), new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y), Color.Orange);
|
||||
|
||||
foreach (RevoluteJoint joint in LimbJoints)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorA);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true);
|
||||
|
||||
pos = ConvertUnits.ToDisplayUnits(joint.WorldAnchorB);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), Color.White, true);
|
||||
}
|
||||
|
||||
foreach (Limb limb in Limbs)
|
||||
{
|
||||
if (limb.body.TargetPosition != null)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits((Vector2)limb.body.TargetPosition);
|
||||
if (currentHull?.Submarine != null) pos += currentHull.Submarine.DrawPosition;
|
||||
pos.Y = -pos.Y;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X - 10, (int)pos.Y - 10, 20, 20), Color.Cyan, false, 0.01f);
|
||||
GUI.DrawLine(spriteBatch, pos, new Vector2(limb.WorldPosition.X, -limb.WorldPosition.Y), Color.Cyan);
|
||||
}
|
||||
}
|
||||
|
||||
if (this is HumanoidAnimController humanoid)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(humanoid.RightHandIKPos);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
|
||||
pos = ConvertUnits.ToDisplayUnits(humanoid.LeftHandIKPos);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 4, 4), GUI.Style.Green, true);
|
||||
}
|
||||
|
||||
if (character.MemState.Count > 1)
|
||||
{
|
||||
Vector2 prevPos = ConvertUnits.ToDisplayUnits(character.MemState[0].Position);
|
||||
if (currentHull?.Submarine != null) prevPos += currentHull.Submarine.DrawPosition;
|
||||
prevPos.Y = -prevPos.Y;
|
||||
|
||||
for (int i = 1; i < character.MemState.Count; i++)
|
||||
{
|
||||
Vector2 currPos = ConvertUnits.ToDisplayUnits(character.MemState[i].Position);
|
||||
if (currentHull?.Submarine != null) currPos += currentHull.Submarine.DrawPosition;
|
||||
currPos.Y = -currPos.Y;
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)currPos.X - 3, (int)currPos.Y - 3, 6, 6), Color.Cyan * 0.6f, true, 0.01f);
|
||||
GUI.DrawLine(spriteBatch, prevPos, currPos, Color.Cyan * 0.6f, 0, 3);
|
||||
|
||||
prevPos = currPos;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentHull != null)
|
||||
{
|
||||
Vector2 displayFloorPos = ConvertUnits.ToDisplayUnits(new Vector2(Collider.SimPosition.X, floorY));
|
||||
if (currentHull?.Submarine != null) { displayFloorPos += currentHull.Submarine.DrawPosition; }
|
||||
displayFloorPos.Y = -displayFloorPos.Y;
|
||||
GUI.DrawLine(spriteBatch, displayFloorPos, displayFloorPos + new Vector2(floorNormal.X, -floorNormal.Y) * 50.0f, Color.Cyan * 0.5f, 0, 2);
|
||||
}
|
||||
|
||||
if (IgnorePlatforms)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y),
|
||||
new Vector2(Collider.DrawPosition.X, -Collider.DrawPosition.Y + 50),
|
||||
Color.Orange, 0, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
878
Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs
Normal file
878
Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs
Normal file
@@ -0,0 +1,878 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Particles;
|
||||
using Barotrauma.Sounds;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Character
|
||||
{
|
||||
public static bool DisableControls;
|
||||
|
||||
public static bool DebugDrawInteract;
|
||||
|
||||
protected float soundTimer;
|
||||
protected float soundInterval;
|
||||
protected float hudInfoTimer;
|
||||
protected bool hudInfoVisible;
|
||||
|
||||
private float pressureParticleTimer;
|
||||
|
||||
private float findFocusedTimer;
|
||||
|
||||
protected float lastRecvPositionUpdateTime;
|
||||
|
||||
private float hudInfoHeight;
|
||||
|
||||
private List<CharacterSound> sounds;
|
||||
|
||||
public bool ExternalHighlight;
|
||||
|
||||
/// <summary>
|
||||
/// Is the character currently visible on the camera. Refresh the value by calling DoVisibilityCheck.
|
||||
/// </summary>
|
||||
public bool IsVisible
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = true;
|
||||
|
||||
//the Character that the player is currently controlling
|
||||
private static Character controlled;
|
||||
|
||||
public static Character Controlled
|
||||
{
|
||||
get { return controlled; }
|
||||
set
|
||||
{
|
||||
if (controlled == value) return;
|
||||
controlled = value;
|
||||
if (controlled != null) controlled.Enabled = true;
|
||||
CharacterHealth.OpenHealthWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<object, HUDProgressBar> hudProgressBars;
|
||||
private readonly List<KeyValuePair<object, HUDProgressBar>> progressBarRemovals = new List<KeyValuePair<object, HUDProgressBar>>();
|
||||
|
||||
public Dictionary<object, HUDProgressBar> HUDProgressBars
|
||||
{
|
||||
get { return hudProgressBars; }
|
||||
}
|
||||
|
||||
private float blurStrength;
|
||||
public float BlurStrength
|
||||
{
|
||||
get { return blurStrength; }
|
||||
set { blurStrength = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
}
|
||||
|
||||
private float distortStrength;
|
||||
public float DistortStrength
|
||||
{
|
||||
get { return distortStrength; }
|
||||
set { distortStrength = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
}
|
||||
|
||||
private float radialDistortStrength;
|
||||
public float RadialDistortStrength
|
||||
{
|
||||
get { return radialDistortStrength; }
|
||||
set { radialDistortStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
}
|
||||
|
||||
private float chromaticAberrationStrength;
|
||||
public float ChromaticAberrationStrength
|
||||
{
|
||||
get { return chromaticAberrationStrength; }
|
||||
set { chromaticAberrationStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
}
|
||||
|
||||
public string BloodDecalName => Params.BloodDecal;
|
||||
|
||||
private readonly List<ParticleEmitter> bloodEmitters = new List<ParticleEmitter>();
|
||||
public IEnumerable<ParticleEmitter> BloodEmitters
|
||||
{
|
||||
get { return bloodEmitters; }
|
||||
}
|
||||
|
||||
private readonly List<ParticleEmitter> damageEmitters = new List<ParticleEmitter>();
|
||||
public IEnumerable<ParticleEmitter> DamageEmitters
|
||||
{
|
||||
get { return damageEmitters; }
|
||||
}
|
||||
|
||||
private readonly List<ParticleEmitter> gibEmitters = new List<ParticleEmitter>();
|
||||
public IEnumerable<ParticleEmitter> GibEmitters
|
||||
{
|
||||
get { return gibEmitters; }
|
||||
}
|
||||
|
||||
public class ObjectiveEntity
|
||||
{
|
||||
public Entity Entity;
|
||||
public Sprite Sprite;
|
||||
public Color Color;
|
||||
|
||||
public ObjectiveEntity(Entity entity, Sprite sprite, Color? color = null)
|
||||
{
|
||||
Entity = entity;
|
||||
Sprite = sprite;
|
||||
if (color.HasValue)
|
||||
{
|
||||
Color = color.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Color = Color.White;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ObjectiveEntity> activeObjectiveEntities = new List<ObjectiveEntity>();
|
||||
public IEnumerable<ObjectiveEntity> ActiveObjectiveEntities
|
||||
{
|
||||
get { return activeObjectiveEntities; }
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement mainElement)
|
||||
{
|
||||
soundInterval = mainElement.GetAttributeFloat("soundinterval", 10.0f);
|
||||
soundTimer = Rand.Range(0.0f, soundInterval);
|
||||
|
||||
sounds = new List<CharacterSound>();
|
||||
Params.Sounds.ForEach(s => sounds.Add(new CharacterSound(s)));
|
||||
|
||||
foreach (XElement subElement in mainElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "damageemitter":
|
||||
damageEmitters.Add(new ParticleEmitter(subElement));
|
||||
break;
|
||||
case "bloodemitter":
|
||||
bloodEmitters.Add(new ParticleEmitter(subElement));
|
||||
break;
|
||||
case "gibemitter":
|
||||
gibEmitters.Add(new ParticleEmitter(subElement));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
hudProgressBars = new Dictionary<object, HUDProgressBar>();
|
||||
}
|
||||
|
||||
partial void UpdateLimbLightSource(Limb limb)
|
||||
{
|
||||
if (limb.LightSource != null)
|
||||
{
|
||||
limb.LightSource.Enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
private bool wasFiring;
|
||||
|
||||
/// <summary>
|
||||
/// Control the Character according to player input
|
||||
/// </summary>
|
||||
public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
|
||||
{
|
||||
if (DisableControls || GUI.PauseMenuOpen || GUI.SettingsMenuOpen)
|
||||
{
|
||||
foreach (Key key in keys)
|
||||
{
|
||||
if (key == null) continue;
|
||||
key.Reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
wasFiring |= keys[(int)InputType.Aim].Held && keys[(int)InputType.Shoot].Held;
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
keys[i].SetState();
|
||||
}
|
||||
//if we were firing (= pressing the aim and shoot keys at the same time)
|
||||
//and the fire key is the same as Select or Use, reset the key to prevent accidentally selecting/using items
|
||||
if (wasFiring && !keys[(int)InputType.Shoot].Held)
|
||||
{
|
||||
if (GameMain.Config.KeyBind(InputType.Shoot).Equals(GameMain.Config.KeyBind(InputType.Select)))
|
||||
{
|
||||
keys[(int)InputType.Select].Reset();
|
||||
}
|
||||
if (GameMain.Config.KeyBind(InputType.Shoot).Equals(GameMain.Config.KeyBind(InputType.Use)))
|
||||
{
|
||||
keys[(int)InputType.Use].Reset();
|
||||
}
|
||||
wasFiring = false;
|
||||
}
|
||||
|
||||
float targetOffsetAmount = 0.0f;
|
||||
if (moveCam)
|
||||
{
|
||||
if (NeedsAir &&
|
||||
pressureProtection < 80.0f &&
|
||||
(AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 0.0f))
|
||||
{
|
||||
float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;
|
||||
if (pressure > 0.0f)
|
||||
{
|
||||
float zoomInEffectStrength = MathHelper.Clamp(pressure / 100.0f, 0.1f, 1.0f);
|
||||
cam.Zoom = MathHelper.Lerp(cam.Zoom,
|
||||
cam.DefaultZoom + (Math.Max(pressure, 10) / 150.0f) * Rand.Range(0.9f, 1.1f),
|
||||
zoomInEffectStrength);
|
||||
|
||||
pressureParticleTimer += pressure * deltaTime;
|
||||
if (pressureParticleTimer > 10.0f)
|
||||
{
|
||||
Particle p = GameMain.ParticleManager.CreateParticle("waterblood", WorldPosition + Rand.Vector(5.0f), Rand.Vector(10.0f));
|
||||
pressureParticleTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsHumanoid)
|
||||
{
|
||||
cam.OffsetAmount = 250.0f;// MathHelper.Lerp(cam.OffsetAmount, 250.0f, deltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
//increased visibility range when controlling large a non-humanoid
|
||||
cam.OffsetAmount = MathHelper.Clamp(Mass, 250.0f, 1500.0f);
|
||||
}
|
||||
}
|
||||
|
||||
cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
if (AnimController.CurrentHull?.Submarine != null)
|
||||
{
|
||||
cursorPosition -= AnimController.CurrentHull.Submarine.Position;
|
||||
}
|
||||
|
||||
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
|
||||
if (GUI.PauseMenuOpen)
|
||||
{
|
||||
cam.OffsetAmount = targetOffsetAmount = 0.0f;
|
||||
}
|
||||
else if (Lights.LightManager.ViewTarget is Item item && item.Prefab.FocusOnSelected)
|
||||
{
|
||||
cam.OffsetAmount = targetOffsetAmount = item.Prefab.OffsetOnSelected;
|
||||
}
|
||||
else if (SelectedConstruction != null && ViewTarget == null &&
|
||||
SelectedConstruction.Components.Any(ic => ic?.GuiFrame != null && ic.ShouldDrawHUD(this)))
|
||||
{
|
||||
cam.OffsetAmount = targetOffsetAmount = 0.0f;
|
||||
cursorPosition =
|
||||
SelectedConstruction.Position +
|
||||
new Vector2(cursorPosition.X % 10.0f, cursorPosition.Y % 10.0f); //apply a little bit of movement to the cursor pos to prevent AFK kicking
|
||||
}
|
||||
else if (!GameMain.Config.EnableMouseLook)
|
||||
{
|
||||
cam.OffsetAmount = targetOffsetAmount = 0.0f;
|
||||
}
|
||||
else if (Lights.LightManager.ViewTarget == this)
|
||||
{
|
||||
if (GUI.PauseMenuOpen || IsUnconscious)
|
||||
{
|
||||
if (deltaTime > 0.0f)
|
||||
{
|
||||
cam.OffsetAmount = targetOffsetAmount = 0.0f;
|
||||
}
|
||||
}
|
||||
else if (Vector2.DistanceSquared(AnimController.Limbs[0].SimPosition, mouseSimPos) > 1.0f)
|
||||
{
|
||||
Body body = Submarine.CheckVisibility(AnimController.Limbs[0].SimPosition, mouseSimPos);
|
||||
Structure structure = body?.UserData as Structure;
|
||||
|
||||
float sightDist = Submarine.LastPickedFraction;
|
||||
if (body?.UserData is Structure && !((Structure)body.UserData).CastShadow)
|
||||
{
|
||||
sightDist = 1.0f;
|
||||
}
|
||||
targetOffsetAmount = Math.Max(250.0f, sightDist * 500.0f);
|
||||
}
|
||||
}
|
||||
|
||||
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, targetOffsetAmount, 0.05f);
|
||||
DoInteractionUpdate(deltaTime, mouseSimPos);
|
||||
}
|
||||
|
||||
if (!GUI.PauseMenuOpen && !GUI.SettingsMenuOpen)
|
||||
{
|
||||
if (SelectedConstruction != null &&
|
||||
(SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)) ||
|
||||
((ViewTarget as Item)?.Prefab.FocusOnSelected ?? false) && PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)))
|
||||
{
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
//emulate a Select input to get the character to deselect the item server-side
|
||||
//keys[(int)InputType.Select].Hit = true;
|
||||
keys[(int)InputType.Deselect].Hit = true;
|
||||
}
|
||||
//reset focus to prevent us from accidentally interacting with another entity
|
||||
focusedItem = null;
|
||||
FocusedCharacter = null;
|
||||
findFocusedTimer = 0.2f;
|
||||
SelectedConstruction = null;
|
||||
}
|
||||
}
|
||||
|
||||
DisableControls = false;
|
||||
}
|
||||
|
||||
partial void UpdateControlled(float deltaTime, Camera cam)
|
||||
{
|
||||
if (controlled != this) return;
|
||||
|
||||
ControlLocalPlayer(deltaTime, cam);
|
||||
|
||||
Lights.LightManager.ViewTarget = this;
|
||||
CharacterHUD.Update(deltaTime, this, cam);
|
||||
|
||||
if (hudProgressBars.Any())
|
||||
{
|
||||
foreach (var progressBar in hudProgressBars)
|
||||
{
|
||||
if (progressBar.Value.FadeTimer <= 0.0f)
|
||||
{
|
||||
progressBarRemovals.Add(progressBar);
|
||||
continue;
|
||||
}
|
||||
progressBar.Value.Update(deltaTime);
|
||||
}
|
||||
if (progressBarRemovals.Any())
|
||||
{
|
||||
progressBarRemovals.ForEach(pb => hudProgressBars.Remove(pb.Key));
|
||||
progressBarRemovals.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnAttackedProjSpecific(Character attacker, AttackResult attackResult)
|
||||
{
|
||||
if (attackResult.Damage <= 1.0f || IsDead) { return; }
|
||||
if (soundTimer < soundInterval * 0.5f)
|
||||
{
|
||||
PlaySound(CharacterSound.SoundType.Damage);
|
||||
soundTimer = soundInterval;
|
||||
}
|
||||
}
|
||||
|
||||
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && controlled == this)
|
||||
{
|
||||
string chatMessage = CauseOfDeath.Type == CauseOfDeathType.Affliction ?
|
||||
CauseOfDeath.Affliction.SelfCauseOfDeathDescription :
|
||||
TextManager.Get("Self_CauseOfDeathDescription." + CauseOfDeath.Type.ToString(), fallBackTag: "Self_CauseOfDeathDescription.Damage");
|
||||
|
||||
if (GameMain.Client != null) chatMessage += " " + TextManager.Get("DeathChatNotification");
|
||||
|
||||
GameMain.NetworkMember.AddChatMessage(chatMessage, ChatMessageType.Dead);
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
controlled = null;
|
||||
}
|
||||
|
||||
PlaySound(CharacterSound.SoundType.Die);
|
||||
}
|
||||
|
||||
partial void DisposeProjSpecific()
|
||||
{
|
||||
if (controlled == this) controlled = null;
|
||||
|
||||
if (GameMain.GameSession?.CrewManager != null &&
|
||||
GameMain.GameSession.CrewManager.GetCharacters().Contains(this))
|
||||
{
|
||||
GameMain.GameSession.CrewManager.RemoveCharacter(this);
|
||||
}
|
||||
|
||||
if (GameMain.Client?.Character == this) GameMain.Client.Character = null;
|
||||
|
||||
if (Lights.LightManager.ViewTarget == this) Lights.LightManager.ViewTarget = null;
|
||||
}
|
||||
|
||||
|
||||
private List<Item> debugInteractablesInRange = new List<Item>();
|
||||
private List<Item> debugInteractablesAtCursor = new List<Item>();
|
||||
private List<Pair<Item, float>> debugInteractablesNearCursor = new List<Pair<Item, float>>();
|
||||
|
||||
/// <summary>
|
||||
/// Finds the front (lowest depth) interactable item at a position. "Interactable" in this case means that the character can "reach" the item.
|
||||
/// </summary>
|
||||
/// <param name="character">The Character who is looking for the interactable item, only items that are close enough to this character are returned</param>
|
||||
/// <param name="simPosition">The item at the simPosition, with the lowest depth, is returned</param>
|
||||
/// <param name="allowFindingNearestItem">If this is true and an item cannot be found at simPosition then a nearest item will be returned if possible</param>
|
||||
/// <param name="hull">If a hull is specified, only items within that hull are returned</param>
|
||||
public Item FindItemAtPosition(Vector2 simPosition, float aimAssistModifier = 0.0f, Item[] ignoredItems = null)
|
||||
{
|
||||
if (Submarine != null)
|
||||
{
|
||||
simPosition += Submarine.SimPosition;
|
||||
}
|
||||
|
||||
debugInteractablesInRange.Clear();
|
||||
debugInteractablesAtCursor.Clear();
|
||||
debugInteractablesNearCursor.Clear();
|
||||
|
||||
bool draggingItemToWorld = CharacterInventory.DraggingItemToWorld;
|
||||
|
||||
//reduce the amount of aim assist if an item has been selected
|
||||
//= can't switch selection to another item without deselecting the current one first UNLESS the cursor is directly on the item
|
||||
//otherwise it would be too easy to accidentally switch the selected item when rewiring items
|
||||
float aimAssistAmount = SelectedConstruction == null ? 100.0f * aimAssistModifier : 1.0f;
|
||||
|
||||
Vector2 displayPosition = ConvertUnits.ToDisplayUnits(simPosition);
|
||||
|
||||
//use the list of visible entities if it exists
|
||||
var entityList = Submarine.VisibleEntities ?? Item.ItemList;
|
||||
|
||||
Item closestItem = null;
|
||||
float closestItemDistance = Math.Max(aimAssistAmount, 2.0f);
|
||||
foreach (MapEntity entity in entityList)
|
||||
{
|
||||
if (!(entity is Item item))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (item.body != null && !item.body.Enabled) continue;
|
||||
if (item.ParentInventory != null) continue;
|
||||
if (ignoredItems != null && ignoredItems.Contains(item)) continue;
|
||||
|
||||
if (draggingItemToWorld)
|
||||
{
|
||||
if (item.OwnInventory == null ||
|
||||
!item.OwnInventory.CanBePut(CharacterInventory.draggingItem) ||
|
||||
!CanAccessInventory(item.OwnInventory))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
float distanceToItem = float.PositiveInfinity;
|
||||
if (item.IsInsideTrigger(displayPosition, out Rectangle transformedTrigger))
|
||||
{
|
||||
debugInteractablesAtCursor.Add(item);
|
||||
//distance is between 0-1 when the cursor is directly on the item
|
||||
distanceToItem =
|
||||
Math.Abs(transformedTrigger.Center.X - displayPosition.X) / transformedTrigger.Width +
|
||||
Math.Abs((transformedTrigger.Y - transformedTrigger.Height / 2.0f) - displayPosition.Y) / transformedTrigger.Height;
|
||||
//modify the distance based on the size of the trigger (preferring smaller items)
|
||||
distanceToItem *= MathHelper.Lerp(0.05f, 2.0f, (transformedTrigger.Width + transformedTrigger.Height) / 250.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Rectangle itemDisplayRect = new Rectangle(item.InteractionRect.X, item.InteractionRect.Y - item.InteractionRect.Height, item.InteractionRect.Width, item.InteractionRect.Height);
|
||||
|
||||
if (itemDisplayRect.Contains(displayPosition))
|
||||
{
|
||||
debugInteractablesAtCursor.Add(item);
|
||||
//distance is between 0-1 when the cursor is directly on the item
|
||||
distanceToItem =
|
||||
Math.Abs(itemDisplayRect.Center.X - displayPosition.X) / itemDisplayRect.Width +
|
||||
Math.Abs(itemDisplayRect.Center.Y - displayPosition.Y) / itemDisplayRect.Height;
|
||||
//modify the distance based on the size of the item (preferring smaller ones)
|
||||
distanceToItem *= MathHelper.Lerp(0.05f, 2.0f, (itemDisplayRect.Width + itemDisplayRect.Height) / 250.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (closestItemDistance < 2.0f) { continue; }
|
||||
//get the point on the itemDisplayRect which is closest to the cursor
|
||||
Vector2 rectIntersectionPoint = new Vector2(
|
||||
MathHelper.Clamp(displayPosition.X, itemDisplayRect.X, itemDisplayRect.Right),
|
||||
MathHelper.Clamp(displayPosition.Y, itemDisplayRect.Y, itemDisplayRect.Bottom));
|
||||
distanceToItem = 2.0f + Vector2.Distance(rectIntersectionPoint, displayPosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (distanceToItem > closestItemDistance) { continue; }
|
||||
if (!CanInteractWith(item)) { continue; }
|
||||
|
||||
debugInteractablesNearCursor.Add(new Pair<Item, float>(item, 1.0f - distanceToItem / (100.0f * aimAssistModifier)));
|
||||
closestItem = item;
|
||||
closestItemDistance = distanceToItem;
|
||||
}
|
||||
|
||||
return closestItem;
|
||||
}
|
||||
|
||||
private Character FindCharacterAtPosition(Vector2 mouseSimPos, float maxDist = 150.0f)
|
||||
{
|
||||
Character closestCharacter = null;
|
||||
float closestDist = 0.0f;
|
||||
|
||||
maxDist = ConvertUnits.ToSimUnits(maxDist);
|
||||
|
||||
foreach (Character c in CharacterList)
|
||||
{
|
||||
if (!CanInteractWith(c, checkVisibility: false)) continue;
|
||||
|
||||
float dist = Vector2.DistanceSquared(mouseSimPos, c.SimPosition);
|
||||
if (dist < maxDist * maxDist && (closestCharacter == null || dist < closestDist))
|
||||
{
|
||||
closestCharacter = c;
|
||||
closestDist = dist;
|
||||
}
|
||||
|
||||
/*FarseerPhysics.Common.Transform transform;
|
||||
c.AnimController.Collider.FarseerBody.GetTransform(out transform);
|
||||
for (int i = 0; i < c.AnimController.Collider.FarseerBody.FixtureList.Count; i++)
|
||||
{
|
||||
if (c.AnimController.Collider.FarseerBody.FixtureList[i].Shape.TestPoint(ref transform, ref mouseSimPos))
|
||||
{
|
||||
Console.WriteLine("Hit: " + i);
|
||||
closestCharacter = c;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
return closestCharacter;
|
||||
}
|
||||
|
||||
public bool ShouldLockHud()
|
||||
{
|
||||
if (this != controlled) { return false; }
|
||||
|
||||
//lock if using a controller, except if we're also using a connection panel in the same item
|
||||
return
|
||||
SelectedConstruction != null &&
|
||||
SelectedConstruction?.GetComponent<Controller>()?.User == this &&
|
||||
SelectedConstruction?.GetComponent<ConnectionPanel>()?.User != this;
|
||||
}
|
||||
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime, Camera cam)
|
||||
{
|
||||
if (!enabled) { return; }
|
||||
|
||||
if (!IsDead && !IsUnconscious)
|
||||
{
|
||||
if (soundTimer > 0)
|
||||
{
|
||||
soundTimer -= deltaTime;
|
||||
}
|
||||
else if (AIController != null)
|
||||
{
|
||||
switch (AIController.State)
|
||||
{
|
||||
case AIState.Attack:
|
||||
PlaySound(CharacterSound.SoundType.Attack);
|
||||
break;
|
||||
default:
|
||||
PlaySound(CharacterSound.SoundType.Idle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info != null || Vitality < MaxVitality * 0.98f)
|
||||
{
|
||||
hudInfoTimer -= deltaTime;
|
||||
if (hudInfoTimer <= 0.0f)
|
||||
{
|
||||
if (controlled == null)
|
||||
{
|
||||
hudInfoVisible = true;
|
||||
}
|
||||
|
||||
//if the character is not in the camera view, the name can't be visible and we can avoid the expensive visibility checks
|
||||
else if (WorldPosition.X < cam.WorldView.X || WorldPosition.X > cam.WorldView.Right ||
|
||||
WorldPosition.Y > cam.WorldView.Y || WorldPosition.Y < cam.WorldView.Y - cam.WorldView.Height)
|
||||
{
|
||||
hudInfoVisible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Ideally it shouldn't send the character entirely if we can't see them but /shrug, this isn't the most hacker-proof game atm
|
||||
hudInfoVisible = controlled.CanSeeCharacter(this, controlled.ViewTarget == null ? controlled.WorldPosition : controlled.ViewTarget.WorldPosition);
|
||||
}
|
||||
hudInfoTimer = Rand.Range(0.5f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
CharacterHealth.UpdateClientSpecific(deltaTime);
|
||||
if (controlled == this)
|
||||
{
|
||||
CharacterHealth.UpdateHUD(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
partial void SetOrderProjSpecific(Order order, string orderOption)
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.DisplayCharacterOrder(this, order, orderOption);
|
||||
}
|
||||
|
||||
public static void AddAllToGUIUpdateList()
|
||||
{
|
||||
for (int i = 0; i < CharacterList.Count; i++)
|
||||
{
|
||||
CharacterList[i].AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AddToGUIUpdateList()
|
||||
{
|
||||
if (controlled == this)
|
||||
{
|
||||
CharacterHUD.AddToGUIUpdateList(this);
|
||||
CharacterHealth.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
|
||||
public void DoVisibilityCheck(Camera cam)
|
||||
{
|
||||
IsVisible = false;
|
||||
if (!Enabled || AnimController.SimplePhysicsEnabled) { return; }
|
||||
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
float maxExtent = ConvertUnits.ToDisplayUnits(limb.body.GetMaxExtent());
|
||||
if (limb.LightSource != null) { maxExtent = Math.Max(limb.LightSource.Range, maxExtent); }
|
||||
if (limb.body.DrawPosition.X < cam.WorldView.X - maxExtent || limb.body.DrawPosition.X > cam.WorldView.Right + maxExtent) { continue; }
|
||||
if (limb.body.DrawPosition.Y < cam.WorldView.Y - cam.WorldView.Height - maxExtent || limb.body.DrawPosition.Y > cam.WorldView.Y + maxExtent) { continue; }
|
||||
IsVisible = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
if (!Enabled) { return; }
|
||||
AnimController.Draw(spriteBatch, cam);
|
||||
}
|
||||
|
||||
public void DrawHUD(SpriteBatch spriteBatch, Camera cam, bool drawHealth = true)
|
||||
{
|
||||
CharacterHUD.Draw(spriteBatch, this, cam);
|
||||
if (drawHealth) CharacterHealth.DrawHUD(spriteBatch);
|
||||
}
|
||||
|
||||
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
if (!Enabled) { return; }
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
AnimController.DebugDraw(spriteBatch);
|
||||
}
|
||||
|
||||
if (GUI.DisableHUD) return;
|
||||
|
||||
if (Controlled != null &&
|
||||
Controlled != this &&
|
||||
Submarine != null &&
|
||||
Controlled.Submarine == Submarine &&
|
||||
GameMain.Config.LosMode != LosMode.None)
|
||||
{
|
||||
float yPos = Controlled.AnimController.FloorY - 1.5f;
|
||||
|
||||
if (Controlled.AnimController.Stairs != null)
|
||||
{
|
||||
yPos = Controlled.AnimController.Stairs.SimPosition.Y - Controlled.AnimController.Stairs.RectHeight * 0.5f;
|
||||
}
|
||||
|
||||
foreach (var ladder in Ladder.List)
|
||||
{
|
||||
if (CanInteractWith(ladder.Item) && Controlled.CanInteractWith(ladder.Item))
|
||||
{
|
||||
float xPos = ladder.Item.SimPosition.X;
|
||||
if (Math.Abs(xPos - SimPosition.X) < 3.0)
|
||||
{
|
||||
yPos = ladder.Item.SimPosition.Y - ladder.Item.RectHeight * 0.5f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (AnimController.FloorY < yPos) { return; }
|
||||
}
|
||||
|
||||
Vector2 pos = DrawPosition;
|
||||
pos.Y += hudInfoHeight;
|
||||
|
||||
if (CurrentHull != null && DrawPosition.Y > CurrentHull.WorldRect.Y - 130.0f)
|
||||
{
|
||||
float lowerAmount = DrawPosition.Y - (CurrentHull.WorldRect.Y - 130.0f);
|
||||
hudInfoHeight = MathHelper.Lerp(hudInfoHeight, 100.0f - lowerAmount, 0.1f);
|
||||
hudInfoHeight = Math.Max(hudInfoHeight, 20.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
hudInfoHeight = MathHelper.Lerp(hudInfoHeight, 100.0f, 0.1f);
|
||||
}
|
||||
|
||||
pos.Y = -pos.Y;
|
||||
|
||||
if (speechBubbleTimer > 0.0f)
|
||||
{
|
||||
GUI.SpeechBubbleIcon.Draw(spriteBatch, pos - Vector2.UnitY * 30,
|
||||
speechBubbleColor * Math.Min(speechBubbleTimer, 1.0f), 0.0f,
|
||||
Math.Min(speechBubbleTimer, 1.0f));
|
||||
}
|
||||
|
||||
if (this == controlled)
|
||||
{
|
||||
if (DebugDrawInteract)
|
||||
{
|
||||
Vector2 cursorPos = cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
cursorPos.Y = -cursorPos.Y;
|
||||
foreach (Item item in debugInteractablesAtCursor)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch, cursorPos,
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), Color.LightGreen, width: 4);
|
||||
}
|
||||
foreach (Item item in debugInteractablesInRange)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch, new Vector2(DrawPosition.X, -DrawPosition.Y),
|
||||
new Vector2(item.DrawPosition.X, -item.DrawPosition.Y), Color.White * 0.1f, width: 4);
|
||||
}
|
||||
foreach (Pair<Item, float> item in debugInteractablesNearCursor)
|
||||
{
|
||||
GUI.DrawLine(spriteBatch,
|
||||
cursorPos,
|
||||
new Vector2(item.First.DrawPosition.X, -item.First.DrawPosition.Y),
|
||||
ToolBox.GradientLerp(item.Second, GUI.Style.Red, GUI.Style.Orange, GUI.Style.Green), width: 2);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float hoverRange = 300.0f;
|
||||
float fadeOutRange = 200.0f;
|
||||
float cursorDist = Vector2.Distance(WorldPosition, cam.ScreenToWorld(PlayerInput.MousePosition));
|
||||
float hudInfoAlpha = MathHelper.Clamp(1.0f - (cursorDist - (hoverRange - fadeOutRange)) / fadeOutRange, 0.2f, 1.0f);
|
||||
|
||||
if (!GUI.DisableCharacterNames && hudInfoVisible && info != null &&
|
||||
(controlled == null || this != controlled.FocusedCharacter))
|
||||
{
|
||||
string name = Info.DisplayName;
|
||||
if (controlled == null && name != Info.Name) name += " " + TextManager.Get("Disguised");
|
||||
|
||||
Vector2 namePos = new Vector2(pos.X, pos.Y - 10.0f - (5.0f / cam.Zoom)) - GUI.Font.MeasureString(name) * 0.5f / cam.Zoom;
|
||||
|
||||
Vector2 screenSize = new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
Vector2 viewportSize = new Vector2(cam.WorldView.Width, cam.WorldView.Height);
|
||||
namePos.X -= cam.WorldView.X; namePos.Y += cam.WorldView.Y;
|
||||
namePos *= screenSize / viewportSize;
|
||||
namePos.X = (float)Math.Floor(namePos.X); namePos.Y = (float)Math.Floor(namePos.Y);
|
||||
namePos *= viewportSize / screenSize;
|
||||
namePos.X += cam.WorldView.X; namePos.Y -= cam.WorldView.Y;
|
||||
|
||||
Color nameColor = Color.White;
|
||||
if (Controlled != null && TeamID != Controlled.TeamID)
|
||||
{
|
||||
nameColor = TeamID == TeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
|
||||
}
|
||||
GUI.Font.DrawString(spriteBatch, name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
|
||||
GUI.Font.DrawString(spriteBatch, name, namePos, nameColor * hudInfoAlpha, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
GUI.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsDead) return;
|
||||
|
||||
if (CharacterHealth.DisplayedVitality < MaxVitality * 0.98f && hudInfoVisible)
|
||||
{
|
||||
hudInfoAlpha = Math.Max(hudInfoAlpha, Math.Min(CharacterHealth.DamageOverlayTimer, 1.0f));
|
||||
|
||||
Vector2 healthBarPos = new Vector2(pos.X - 50, -pos.Y);
|
||||
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f),
|
||||
CharacterHealth.DisplayedVitality / MaxVitality,
|
||||
Color.Lerp(GUI.Style.Red, GUI.Style.Green, CharacterHealth.DisplayedVitality / MaxVitality) * 0.8f * hudInfoAlpha,
|
||||
new Color(0.5f, 0.57f, 0.6f, 1.0f) * hudInfoAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a progress bar that's "linked" to the specified object (or updates an existing one if there's one already linked to the object)
|
||||
/// The progress bar will automatically fade out after 1 sec if the method hasn't been called during that time
|
||||
/// </summary>
|
||||
public HUDProgressBar UpdateHUDProgressBar(object linkedObject, Vector2 worldPosition, float progress, Color emptyColor, Color fullColor)
|
||||
{
|
||||
if (controlled != this) return null;
|
||||
|
||||
if (!hudProgressBars.TryGetValue(linkedObject, out HUDProgressBar progressBar))
|
||||
{
|
||||
progressBar = new HUDProgressBar(worldPosition, Submarine, emptyColor, fullColor);
|
||||
hudProgressBars.Add(linkedObject, progressBar);
|
||||
}
|
||||
|
||||
progressBar.WorldPosition = worldPosition;
|
||||
progressBar.FadeTimer = Math.Max(progressBar.FadeTimer, 1.0f);
|
||||
progressBar.Progress = progress;
|
||||
|
||||
return progressBar;
|
||||
}
|
||||
|
||||
private SoundChannel soundChannel;
|
||||
public void PlaySound(CharacterSound.SoundType soundType)
|
||||
{
|
||||
if (sounds == null || sounds.Count == 0) { return; }
|
||||
if (soundChannel != null && soundChannel.IsPlaying) { return; }
|
||||
|
||||
var matchingSounds = sounds.Where(s =>
|
||||
s.Type == soundType &&
|
||||
(s.Gender == Gender.None || (info != null && info.Gender == s.Gender)));
|
||||
if (!matchingSounds.Any()) { return; }
|
||||
|
||||
var matchingSoundsList = matchingSounds.ToList();
|
||||
var selectedSound = matchingSoundsList[Rand.Int(matchingSoundsList.Count)];
|
||||
soundChannel = SoundPlayer.PlaySound(selectedSound.Sound, AnimController.WorldPosition, selectedSound.Volume, selectedSound.Range, CurrentHull);
|
||||
soundTimer = soundInterval;
|
||||
}
|
||||
|
||||
public void AddActiveObjectiveEntity(Entity entity, Sprite sprite, Color? color = null)
|
||||
{
|
||||
if (activeObjectiveEntities.Any(aoe => aoe.Entity == entity)) return;
|
||||
ObjectiveEntity objectiveEntity = new ObjectiveEntity(entity, sprite, color);
|
||||
activeObjectiveEntities.Add(objectiveEntity);
|
||||
}
|
||||
|
||||
public void RemoveActiveObjectiveEntity(Entity entity)
|
||||
{
|
||||
ObjectiveEntity found = activeObjectiveEntities.Find(aoe => aoe.Entity == entity);
|
||||
if (found == null) return;
|
||||
activeObjectiveEntities.Remove(found);
|
||||
}
|
||||
|
||||
partial void ImplodeFX()
|
||||
{
|
||||
Vector2 centerOfMass = AnimController.GetCenterOfMass();
|
||||
|
||||
SoundPlayer.PlaySound("implode", WorldPosition);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
Particle p = GameMain.ParticleManager.CreateParticle("waterblood",
|
||||
WorldPosition + Rand.Vector(5.0f),
|
||||
Rand.Vector(10.0f));
|
||||
if (p != null) p.Size *= 2.0f;
|
||||
|
||||
GameMain.ParticleManager.CreateParticle("bubbles",
|
||||
ConvertUnits.ToDisplayUnits(centerOfMass) + Rand.Vector(5.0f),
|
||||
new Vector2(Rand.Range(-50f, 50f), Rand.Range(-100f, 50f)));
|
||||
|
||||
GameMain.ParticleManager.CreateParticle("gib",
|
||||
WorldPosition + Rand.Vector(Rand.Range(0.0f, 50.0f)),
|
||||
Rand.Range(0.0f, MathHelper.TwoPi),
|
||||
Rand.Range(200.0f, 700.0f), null);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 30; i++)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("heavygib",
|
||||
WorldPosition + Rand.Vector(Rand.Range(0.0f, 50.0f)),
|
||||
Rand.Range(0.0f, MathHelper.TwoPi),
|
||||
Rand.Range(50.0f, 500.0f), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class CharacterHUD
|
||||
{
|
||||
private static Dictionary<Entity, int> orderIndicatorCount = new Dictionary<Entity, int>();
|
||||
const float ItemOverlayDelay = 1.0f;
|
||||
private static Item focusedItem;
|
||||
private static float focusedItemOverlayTimer;
|
||||
|
||||
private static List<Item> brokenItems = new List<Item>();
|
||||
private static float brokenItemsCheckTimer;
|
||||
|
||||
private static Dictionary<string, string> cachedHudTexts = new Dictionary<string, string>();
|
||||
|
||||
private static GUIFrame hudFrame;
|
||||
public static GUIFrame HUDFrame
|
||||
{
|
||||
|
||||
get
|
||||
{
|
||||
if (hudFrame == null)
|
||||
{
|
||||
hudFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
return hudFrame;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ShouldDrawInventory(Character character)
|
||||
{
|
||||
return
|
||||
character?.Inventory != null &&
|
||||
character.AllowInput &&
|
||||
!character.LockHands &&
|
||||
character.SelectedConstruction?.GetComponent<Controller>()?.User != character;
|
||||
}
|
||||
|
||||
private static string GetCachedHudText(string textTag, string keyBind)
|
||||
{
|
||||
if (cachedHudTexts.TryGetValue(textTag + keyBind, out string text))
|
||||
{
|
||||
return text;
|
||||
}
|
||||
text = TextManager.GetWithVariable(textTag, "[key]", keyBind);
|
||||
cachedHudTexts.Add(textTag + keyBind, text);
|
||||
return text;
|
||||
}
|
||||
|
||||
public static void AddToGUIUpdateList(Character character)
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
|
||||
if (!character.IsUnconscious && character.Stun <= 0.0f)
|
||||
{
|
||||
if (character.Inventory != null)
|
||||
{
|
||||
for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
|
||||
{
|
||||
var item = character.Inventory.Items[i];
|
||||
if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) continue;
|
||||
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic.DrawHudWhenEquipped) ic.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (character.IsHumanoid && character.SelectedCharacter != null)
|
||||
{
|
||||
character.SelectedCharacter.CharacterHealth.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
|
||||
HUDFrame.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public static void Update(float deltaTime, Character character, Camera cam)
|
||||
{
|
||||
if (GUI.DisableHUD) { return; }
|
||||
|
||||
if (!character.IsUnconscious && character.Stun <= 0.0f)
|
||||
{
|
||||
if (character.Info != null && !character.ShouldLockHud() && character.SelectedCharacter == null)
|
||||
{
|
||||
bool mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && GUI.MouseOn == null;
|
||||
if (mouseOnPortrait && PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
CharacterHealth.OpenHealthWindow = character.CharacterHealth;
|
||||
}
|
||||
}
|
||||
|
||||
if (character.Inventory != null)
|
||||
{
|
||||
if (!LockInventory(character))
|
||||
{
|
||||
character.Inventory.Update(deltaTime, cam);
|
||||
}
|
||||
for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
|
||||
{
|
||||
var item = character.Inventory.Items[i];
|
||||
if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) continue;
|
||||
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic.DrawHudWhenEquipped) ic.UpdateHUD(character, deltaTime, cam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
|
||||
{
|
||||
if (character.SelectedCharacter.CanInventoryBeAccessed)
|
||||
{
|
||||
character.SelectedCharacter.Inventory.Update(deltaTime, cam);
|
||||
}
|
||||
character.SelectedCharacter.CharacterHealth.UpdateHUD(deltaTime);
|
||||
}
|
||||
|
||||
Inventory.UpdateDragging();
|
||||
}
|
||||
|
||||
if (focusedItem != null)
|
||||
{
|
||||
if (character.FocusedItem != null)
|
||||
{
|
||||
focusedItemOverlayTimer = Math.Min(focusedItemOverlayTimer + deltaTime, ItemOverlayDelay + 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
focusedItemOverlayTimer = Math.Max(focusedItemOverlayTimer - deltaTime, 0.0f);
|
||||
if (focusedItemOverlayTimer <= 0.0f) focusedItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (brokenItemsCheckTimer > 0.0f)
|
||||
{
|
||||
brokenItemsCheckTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
brokenItems.Clear();
|
||||
brokenItemsCheckTimer = 1.0f;
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (!item.Repairables.Any(r => item.ConditionPercentage <= r.AIRepairThreshold)) { continue; }
|
||||
if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item)) { continue; }
|
||||
|
||||
Vector2 diff = item.WorldPosition - character.WorldPosition;
|
||||
if (Submarine.CheckVisibility(character.SimPosition, character.SimPosition + ConvertUnits.ToSimUnits(diff)) == null)
|
||||
{
|
||||
brokenItems.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Draw(SpriteBatch spriteBatch, Character character, Camera cam)
|
||||
{
|
||||
if (GUI.DisableHUD) { return; }
|
||||
|
||||
character.CharacterHealth.Alignment = Alignment.Right;
|
||||
|
||||
if (GameMain.GameSession?.CrewManager != null)
|
||||
{
|
||||
orderIndicatorCount.Clear();
|
||||
foreach (Pair<Order, float> timedOrder in GameMain.GameSession.CrewManager.ActiveOrders)
|
||||
{
|
||||
DrawOrderIndicator(spriteBatch, cam, character, timedOrder.First, MathHelper.Clamp(timedOrder.Second / 10.0f, 0.2f, 1.0f));
|
||||
}
|
||||
|
||||
if (character.CurrentOrder != null)
|
||||
{
|
||||
DrawOrderIndicator(spriteBatch, cam, character, character.CurrentOrder, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Character.ObjectiveEntity objectiveEntity in character.ActiveObjectiveEntities)
|
||||
{
|
||||
DrawObjectiveIndicator(spriteBatch, cam, character, objectiveEntity, 1.0f);
|
||||
}
|
||||
|
||||
foreach (Item brokenItem in brokenItems)
|
||||
{
|
||||
float dist = Vector2.Distance(character.WorldPosition, brokenItem.WorldPosition);
|
||||
Vector2 drawPos = brokenItem.DrawPosition;
|
||||
float alpha = Math.Min((1000.0f - dist) / 1000.0f * 2.0f, 1.0f);
|
||||
if (alpha <= 0.0f) continue;
|
||||
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, GUI.BrokenIcon,
|
||||
Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha);
|
||||
}
|
||||
|
||||
if (!character.IsUnconscious && character.Stun <= 0.0f)
|
||||
{
|
||||
if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected)
|
||||
{
|
||||
DrawCharacterHoverTexts(spriteBatch, cam, character);
|
||||
}
|
||||
|
||||
float circleSize;
|
||||
if (character.FocusedItem != null)
|
||||
{
|
||||
if (focusedItem != character.FocusedItem)
|
||||
{
|
||||
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
|
||||
}
|
||||
focusedItem = character.FocusedItem;
|
||||
}
|
||||
|
||||
if (focusedItem != null && focusedItemOverlayTimer > ItemOverlayDelay)
|
||||
{
|
||||
Vector2 circlePos = cam.WorldToScreen(focusedItem.DrawPosition);
|
||||
circleSize = Math.Max(focusedItem.Rect.Width, focusedItem.Rect.Height) * 1.5f;
|
||||
circleSize = MathHelper.Clamp(circleSize, 45.0f, 100.0f) * Math.Min((focusedItemOverlayTimer - 1.0f) * 5.0f, 1.0f);
|
||||
if (circleSize > 0.0f)
|
||||
{
|
||||
Vector2 scale = new Vector2(circleSize / GUI.Style.FocusIndicator.FrameSize.X);
|
||||
GUI.Style.FocusIndicator.Draw(spriteBatch,
|
||||
(int)((focusedItemOverlayTimer - 1.0f) * GUI.Style.FocusIndicator.FrameCount * 3.0f),
|
||||
circlePos,
|
||||
Color.LightBlue * 0.3f,
|
||||
origin: GUI.Style.FocusIndicator.FrameSize.ToVector2() / 2,
|
||||
rotate: (float)Timing.TotalTime,
|
||||
scale: scale);
|
||||
}
|
||||
|
||||
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
|
||||
{
|
||||
var hudTexts = focusedItem.GetHUDTexts(character);
|
||||
|
||||
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
|
||||
|
||||
Vector2 textSize = GUI.Font.MeasureString(focusedItem.Name);
|
||||
Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusedItem.Name);
|
||||
|
||||
Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition);
|
||||
startPos.Y -= (hudTexts.Count + 1) * textSize.Y;
|
||||
if (focusedItem.Sprite != null)
|
||||
{
|
||||
startPos.X += (int)(circleSize * 0.4f * dir);
|
||||
startPos.Y -= (int)(circleSize * 0.4f);
|
||||
}
|
||||
|
||||
Vector2 textPos = startPos;
|
||||
if (dir == -1) { textPos.X -= largeTextSize.X; }
|
||||
|
||||
float alpha = MathHelper.Clamp((focusedItemOverlayTimer - ItemOverlayDelay) * 2.0f, 0.0f, 1.0f);
|
||||
|
||||
GUI.DrawString(spriteBatch, textPos, focusedItem.Name, GUI.Style.TextColor * alpha, Color.Black * alpha * 0.7f, 2, font: GUI.SubHeadingFont);
|
||||
startPos.X += dir * 10.0f * GUI.Scale;
|
||||
textPos.X += dir * 10.0f * GUI.Scale;
|
||||
textPos.Y += largeTextSize.Y;
|
||||
foreach (ColoredText coloredText in hudTexts)
|
||||
{
|
||||
if (dir == -1) textPos.X = (int)(startPos.X - GUI.SmallFont.MeasureString(coloredText.Text).X);
|
||||
GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUI.SmallFont);
|
||||
textPos.Y += textSize.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (HUDProgressBar progressBar in character.HUDProgressBars.Values)
|
||||
{
|
||||
progressBar.Draw(spriteBatch, cam);
|
||||
}
|
||||
}
|
||||
|
||||
if (character.SelectedConstruction != null &&
|
||||
(character.CanInteractWith(Character.Controlled.SelectedConstruction) || Screen.Selected == GameMain.SubEditorScreen))
|
||||
{
|
||||
character.SelectedConstruction.DrawHUD(spriteBatch, cam, Character.Controlled);
|
||||
}
|
||||
|
||||
if (character.Inventory != null)
|
||||
{
|
||||
for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
|
||||
{
|
||||
var item = character.Inventory.Items[i];
|
||||
if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) continue;
|
||||
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic.DrawHudWhenEquipped) ic.DrawHUD(spriteBatch, character);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool mouseOnPortrait = false;
|
||||
if (character.Stun <= 0.1f && !character.IsDead)
|
||||
{
|
||||
if (CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null)
|
||||
{
|
||||
if (character.Info != null && !character.ShouldLockHud())
|
||||
{
|
||||
character.Info.DrawBackground(spriteBatch);
|
||||
character.Info.DrawJobIcon(spriteBatch,
|
||||
new Rectangle(
|
||||
(int)(HUDLayoutSettings.BottomRightInfoArea.X + HUDLayoutSettings.BottomRightInfoArea.Width * 0.05f),
|
||||
(int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f),
|
||||
(int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2),
|
||||
(int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f)));
|
||||
character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true);
|
||||
}
|
||||
mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud();
|
||||
if (mouseOnPortrait)
|
||||
{
|
||||
GUI.UIGlow.Draw(spriteBatch, HUDLayoutSettings.BottomRightInfoArea, GUI.Style.Green * 0.5f);
|
||||
}
|
||||
}
|
||||
if (ShouldDrawInventory(character))
|
||||
{
|
||||
character.Inventory.Locked = LockInventory(character);
|
||||
character.Inventory.DrawOwn(spriteBatch);
|
||||
character.Inventory.CurrentLayout = CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null ?
|
||||
CharacterInventory.Layout.Default :
|
||||
CharacterInventory.Layout.Right;
|
||||
}
|
||||
}
|
||||
|
||||
if (!character.IsUnconscious && character.Stun <= 0.0f)
|
||||
{
|
||||
if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
|
||||
{
|
||||
if (character.SelectedCharacter.CanInventoryBeAccessed)
|
||||
{
|
||||
///character.Inventory.CurrentLayout = Alignment.Left;
|
||||
character.SelectedCharacter.Inventory.CurrentLayout = CharacterInventory.Layout.Left;
|
||||
character.SelectedCharacter.Inventory.DrawOwn(spriteBatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
//character.Inventory.CurrentLayout = (CharacterHealth.OpenHealthWindow == null) ? Alignment.Center : Alignment.Left;
|
||||
}
|
||||
if (CharacterHealth.OpenHealthWindow == character.SelectedCharacter.CharacterHealth)
|
||||
{
|
||||
character.SelectedCharacter.CharacterHealth.Alignment = Alignment.Left;
|
||||
character.SelectedCharacter.CharacterHealth.DrawStatusHUD(spriteBatch);
|
||||
}
|
||||
}
|
||||
else if (character.Inventory != null)
|
||||
{
|
||||
//character.Inventory.CurrentLayout = (CharacterHealth.OpenHealthWindow == null) ? Alignment.Center : Alignment.Left;
|
||||
}
|
||||
}
|
||||
|
||||
if (mouseOnPortrait)
|
||||
{
|
||||
GUIComponent.DrawToolTip(
|
||||
spriteBatch,
|
||||
character.Info?.Job == null ? character.DisplayName : character.Name + " (" + character.Info.Job.Name + ")",
|
||||
HUDLayoutSettings.PortraitArea);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawCharacterHoverTexts(SpriteBatch spriteBatch, Camera cam, Character character)
|
||||
{
|
||||
foreach (Item item in character.Inventory.Items)
|
||||
{
|
||||
var statusHUD = item?.GetComponent<StatusHUD>();
|
||||
if (statusHUD != null && statusHUD.IsActive && statusHUD.VisibleCharacters.Contains(character.FocusedCharacter))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 startPos = character.DrawPosition + (character.FocusedCharacter.DrawPosition - character.DrawPosition) * 0.7f;
|
||||
startPos = cam.WorldToScreen(startPos);
|
||||
|
||||
string focusName = character.FocusedCharacter.DisplayName;
|
||||
if (character.FocusedCharacter.Info != null)
|
||||
{
|
||||
focusName = character.FocusedCharacter.Info.DisplayName;
|
||||
}
|
||||
Vector2 textPos = startPos;
|
||||
Vector2 textSize = GUI.Font.MeasureString(focusName);
|
||||
Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusName);
|
||||
|
||||
textPos -= new Vector2(textSize.X / 2, textSize.Y);
|
||||
|
||||
Color nameColor = GUI.Style.TextColor;
|
||||
if (character.TeamID != character.FocusedCharacter.TeamID)
|
||||
{
|
||||
nameColor = character.FocusedCharacter.TeamID == Character.TeamType.FriendlyNPC ? Color.SkyBlue : GUI.Style.Red;
|
||||
}
|
||||
|
||||
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2, GUI.SubHeadingFont);
|
||||
textPos.X += 10.0f * GUI.Scale;
|
||||
textPos.Y += GUI.SubHeadingFont.MeasureString(focusName).Y;
|
||||
if (character.FocusedCharacter.CanBeDragged)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBindText(InputType.Grab)),
|
||||
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
|
||||
textPos.Y += largeTextSize.Y;
|
||||
}
|
||||
if (character.FocusedCharacter.CharacterHealth.UseHealthWindow && character.CanInteractWith(character.FocusedCharacter, 160f, false))
|
||||
{
|
||||
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("HealHint", GameMain.Config.KeyBindText(InputType.Health)),
|
||||
GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
|
||||
textPos.Y += textSize.Y;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(character.FocusedCharacter.customInteractHUDText))
|
||||
{
|
||||
GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.customInteractHUDText, GUI.Style.Green, Color.Black, 2, GUI.SmallFont);
|
||||
textPos.Y += textSize.Y;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool LockInventory(Character character)
|
||||
{
|
||||
if (character?.Inventory == null || !character.AllowInput || character.LockHands) { return true; }
|
||||
|
||||
return character.ShouldLockHud();
|
||||
}
|
||||
|
||||
private static void DrawOrderIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Order order, float iconAlpha = 1.0f)
|
||||
{
|
||||
if (order.TargetAllCharacters)
|
||||
{
|
||||
if (order.OrderGiver != character && !order.HasAppropriateJob(character))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Entity target = order.ConnectedController != null ? order.ConnectedController.Item : order.TargetEntity;
|
||||
if (target == null) { return; }
|
||||
|
||||
//don't show the indicator if far away and not inside the same sub
|
||||
//prevents exploiting the indicators in locating the sub
|
||||
if (character.Submarine != target.Submarine &&
|
||||
Vector2.DistanceSquared(character.WorldPosition, target.WorldPosition) > 1000.0f * 1000.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!orderIndicatorCount.ContainsKey(target)) { orderIndicatorCount.Add(target, 0); }
|
||||
|
||||
Vector2 drawPos = target.DrawPosition + Vector2.UnitX * order.SymbolSprite.size.X * 1.5f * orderIndicatorCount[target];
|
||||
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, order.SymbolSprite, order.Color * iconAlpha);
|
||||
|
||||
orderIndicatorCount[target] = orderIndicatorCount[target] + 1;
|
||||
}
|
||||
|
||||
private static void DrawObjectiveIndicator(SpriteBatch spriteBatch, Camera cam, Character character, Character.ObjectiveEntity objectiveEntity, float iconAlpha = 1.0f)
|
||||
{
|
||||
if (objectiveEntity == null) return;
|
||||
|
||||
Vector2 drawPos = objectiveEntity.Entity.WorldPosition;// + Vector2.UnitX * objectiveEntity.Sprite.size.X * 1.5f;
|
||||
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, objectiveEntity.Sprite, objectiveEntity.Color * iconAlpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class CharacterInfo
|
||||
{
|
||||
private static Sprite infoAreaPortraitBG;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
infoAreaPortraitBG = GUI.Style.GetComponentStyle("InfoAreaPortraitBG")?.Sprites[GUIComponent.ComponentState.None][0].Sprite;
|
||||
new Sprite("Content/UI/InventoryUIAtlas.png", new Rectangle(833, 298, 142, 98), null, 0);
|
||||
}
|
||||
|
||||
|
||||
public GUIFrame CreateInfoFrame(GUIFrame frame)
|
||||
{
|
||||
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), frame.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.1f) })
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.03f
|
||||
};
|
||||
|
||||
var headerArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.4f), paddedFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
new GUICustomComponent(new RectTransform(new Vector2(0.25f, 1.0f), headerArea.RectTransform),
|
||||
onDraw: (sb, component) => DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()));
|
||||
|
||||
ScalableFont font = paddedFrame.Rect.Width < 280 ? GUI.SmallFont : GUI.Font;
|
||||
|
||||
var headerTextArea = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 1.0f), headerArea.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f,
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
Color? nameColor = null;
|
||||
if (Job != null) { nameColor = Job.Prefab.UIColor; }
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform),
|
||||
Name, textColor: nameColor, font: GUI.LargeFont)
|
||||
{
|
||||
Padding = Vector4.Zero,
|
||||
AutoScaleHorizontal = true
|
||||
};
|
||||
|
||||
if (Job != null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform),
|
||||
Job.Name, textColor: Job.Prefab.UIColor, font: font);
|
||||
}
|
||||
|
||||
if (personalityTrait != null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), headerTextArea.RectTransform),
|
||||
TextManager.AddPunctuation(':', TextManager.Get("PersonalityTrait"), TextManager.Get("personalitytrait." + personalityTrait.Name.Replace(" ", ""))), font: font);
|
||||
}
|
||||
|
||||
//spacing
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.05f), paddedFrame.RectTransform), style: null);
|
||||
|
||||
if (Job != null)
|
||||
{
|
||||
var skills = Job.Skills;
|
||||
skills.Sort((s1, s2) => -s1.Level.CompareTo(s2.Level));
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
|
||||
TextManager.Get("Skills") + ":", font: font);
|
||||
|
||||
foreach (Skill skill in skills)
|
||||
{
|
||||
Color textColor = Color.White * (0.5f + skill.Level / 200.0f);
|
||||
|
||||
var skillName = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform),
|
||||
TextManager.Get("SkillName." + skill.Identifier), textColor: textColor, font: font);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), skillName.RectTransform),
|
||||
((int)skill.Level).ToString(), textColor: textColor, font: font, textAlignment: Alignment.CenterRight);
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
public GUIFrame CreateCharacterFrame(GUIComponent parent, string text, object userData)
|
||||
{
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(parent.Rect.Width, 40), parent.RectTransform) { IsFixedSize = false }, "ListBoxElement")
|
||||
{
|
||||
UserData = userData
|
||||
};
|
||||
|
||||
Color? textColor = null;
|
||||
if (Job != null) { textColor = Job.Prefab.UIColor; }
|
||||
|
||||
GUITextBlock textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(40, 0) }, text, textColor: textColor, font: GUI.SmallFont);
|
||||
new GUICustomComponent(new RectTransform(new Point(frame.Rect.Height, frame.Rect.Height), frame.RectTransform, Anchor.CenterLeft) { IsFixedSize = false },
|
||||
onDraw: (sb, component) => DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2()));
|
||||
return frame;
|
||||
}
|
||||
|
||||
partial void OnSkillChanged(string skillIdentifier, float prevLevel, float newLevel, Vector2 textPopupPos)
|
||||
{
|
||||
if (newLevel - prevLevel > 0.1f)
|
||||
{
|
||||
GUI.AddMessage(
|
||||
"+" + ((int)((newLevel - prevLevel) * 100.0f)).ToString() + " XP",
|
||||
GUI.Style.Green,
|
||||
textPopupPos,
|
||||
Vector2.UnitY * 10.0f);
|
||||
}
|
||||
else if (prevLevel % 0.1f > 0.05f && newLevel % 0.1f < 0.05f)
|
||||
{
|
||||
GUI.AddMessage(
|
||||
"+10 XP",
|
||||
GUI.Style.Green,
|
||||
textPopupPos,
|
||||
Vector2.UnitY * 10.0f);
|
||||
}
|
||||
|
||||
if ((int)newLevel > (int)prevLevel)
|
||||
{
|
||||
GUI.AddMessage(
|
||||
TextManager.GetWithVariables("SkillIncreased", new string[3] { "[name]", "[skillname]", "[newlevel]" },
|
||||
new string[3] { Name, TextManager.Get("SkillName." + skillIdentifier), ((int)newLevel).ToString() },
|
||||
new bool[3] { false, true, false }), GUI.Style.Green);
|
||||
}
|
||||
}
|
||||
|
||||
partial void LoadAttachmentSprites(bool omitJob)
|
||||
{
|
||||
if (attachmentSprites == null)
|
||||
{
|
||||
attachmentSprites = new List<WearableSprite>();
|
||||
}
|
||||
if (!IsAttachmentsLoaded)
|
||||
{
|
||||
LoadHeadAttachments();
|
||||
}
|
||||
FaceAttachment?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.FaceAttachment)));
|
||||
BeardElement?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Beard)));
|
||||
MoustacheElement?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Moustache)));
|
||||
HairElement?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.Hair)));
|
||||
if (omitJob)
|
||||
{
|
||||
JobPrefab.NoJobElement?.Element("PortraitClothing")?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.JobIndicator)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Job?.Prefab.ClothingElement?.Elements("sprite").ForEach(s => attachmentSprites.Add(new WearableSprite(s, WearableType.JobIndicator)));
|
||||
}
|
||||
}
|
||||
|
||||
// Doesn't work if the head's source rect does not start at 0,0.
|
||||
public static Point CalculateOffset(Sprite sprite, Point offset) => sprite.SourceRect.Size * offset;
|
||||
|
||||
public void CalculateHeadPosition(Sprite sprite)
|
||||
{
|
||||
if (sprite == null) { return; }
|
||||
if (Head.SheetIndex == null) { return; }
|
||||
Point location = CalculateOffset(sprite, Head.SheetIndex.Value.ToPoint());
|
||||
sprite.SourceRect = new Rectangle(location, sprite.SourceRect.Size);
|
||||
}
|
||||
|
||||
public void DrawBackground(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (infoAreaPortraitBG == null) { return; }
|
||||
infoAreaPortraitBG.Draw(spriteBatch, HUDLayoutSettings.BottomRightInfoArea.Location.ToVector2(), Color.White, Vector2.Zero, 0.0f,
|
||||
scale: new Vector2(
|
||||
HUDLayoutSettings.BottomRightInfoArea.Width / (float)infoAreaPortraitBG.SourceRect.Width,
|
||||
HUDLayoutSettings.BottomRightInfoArea.Height / (float)infoAreaPortraitBG.SourceRect.Height));
|
||||
}
|
||||
|
||||
public void DrawPortrait(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 offset, float targetWidth, bool flip = false)
|
||||
{
|
||||
if (Portrait != null)
|
||||
{
|
||||
// Scale down the head sprite 10%
|
||||
float scale = targetWidth * 0.9f / Portrait.size.X;
|
||||
if (Head.SheetIndex.HasValue)
|
||||
{
|
||||
Portrait.SourceRect = new Rectangle(CalculateOffset(Portrait, Head.SheetIndex.Value.ToPoint()), Portrait.SourceRect.Size);
|
||||
}
|
||||
Portrait.Draw(spriteBatch, screenPos + offset, Color.White, Portrait.Origin, scale: scale, spriteEffect: flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
|
||||
if (AttachmentSprites != null)
|
||||
{
|
||||
float depthStep = 0.000001f;
|
||||
foreach (var attachment in AttachmentSprites)
|
||||
{
|
||||
DrawAttachmentSprite(spriteBatch, attachment, Portrait, screenPos + offset, scale, depthStep, flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None);
|
||||
depthStep += depthStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawIcon(SpriteBatch spriteBatch, Vector2 screenPos, Vector2 targetAreaSize)
|
||||
{
|
||||
var headSprite = HeadSprite;
|
||||
if (headSprite != null)
|
||||
{
|
||||
float scale = Math.Min(targetAreaSize.X / headSprite.size.X, targetAreaSize.Y / headSprite.size.Y);
|
||||
if (Head.SheetIndex.HasValue)
|
||||
{
|
||||
headSprite.SourceRect = new Rectangle(CalculateOffset(headSprite, Head.SheetIndex.Value.ToPoint()), headSprite.SourceRect.Size);
|
||||
}
|
||||
headSprite.Draw(spriteBatch, screenPos, scale: scale);
|
||||
if (AttachmentSprites != null)
|
||||
{
|
||||
float depthStep = 0.000001f;
|
||||
foreach (var attachment in AttachmentSprites)
|
||||
{
|
||||
DrawAttachmentSprite(spriteBatch, attachment, headSprite, screenPos, scale, depthStep);
|
||||
depthStep += depthStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawJobIcon(SpriteBatch spriteBatch, Vector2 pos, float scale = 1.0f)
|
||||
{
|
||||
var icon = Job?.Prefab?.Icon;
|
||||
if (icon == null) { return; }
|
||||
icon.Draw(spriteBatch, pos, Job.Prefab.UIColor, scale: scale);
|
||||
}
|
||||
public void DrawJobIcon(SpriteBatch spriteBatch, Rectangle area)
|
||||
{
|
||||
var icon = Job?.Prefab?.Icon;
|
||||
if (icon == null) { return; }
|
||||
icon.Draw(spriteBatch,
|
||||
area.Center.ToVector2(),
|
||||
Job.Prefab.UIColor,
|
||||
scale: Math.Min(area.Width / (float)icon.SourceRect.Width, area.Height / (float)icon.SourceRect.Height));
|
||||
}
|
||||
|
||||
private void DrawAttachmentSprite(SpriteBatch spriteBatch, WearableSprite attachment, Sprite head, Vector2 drawPos, float scale, float depthStep, SpriteEffects spriteEffects = SpriteEffects.None)
|
||||
{
|
||||
if (attachment.InheritSourceRect)
|
||||
{
|
||||
if (attachment.SheetIndex.HasValue)
|
||||
{
|
||||
attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, attachment.SheetIndex.Value), head.SourceRect.Size);
|
||||
}
|
||||
else if (Head.SheetIndex.HasValue)
|
||||
{
|
||||
attachment.Sprite.SourceRect = new Rectangle(CalculateOffset(head, Head.SheetIndex.Value.ToPoint()), head.SourceRect.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
attachment.Sprite.SourceRect = head.SourceRect;
|
||||
}
|
||||
}
|
||||
Vector2 origin = attachment.Sprite.Origin;
|
||||
if (attachment.InheritOrigin)
|
||||
{
|
||||
origin = head.Origin;
|
||||
attachment.Sprite.Origin = origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
origin = attachment.Sprite.Origin;
|
||||
}
|
||||
float depth = attachment.Sprite.Depth;
|
||||
if (attachment.InheritLimbDepth)
|
||||
{
|
||||
depth = head.Depth - depthStep;
|
||||
}
|
||||
attachment.Sprite.Draw(spriteBatch, drawPos, Color.White, origin, rotate: 0, scale: scale, depth: depth, spriteEffect: spriteEffects);
|
||||
}
|
||||
|
||||
public static CharacterInfo ClientRead(string speciesName, IReadMessage inc)
|
||||
{
|
||||
ushort infoID = inc.ReadUInt16();
|
||||
string newName = inc.ReadString();
|
||||
int gender = inc.ReadByte();
|
||||
int race = inc.ReadByte();
|
||||
int headSpriteID = inc.ReadByte();
|
||||
int hairIndex = inc.ReadByte();
|
||||
int beardIndex = inc.ReadByte();
|
||||
int moustacheIndex = inc.ReadByte();
|
||||
int faceAttachmentIndex = inc.ReadByte();
|
||||
string ragdollFile = inc.ReadString();
|
||||
|
||||
string jobIdentifier = inc.ReadString();
|
||||
int variant = inc.ReadByte();
|
||||
|
||||
JobPrefab jobPrefab = null;
|
||||
Dictionary<string, float> skillLevels = new Dictionary<string, float>();
|
||||
if (!string.IsNullOrEmpty(jobIdentifier))
|
||||
{
|
||||
jobPrefab = JobPrefab.Get(jobIdentifier);
|
||||
byte skillCount = inc.ReadByte();
|
||||
for (int i = 0; i < skillCount; i++)
|
||||
{
|
||||
string skillIdentifier = inc.ReadString();
|
||||
float skillLevel = inc.ReadSingle();
|
||||
skillLevels.Add(skillIdentifier, skillLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: animations
|
||||
CharacterInfo ch = new CharacterInfo(speciesName, newName, jobPrefab, ragdollFile, variant)
|
||||
{
|
||||
ID = infoID,
|
||||
};
|
||||
ch.RecreateHead(headSpriteID,(Race)race, (Gender)gender, hairIndex, beardIndex, moustacheIndex, faceAttachmentIndex);
|
||||
if (ch.Job != null)
|
||||
{
|
||||
foreach (KeyValuePair<string, float> skill in skillLevels)
|
||||
{
|
||||
Skill matchingSkill = ch.Job.Skills.Find(s => s.Identifier == skill.Key);
|
||||
if (matchingSkill == null)
|
||||
{
|
||||
ch.Job.Skills.Add(new Skill(skill.Key, skill.Value));
|
||||
continue;
|
||||
}
|
||||
matchingSkill.Level = skill.Value;
|
||||
}
|
||||
ch.Job.Skills.RemoveAll(s => !skillLevels.ContainsKey(s.Identifier));
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Character
|
||||
{
|
||||
partial void UpdateNetInput()
|
||||
{
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
if (this != Controlled)
|
||||
{
|
||||
if (GameMain.Client.EndCinematic != null) // Freezes the characters during the ending cinematic
|
||||
{
|
||||
AnimController.Frozen = true;
|
||||
memState.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
//freeze AI characters if more than x seconds have passed since last update from the server
|
||||
if (lastRecvPositionUpdateTime < Lidgren.Network.NetTime.Now - NetConfig.FreezeCharacterIfPositionDataMissingDelay)
|
||||
{
|
||||
AnimController.Frozen = true;
|
||||
memState.Clear();
|
||||
//hide after y seconds
|
||||
if (lastRecvPositionUpdateTime < Lidgren.Network.NetTime.Now - NetConfig.DisableCharacterIfPositionDataMissingDelay)
|
||||
{
|
||||
Enabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
SimPosition,
|
||||
AnimController.Collider.Rotation,
|
||||
LastNetworkUpdateID,
|
||||
AnimController.TargetDir,
|
||||
SelectedCharacter,
|
||||
SelectedConstruction,
|
||||
AnimController.Anim);
|
||||
|
||||
memLocalState.Add(posInfo);
|
||||
|
||||
InputNetFlags newInput = InputNetFlags.None;
|
||||
if (IsKeyDown(InputType.Left)) newInput |= InputNetFlags.Left;
|
||||
if (IsKeyDown(InputType.Right)) newInput |= InputNetFlags.Right;
|
||||
if (IsKeyDown(InputType.Up)) newInput |= InputNetFlags.Up;
|
||||
if (IsKeyDown(InputType.Down)) newInput |= InputNetFlags.Down;
|
||||
if (IsKeyDown(InputType.Run)) newInput |= InputNetFlags.Run;
|
||||
if (IsKeyDown(InputType.Crouch)) newInput |= InputNetFlags.Crouch;
|
||||
if (IsKeyHit(InputType.Select)) newInput |= InputNetFlags.Select; //TODO: clean up the way this input is registered
|
||||
if (IsKeyHit(InputType.Deselect)) newInput |= InputNetFlags.Deselect;
|
||||
if (IsKeyHit(InputType.Health)) newInput |= InputNetFlags.Health;
|
||||
if (IsKeyHit(InputType.Grab)) newInput |= InputNetFlags.Grab;
|
||||
if (IsKeyDown(InputType.Use)) newInput |= InputNetFlags.Use;
|
||||
if (IsKeyDown(InputType.Aim)) newInput |= InputNetFlags.Aim;
|
||||
if (IsKeyDown(InputType.Shoot)) newInput |= InputNetFlags.Shoot;
|
||||
if (IsKeyDown(InputType.Attack)) newInput |= InputNetFlags.Attack;
|
||||
if (IsKeyDown(InputType.Ragdoll)) newInput |= InputNetFlags.Ragdoll;
|
||||
|
||||
if (AnimController.TargetDir == Direction.Left) newInput |= InputNetFlags.FacingLeft;
|
||||
|
||||
Vector2 relativeCursorPos = cursorPosition - AimRefPosition;
|
||||
relativeCursorPos.Normalize();
|
||||
UInt16 intAngle = (UInt16)(65535.0 * Math.Atan2(relativeCursorPos.Y, relativeCursorPos.X) / (2.0 * Math.PI));
|
||||
|
||||
NetInputMem newMem = new NetInputMem
|
||||
{
|
||||
states = newInput,
|
||||
intAim = intAngle
|
||||
};
|
||||
if (focusedItem != null && !CharacterInventory.DraggingItemToWorld &&
|
||||
(!newMem.states.HasFlag(InputNetFlags.Grab) && !newMem.states.HasFlag(InputNetFlags.Health)))
|
||||
{
|
||||
newMem.interact = focusedItem.ID;
|
||||
}
|
||||
else if (FocusedCharacter != null)
|
||||
{
|
||||
newMem.interact = FocusedCharacter.ID;
|
||||
}
|
||||
|
||||
memInput.Insert(0, newMem);
|
||||
LastNetworkUpdateID++;
|
||||
if (memInput.Count > 60)
|
||||
{
|
||||
memInput.RemoveRange(60, memInput.Count - 60);
|
||||
}
|
||||
}
|
||||
}
|
||||
else //this == Character.Controlled && GameMain.Client == null
|
||||
{
|
||||
AnimController.Frozen = false;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
{
|
||||
if (extraData != null)
|
||||
{
|
||||
switch ((NetEntityEvent.Type)extraData[0])
|
||||
{
|
||||
case NetEntityEvent.Type.InventoryState:
|
||||
msg.WriteRangedInteger(0, 0, 3);
|
||||
Inventory.ClientWrite(msg, extraData);
|
||||
break;
|
||||
case NetEntityEvent.Type.Treatment:
|
||||
msg.WriteRangedInteger(1, 0, 3);
|
||||
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
|
||||
break;
|
||||
case NetEntityEvent.Type.Status:
|
||||
msg.WriteRangedInteger(2, 0, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write((byte)ClientNetObject.CHARACTER_INPUT);
|
||||
|
||||
if (memInput.Count > 60)
|
||||
{
|
||||
memInput.RemoveRange(60, memInput.Count - 60);
|
||||
}
|
||||
|
||||
msg.Write(LastNetworkUpdateID);
|
||||
byte inputCount = Math.Min((byte)memInput.Count, (byte)60);
|
||||
msg.Write(inputCount);
|
||||
for (int i = 0; i < inputCount; i++)
|
||||
{
|
||||
msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal);
|
||||
msg.Write(memInput[i].intAim);
|
||||
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Deselect) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Use) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Health) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Grab))
|
||||
{
|
||||
msg.Write(memInput[i].interact);
|
||||
}
|
||||
}
|
||||
}
|
||||
msg.WritePadBits();
|
||||
}
|
||||
|
||||
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ServerNetObject.ENTITY_POSITION:
|
||||
bool facingRight = AnimController.Dir > 0.0f;
|
||||
|
||||
lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;
|
||||
|
||||
AnimController.Frozen = false;
|
||||
Enabled = true;
|
||||
|
||||
UInt16 networkUpdateID = 0;
|
||||
if (msg.ReadBoolean())
|
||||
{
|
||||
networkUpdateID = msg.ReadUInt16();
|
||||
}
|
||||
else
|
||||
{
|
||||
bool aimInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Aim].Held = aimInput;
|
||||
keys[(int)InputType.Aim].SetState(false, aimInput);
|
||||
|
||||
bool shootInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Shoot].Held = shootInput;
|
||||
keys[(int)InputType.Shoot].SetState(false, shootInput);
|
||||
|
||||
bool useInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Use].Held = useInput;
|
||||
keys[(int)InputType.Use].SetState(false, useInput);
|
||||
|
||||
if (AnimController is HumanoidAnimController)
|
||||
{
|
||||
bool crouching = msg.ReadBoolean();
|
||||
keys[(int)InputType.Crouch].Held = crouching;
|
||||
keys[(int)InputType.Crouch].SetState(false, crouching);
|
||||
}
|
||||
|
||||
bool attackInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Attack].Held = attackInput;
|
||||
keys[(int)InputType.Attack].SetState(false, attackInput);
|
||||
|
||||
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
|
||||
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
|
||||
TransformCursorPos();
|
||||
|
||||
bool ragdollInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Ragdoll].Held = ragdollInput;
|
||||
keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);
|
||||
|
||||
facingRight = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
bool entitySelected = msg.ReadBoolean();
|
||||
Character selectedCharacter = null;
|
||||
Item selectedItem = null;
|
||||
|
||||
AnimController.Animation animation = AnimController.Animation.None;
|
||||
if (entitySelected)
|
||||
{
|
||||
ushort characterID = msg.ReadUInt16();
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
selectedCharacter = FindEntityByID(characterID) as Character;
|
||||
selectedItem = FindEntityByID(itemID) as Item;
|
||||
if (characterID != NullEntityID)
|
||||
{
|
||||
bool doingCpr = msg.ReadBoolean();
|
||||
if (doingCpr && SelectedCharacter != null)
|
||||
{
|
||||
animation = AnimController.Animation.CPR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 pos = new Vector2(
|
||||
msg.ReadSingle(),
|
||||
msg.ReadSingle());
|
||||
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
|
||||
Vector2 linearVelocity = new Vector2(
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
|
||||
linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);
|
||||
|
||||
bool fixedRotation = msg.ReadBoolean();
|
||||
float? rotation = null;
|
||||
float? angularVelocity = null;
|
||||
if (!fixedRotation)
|
||||
{
|
||||
rotation = msg.ReadSingle();
|
||||
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
|
||||
angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
|
||||
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
|
||||
}
|
||||
|
||||
bool readStatus = msg.ReadBoolean();
|
||||
if (readStatus)
|
||||
{
|
||||
ReadStatus(msg);
|
||||
}
|
||||
|
||||
msg.ReadPadBits();
|
||||
|
||||
int index = 0;
|
||||
if (GameMain.Client.Character == this && CanMove)
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
networkUpdateID,
|
||||
facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
linearVelocity, angularVelocity,
|
||||
sendingTime, facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
|
||||
break;
|
||||
case ServerNetObject.ENTITY_EVENT:
|
||||
|
||||
int eventType = msg.ReadRangedInteger(0, 3);
|
||||
switch (eventType)
|
||||
{
|
||||
case 0:
|
||||
if (Inventory == null)
|
||||
{
|
||||
string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
|
||||
//read anyway to prevent messing up reading the rest of the message
|
||||
UInt16 lastEventID = msg.ReadUInt16();
|
||||
byte itemCount = msg.ReadByte();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
{
|
||||
msg.ReadUInt16();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Inventory.ClientRead(type, msg, sendingTime);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
byte ownerID = msg.ReadByte();
|
||||
ResetNetState();
|
||||
if (ownerID == GameMain.Client.ID)
|
||||
{
|
||||
if (controlled != null)
|
||||
{
|
||||
LastNetworkUpdateID = controlled.LastNetworkUpdateID;
|
||||
}
|
||||
|
||||
if (!IsDead) { Controlled = this; }
|
||||
IsRemotePlayer = false;
|
||||
GameMain.Client.HasSpawned = true;
|
||||
GameMain.Client.Character = this;
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (controlled == this)
|
||||
{
|
||||
Controlled = null;
|
||||
IsRemotePlayer = ownerID > 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
ReadStatus(msg);
|
||||
break;
|
||||
case 3:
|
||||
int skillCount = msg.ReadByte();
|
||||
for (int i = 0; i < skillCount; i++)
|
||||
{
|
||||
string skillIdentifier = msg.ReadString();
|
||||
float skillLevel = msg.ReadSingle();
|
||||
info?.SetSkillLevel(skillIdentifier, skillLevel, WorldPosition + Vector2.UnitY * 150.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static Character ReadSpawnData(IReadMessage inc)
|
||||
{
|
||||
DebugConsole.Log("Reading character spawn data");
|
||||
|
||||
if (GameMain.Client == null) return null;
|
||||
|
||||
bool noInfo = inc.ReadBoolean();
|
||||
ushort id = inc.ReadUInt16();
|
||||
string speciesName = inc.ReadString();
|
||||
string seed = inc.ReadString();
|
||||
|
||||
Vector2 position = new Vector2(inc.ReadSingle(), inc.ReadSingle());
|
||||
|
||||
bool enabled = inc.ReadBoolean();
|
||||
|
||||
DebugConsole.Log("Received spawn data for " + speciesName);
|
||||
|
||||
Character character = null;
|
||||
if (noInfo)
|
||||
{
|
||||
character = Create(speciesName, position, seed, null, true);
|
||||
character.ID = id;
|
||||
bool containsStatusData = inc.ReadBoolean();
|
||||
if (containsStatusData)
|
||||
{
|
||||
character.ReadStatus(inc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool hasOwner = inc.ReadBoolean();
|
||||
int ownerId = hasOwner ? inc.ReadByte() : -1;
|
||||
byte teamID = inc.ReadByte();
|
||||
bool hasAi = inc.ReadBoolean();
|
||||
string infoSpeciesName = inc.ReadString();
|
||||
|
||||
CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc);
|
||||
|
||||
character = Create(speciesName, position, seed, info, GameMain.Client.ID != ownerId, hasAi);
|
||||
character.ID = id;
|
||||
character.TeamID = (TeamType)teamID;
|
||||
|
||||
// Check if the character has a current order
|
||||
if (inc.ReadBoolean())
|
||||
{
|
||||
int orderPrefabIndex = inc.ReadByte();
|
||||
Entity targetEntity = FindEntityByID(inc.ReadUInt16());
|
||||
Character orderGiver = inc.ReadBoolean() ? FindEntityByID(inc.ReadUInt16()) as Character : null;
|
||||
int orderOptionIndex = inc.ReadByte();
|
||||
|
||||
if (orderPrefabIndex >= 0 && orderPrefabIndex < Order.PrefabList.Count)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList[orderPrefabIndex];
|
||||
if ((orderPrefab.ItemComponentType == null && orderPrefab.ItemIdentifiers.None()) ||
|
||||
(targetEntity != null && (targetEntity as Item).Components.Any(c => c?.GetType() == orderPrefab.ItemComponentType)))
|
||||
{
|
||||
character.SetOrder(
|
||||
new Order(orderPrefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(c => c?.GetType() == orderPrefab.ItemComponentType), orderGiver: orderGiver),
|
||||
orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null,
|
||||
orderGiver, speak: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Could not set order \"" + orderPrefab.Identifier + "\" for character \"" + character.Name + "\" because required target entity was not found.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid order prefab index - index (" + orderPrefabIndex + ") out of bounds.");
|
||||
}
|
||||
}
|
||||
|
||||
bool containsStatusData = inc.ReadBoolean();
|
||||
if (containsStatusData)
|
||||
{
|
||||
character.ReadStatus(inc);
|
||||
}
|
||||
|
||||
if (character.IsHuman && character.TeamID != TeamType.FriendlyNPC && !character.IsDead)
|
||||
{
|
||||
CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID);
|
||||
GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo);
|
||||
GameMain.GameSession.CrewManager.AddCharacter(character);
|
||||
}
|
||||
|
||||
if (GameMain.Client.ID == ownerId)
|
||||
{
|
||||
GameMain.Client.HasSpawned = true;
|
||||
GameMain.Client.Character = character;
|
||||
if (!character.IsDead) { Controlled = character; }
|
||||
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
|
||||
character.memInput.Clear();
|
||||
character.memState.Clear();
|
||||
character.memLocalState.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
character.Enabled = Controlled == character || enabled;
|
||||
|
||||
return character;
|
||||
}
|
||||
|
||||
private void ReadStatus(IReadMessage msg)
|
||||
{
|
||||
bool isDead = msg.ReadBoolean();
|
||||
if (isDead)
|
||||
{
|
||||
CauseOfDeathType causeOfDeathType = (CauseOfDeathType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(CauseOfDeathType)).Length - 1);
|
||||
AfflictionPrefab causeOfDeathAffliction = null;
|
||||
if (causeOfDeathType == CauseOfDeathType.Affliction)
|
||||
{
|
||||
string afflictionName = msg.ReadString();
|
||||
if (!AfflictionPrefab.Prefabs.ContainsKey(afflictionName))
|
||||
{
|
||||
string errorMsg = $"Error in CharacterNetworking.ReadStatus: affliction not found ({afflictionName})";
|
||||
causeOfDeathType = CauseOfDeathType.Unknown;
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:AfflictionIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
causeOfDeathAffliction = AfflictionPrefab.Prefabs[afflictionName];
|
||||
}
|
||||
}
|
||||
|
||||
byte severedLimbCount = msg.ReadByte();
|
||||
if (!IsDead)
|
||||
{
|
||||
if (causeOfDeathType == CauseOfDeathType.Pressure)
|
||||
{
|
||||
Implode(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Kill(causeOfDeathType, causeOfDeathAffliction?.Instantiate(1.0f), true);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < severedLimbCount; i++)
|
||||
{
|
||||
int severedJointIndex = msg.ReadByte();
|
||||
if (severedJointIndex < 0 || severedJointIndex >= AnimController.LimbJoints.Length)
|
||||
{
|
||||
string errorMsg = $"Error in CharacterNetworking.ReadStatus: severed joint index out of bounds (index: {severedJointIndex}, joint count: {AnimController.LimbJoints.Length})";
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ReadStatus:JointIndexOutOfBounts", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimController.SeverLimbJoint(AnimController.LimbJoints[severedJointIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsDead) { Revive(); }
|
||||
CharacterHealth.ClientRead(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,8 @@ namespace Barotrauma
|
||||
|
||||
public SoundType Type => Params.State;
|
||||
public Gender Gender => Params.Gender;
|
||||
public float Volume => roundSound.Volume;
|
||||
public float Range => roundSound.Range;
|
||||
public float Volume => roundSound == null ? 0.0f : roundSound.Volume;
|
||||
public float Range => roundSound == null ? 0.0f : roundSound.Range;
|
||||
public Sound Sound => roundSound?.Sound;
|
||||
|
||||
public CharacterSound(CharacterParams.SoundParams soundParams)
|
||||
@@ -41,7 +41,7 @@ namespace Barotrauma
|
||||
private Submarine parentSub;
|
||||
|
||||
public HUDProgressBar(Vector2 worldPosition, Submarine parentSubmarine = null)
|
||||
: this(worldPosition, parentSubmarine, Color.Red, Color.Green)
|
||||
: this(worldPosition, parentSubmarine, GUI.Style.Red, GUI.Style.Green)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class AfflictionHusk : Affliction
|
||||
{
|
||||
partial void UpdateMessages(float prevStrength, Character character)
|
||||
{
|
||||
if (Strength < Prefab.MaxStrength * 0.5f)
|
||||
{
|
||||
if (prevStrength % 10.0f > 0.05f && Strength % 10.0f < 0.05f)
|
||||
{
|
||||
GUI.AddMessage(TextManager.Get("HuskDormant"), GUI.Style.Red);
|
||||
}
|
||||
}
|
||||
else if (Strength < Prefab.MaxStrength)
|
||||
{
|
||||
if (state == InfectionState.Dormant && Character.Controlled == character)
|
||||
{
|
||||
GUI.AddMessage(TextManager.Get("HuskCantSpeak"), GUI.Style.Red);
|
||||
}
|
||||
}
|
||||
else if (state != InfectionState.Active && Character.Controlled == character)
|
||||
{
|
||||
GUI.AddMessage(TextManager.GetWithVariable("HuskActivate", "[Attack]", GameMain.Config.KeyBindText(InputType.Attack)),
|
||||
GUI.Style.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,125 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class JobPrefab : IPrefab, IDisposable
|
||||
{
|
||||
public GUIButton CreateInfoFrame(int variant)
|
||||
{
|
||||
int width = 500, height = 400;
|
||||
|
||||
GUIButton backFrame = new GUIButton(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker");
|
||||
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, height), backFrame.RectTransform, Anchor.Center));
|
||||
GUIFrame paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.9f, 0.9f), frame.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.1f), paddedFrame.RectTransform), Name, font: GUI.LargeFont);
|
||||
|
||||
var descriptionBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), paddedFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.15f) },
|
||||
Description, font: GUI.SmallFont, wrap: true);
|
||||
|
||||
var skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform)
|
||||
{ RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) });
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillContainer.RectTransform),
|
||||
TextManager.Get("Skills"), font: GUI.LargeFont);
|
||||
foreach (SkillPrefab skill in Skills)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillContainer.RectTransform),
|
||||
" - " + TextManager.AddPunctuation(':', TextManager.Get("SkillName." + skill.Identifier), (int)skill.LevelRange.X + " - " + (int)skill.LevelRange.Y),
|
||||
font: GUI.SmallFont);
|
||||
}
|
||||
|
||||
/*if (!ItemIdentifiers.TryGetValue(variant, out var itemIdentifiers)) { return backFrame; }
|
||||
var itemContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 0.5f), paddedFrame.RectTransform, Anchor.TopRight)
|
||||
{ RelativeOffset = new Vector2(0.0f, 0.2f + descriptionBlock.RectTransform.RelativeSize.Y) })
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
|
||||
TextManager.Get("Items", fallBackTag: "mapentitycategory.equipment"), font: GUI.LargeFont);
|
||||
foreach (string identifier in itemIdentifiers.Distinct())
|
||||
{
|
||||
if (!(MapEntityPrefab.Find(name: null, identifier: identifier) is ItemPrefab itemPrefab)) { continue; }
|
||||
int count = itemIdentifiers.Count(i => i == identifier);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), itemContainer.RectTransform),
|
||||
" - " + (count == 1 ? itemPrefab.Name : itemPrefab.Name + " x" + count),
|
||||
font: GUI.SmallFont);
|
||||
}*/
|
||||
|
||||
return backFrame;
|
||||
}
|
||||
|
||||
|
||||
public class OutfitPreview
|
||||
{
|
||||
/// <summary>
|
||||
/// Pair.First = sprite, Pair.Second = draw offset
|
||||
/// </summary>
|
||||
public readonly List<Pair<Sprite, Vector2>> Sprites;
|
||||
public Vector2 Dimensions;
|
||||
|
||||
public OutfitPreview()
|
||||
{
|
||||
Sprites = new List<Pair<Sprite, Vector2>>();
|
||||
Dimensions = Vector2.One;
|
||||
}
|
||||
|
||||
public void AddSprite(Sprite sprite, Vector2 drawOffset)
|
||||
{
|
||||
Sprites.Add(new Pair<Sprite, Vector2>(sprite, drawOffset));
|
||||
}
|
||||
}
|
||||
|
||||
public List<OutfitPreview> GetJobOutfitSprites(Gender gender, bool useInventoryIcon, out Vector2 maxDimensions)
|
||||
{
|
||||
List<OutfitPreview> outfitPreviews = new List<OutfitPreview>();
|
||||
maxDimensions = Vector2.One;
|
||||
|
||||
var equipIdentifiers = Element.GetChildElements("ItemSet").Elements().Where(e => e.GetAttributeBool("outfit", false)).Select(e => e.GetAttributeString("identifier", ""));
|
||||
|
||||
var outfitPrefabs = ItemPrefab.Prefabs.Where(itemPrefab => equipIdentifiers.Contains(itemPrefab.Identifier)).ToList();
|
||||
if (!outfitPrefabs.Any()) { return null; }
|
||||
|
||||
for (int i = 0; i < outfitPrefabs.Count; i++)
|
||||
{
|
||||
var outfitPreview = new OutfitPreview();
|
||||
|
||||
if (!ItemSets.TryGetValue(i, out var itemSetElement)) { continue; }
|
||||
var previewElement = itemSetElement.GetChildElement("PreviewSprites");
|
||||
if (previewElement == null || useInventoryIcon)
|
||||
{
|
||||
if (outfitPrefabs[i] is ItemPrefab prefab && prefab.InventoryIcon != null)
|
||||
{
|
||||
outfitPreview.AddSprite(prefab.InventoryIcon, Vector2.Zero);
|
||||
outfitPreview.Dimensions = prefab.InventoryIcon.SourceRect.Size.ToVector2();
|
||||
maxDimensions.X = MathHelper.Max(maxDimensions.X, outfitPreview.Dimensions.X);
|
||||
maxDimensions.Y = MathHelper.Max(maxDimensions.Y, outfitPreview.Dimensions.Y);
|
||||
}
|
||||
outfitPreviews.Add(outfitPreview);
|
||||
continue;
|
||||
}
|
||||
|
||||
var children = previewElement.Elements().ToList();
|
||||
for (int n = 0; n < children.Count; n++)
|
||||
{
|
||||
XElement spriteElement = children[n];
|
||||
string spriteTexture = spriteElement.GetAttributeString("texture", "").Replace("[GENDER]", (gender == Gender.Female) ? "female" : "male");
|
||||
var sprite = new Sprite(spriteElement, file: spriteTexture);
|
||||
sprite.size = new Vector2(sprite.SourceRect.Width, sprite.SourceRect.Height);
|
||||
outfitPreview.AddSprite(sprite, children[n].GetAttributeVector2("offset", Vector2.Zero));
|
||||
}
|
||||
|
||||
outfitPreview.Dimensions = previewElement.GetAttributeVector2("dims", Vector2.One);
|
||||
maxDimensions.X = MathHelper.Max(maxDimensions.X, outfitPreview.Dimensions.X);
|
||||
maxDimensions.Y = MathHelper.Max(maxDimensions.Y, outfitPreview.Dimensions.Y);
|
||||
|
||||
outfitPreviews.Add(outfitPreview);
|
||||
}
|
||||
|
||||
return outfitPreviews;
|
||||
}
|
||||
}
|
||||
}
|
||||
893
Barotrauma/BarotraumaClient/ClientSource/Characters/Limb.cs
Normal file
893
Barotrauma/BarotraumaClient/ClientSource/Characters/Limb.cs
Normal file
@@ -0,0 +1,893 @@
|
||||
using Barotrauma.Lights;
|
||||
using Barotrauma.Particles;
|
||||
using Barotrauma.SpriteDeformations;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics.Joints;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using SpriteParams = Barotrauma.RagdollParams.SpriteParams;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class LimbJoint : RevoluteJoint
|
||||
{
|
||||
public void UpdateDeformations(float deltaTime)
|
||||
{
|
||||
float diff = Math.Abs(UpperLimit - LowerLimit);
|
||||
float strength = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, MathHelper.Pi, diff));
|
||||
float jointAngle = this.JointAngle * strength;
|
||||
|
||||
JointBendDeformation limbADeformation = LimbA.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
|
||||
JointBendDeformation limbBDeformation = LimbB.Deformations.Find(d => d is JointBendDeformation) as JointBendDeformation;
|
||||
|
||||
if (limbADeformation != null && limbBDeformation != null)
|
||||
{
|
||||
UpdateBend(LimbA, limbADeformation, this.LocalAnchorA, -jointAngle);
|
||||
UpdateBend(LimbB, limbBDeformation, this.LocalAnchorB, jointAngle);
|
||||
}
|
||||
|
||||
void UpdateBend(Limb limb, JointBendDeformation deformation, Vector2 localAnchor, float angle)
|
||||
{
|
||||
deformation.Scale = limb.DeformSprite.Size;
|
||||
|
||||
Vector2 displayAnchor = ConvertUnits.ToDisplayUnits(localAnchor);
|
||||
displayAnchor.Y = -displayAnchor.Y;
|
||||
Vector2 refPos = displayAnchor + limb.DeformSprite.Origin;
|
||||
|
||||
refPos.X /= limb.DeformSprite.Size.X;
|
||||
refPos.Y /= limb.DeformSprite.Size.Y;
|
||||
|
||||
if (Math.Abs(localAnchor.X) > Math.Abs(localAnchor.Y))
|
||||
{
|
||||
if (localAnchor.X > 0.0f)
|
||||
{
|
||||
deformation.BendRightRefPos = refPos;
|
||||
deformation.BendRight = angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
deformation.BendLeftRefPos = refPos;
|
||||
deformation.BendLeft = angle;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (localAnchor.Y > 0.0f)
|
||||
{
|
||||
deformation.BendUpRefPos = refPos;
|
||||
deformation.BendUp = angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
deformation.BendDownRefPos = refPos;
|
||||
deformation.BendDown = angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
// TODO: move this into the character editor
|
||||
//var mouthPos = ragdoll.GetMouthPosition();
|
||||
//if (mouthPos != null)
|
||||
//{
|
||||
// var pos = ConvertUnits.ToDisplayUnits(mouthPos.Value);
|
||||
// pos.Y = -pos.Y;
|
||||
// ShapeExtensions.DrawPoint(spriteBatch, pos, GUI.Style.Red, size: 5);
|
||||
//}
|
||||
return;
|
||||
// A debug visualisation on the bezier curve between limbs.
|
||||
var start = LimbA.WorldPosition;
|
||||
var end = LimbB.WorldPosition;
|
||||
var jointAPos = ConvertUnits.ToDisplayUnits(LocalAnchorA);
|
||||
var control = start + Vector2.Transform(jointAPos, Matrix.CreateRotationZ(LimbA.Rotation));
|
||||
start.Y = -start.Y;
|
||||
end.Y = -end.Y;
|
||||
control.Y = -control.Y;
|
||||
//GUI.DrawRectangle(spriteBatch, start, Vector2.One * 5, Color.White, true);
|
||||
//GUI.DrawRectangle(spriteBatch, end, Vector2.One * 5, Color.Black, true);
|
||||
//GUI.DrawRectangle(spriteBatch, control, Vector2.One * 5, Color.Black, true);
|
||||
//GUI.DrawLine(spriteBatch, start, end, Color.White);
|
||||
//GUI.DrawLine(spriteBatch, start, control, Color.Black);
|
||||
//GUI.DrawLine(spriteBatch, control, end, Color.Black);
|
||||
GUI.DrawBezierWithDots(spriteBatch, start, end, control, 1000, GUI.Style.Red);
|
||||
}
|
||||
}
|
||||
|
||||
partial class Limb
|
||||
{
|
||||
//minimum duration between hit/attack sounds
|
||||
public const float SoundInterval = 0.4f;
|
||||
public float LastAttackSoundTime, LastImpactSoundTime;
|
||||
|
||||
private float wetTimer;
|
||||
private float dripParticleTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Note that different limbs can share the same deformations.
|
||||
/// Use ragdoll.SpriteDeformations for a collection that cannot have duplicates.
|
||||
/// </summary>
|
||||
public List<SpriteDeformation> Deformations { get; private set; } = new List<SpriteDeformation>();
|
||||
|
||||
public Sprite Sprite { get; protected set; }
|
||||
|
||||
protected DeformableSprite _deformSprite;
|
||||
|
||||
public DeformableSprite DeformSprite
|
||||
{
|
||||
get
|
||||
{
|
||||
var conditionalSprite = ConditionalSprites.FirstOrDefault(c => c.IsActive && c.DeformableSprite != null);
|
||||
if (conditionalSprite != null)
|
||||
{
|
||||
return conditionalSprite.DeformableSprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _deformSprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<DecorativeSprite> DecorativeSprites { get; private set; } = new List<DecorativeSprite>();
|
||||
|
||||
public Sprite ActiveSprite
|
||||
{
|
||||
get
|
||||
{
|
||||
var conditionalSprite = ConditionalSprites.FirstOrDefault(c => c.IsActive && c.ActiveSprite != null);
|
||||
if (conditionalSprite != null)
|
||||
{
|
||||
return conditionalSprite.ActiveSprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _deformSprite != null ? _deformSprite.Sprite : Sprite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public WearableSprite HuskSprite { get; private set; }
|
||||
public WearableSprite HerpesSprite { get; private set; }
|
||||
|
||||
public void LoadHuskSprite() => HuskSprite = GetWearableSprite(WearableType.Husk);
|
||||
public void LoadHerpesSprite() => HerpesSprite = GetWearableSprite(WearableType.Herpes);
|
||||
|
||||
public float TextureScale => Params.Ragdoll.TextureScale;
|
||||
|
||||
public Sprite DamagedSprite { get; private set; }
|
||||
|
||||
public List<ConditionalSprite> ConditionalSprites { get; private set; } = new List<ConditionalSprite>();
|
||||
private Dictionary<DecorativeSprite, SpriteState> spriteAnimState = new Dictionary<DecorativeSprite, SpriteState>();
|
||||
private Dictionary<int, List<DecorativeSprite>> DecorativeSpriteGroups = new Dictionary<int, List<DecorativeSprite>>();
|
||||
|
||||
class SpriteState
|
||||
{
|
||||
public float RotationState;
|
||||
public float OffsetState;
|
||||
public bool IsActive = true;
|
||||
}
|
||||
|
||||
public Color InitialLightSourceColor
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public float? InitialLightSpriteAlpha
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public LightSource LightSource
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private float damageOverlayStrength;
|
||||
public float DamageOverlayStrength
|
||||
{
|
||||
get { return damageOverlayStrength; }
|
||||
set { damageOverlayStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
}
|
||||
|
||||
private float burnOverLayStrength;
|
||||
public float BurnOverlayStrength
|
||||
{
|
||||
get { return burnOverLayStrength; }
|
||||
set { burnOverLayStrength = MathHelper.Clamp(value, 0.0f, 100.0f); }
|
||||
}
|
||||
|
||||
public string HitSoundTag => Params?.Sound?.Tag;
|
||||
|
||||
private List<WearableSprite> wearableTypeHidingSprites = new List<WearableSprite>();
|
||||
private List<WearableType> wearableTypesToHide = new List<WearableType>();
|
||||
private bool enableHuskSprite;
|
||||
public bool EnableHuskSprite
|
||||
{
|
||||
get
|
||||
{
|
||||
return enableHuskSprite;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (HuskSprite != null && value != enableHuskSprite)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
List<WearableSprite> otherWearablesWithHusk = new List<WearableSprite>() { HuskSprite };
|
||||
otherWearablesWithHusk.AddRange(OtherWearables);
|
||||
OtherWearables = otherWearablesWithHusk;
|
||||
UpdateWearableTypesToHide();
|
||||
}
|
||||
else
|
||||
{
|
||||
OtherWearables.Remove(HuskSprite);
|
||||
UpdateWearableTypesToHide();
|
||||
}
|
||||
}
|
||||
enableHuskSprite = value;
|
||||
}
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
{
|
||||
for (int i = 0; i < Params.decorativeSpriteParams.Count; i++)
|
||||
{
|
||||
var param = Params.decorativeSpriteParams[i];
|
||||
var decorativeSprite = new DecorativeSprite(param.Element, file: GetSpritePath(param.Element, param));
|
||||
DecorativeSprites.Add(decorativeSprite);
|
||||
int groupID = decorativeSprite.RandomGroupID;
|
||||
if (!DecorativeSpriteGroups.ContainsKey(groupID))
|
||||
{
|
||||
DecorativeSpriteGroups.Add(groupID, new List<DecorativeSprite>());
|
||||
}
|
||||
DecorativeSpriteGroups[groupID].Add(decorativeSprite);
|
||||
spriteAnimState.Add(decorativeSprite, new SpriteState());
|
||||
}
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
Sprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.normalSpriteParams));
|
||||
break;
|
||||
case "damagedsprite":
|
||||
DamagedSprite = new Sprite(subElement, file: GetSpritePath(subElement, Params.damagedSpriteParams));
|
||||
break;
|
||||
case "conditionalsprite":
|
||||
var conditionalSprite = new ConditionalSprite(subElement, character, file: GetSpritePath(subElement, null));
|
||||
ConditionalSprites.Add(conditionalSprite);
|
||||
if (conditionalSprite.DeformableSprite != null)
|
||||
{
|
||||
CreateDeformations(subElement.GetChildElement("deformablesprite"));
|
||||
}
|
||||
break;
|
||||
case "deformablesprite":
|
||||
_deformSprite = new DeformableSprite(subElement, filePath: GetSpritePath(subElement, Params.deformSpriteParams));
|
||||
CreateDeformations(subElement);
|
||||
break;
|
||||
case "lightsource":
|
||||
LightSource = new LightSource(subElement)
|
||||
{
|
||||
ParentBody = body,
|
||||
SpriteScale = Vector2.One * Scale * TextureScale
|
||||
};
|
||||
InitialLightSourceColor = LightSource.Color;
|
||||
InitialLightSpriteAlpha = LightSource.OverrideLightSpriteAlpha;
|
||||
break;
|
||||
}
|
||||
|
||||
void CreateDeformations(XElement e)
|
||||
{
|
||||
foreach (XElement animationElement in e.GetChildElements("spritedeformation"))
|
||||
{
|
||||
int sync = animationElement.GetAttributeInt("sync", -1);
|
||||
SpriteDeformation deformation = null;
|
||||
if (sync > -1)
|
||||
{
|
||||
// if the element is marked with the sync attribute, use a deformation of the same type with the same sync value, if there is one already.
|
||||
string typeName = animationElement.GetAttributeString("type", "").ToLowerInvariant();
|
||||
deformation = ragdoll.Limbs
|
||||
.Where(l => l != null)
|
||||
.SelectMany(l => l.Deformations)
|
||||
.Where(d => d.TypeName == typeName && d.Sync == sync)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
if (deformation == null)
|
||||
{
|
||||
deformation = SpriteDeformation.Load(animationElement, character.SpeciesName);
|
||||
if (deformation != null)
|
||||
{
|
||||
ragdoll.SpriteDeformations.Add(deformation);
|
||||
}
|
||||
}
|
||||
if (deformation != null)
|
||||
{
|
||||
Deformations.Add(deformation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RecreateSprites()
|
||||
{
|
||||
if (Sprite != null)
|
||||
{
|
||||
Sprite.Remove();
|
||||
var source = Sprite.SourceElement;
|
||||
Sprite = new Sprite(source, file: GetSpritePath(source, Params.normalSpriteParams));
|
||||
}
|
||||
if (_deformSprite != null)
|
||||
{
|
||||
_deformSprite.Remove();
|
||||
var source = _deformSprite.Sprite.SourceElement;
|
||||
_deformSprite = new DeformableSprite(source, filePath: GetSpritePath(source, Params.deformSpriteParams));
|
||||
}
|
||||
if (DamagedSprite != null)
|
||||
{
|
||||
DamagedSprite.Remove();
|
||||
var source = DamagedSprite.SourceElement;
|
||||
DamagedSprite = new Sprite(source, file: GetSpritePath(source, Params.damagedSpriteParams));
|
||||
}
|
||||
for (int i = 0; i < ConditionalSprites.Count; i++)
|
||||
{
|
||||
var conditionalSprite = ConditionalSprites[i];
|
||||
var source = conditionalSprite.ActiveSprite.SourceElement;
|
||||
conditionalSprite.Remove();
|
||||
ConditionalSprites[i] = new ConditionalSprite(source, character, file: GetSpritePath(source, null));
|
||||
}
|
||||
for (int i = 0; i < DecorativeSprites.Count; i++)
|
||||
{
|
||||
var decorativeSprite = DecorativeSprites[i];
|
||||
decorativeSprite.Remove();
|
||||
var source = decorativeSprite.Sprite.SourceElement;
|
||||
DecorativeSprites[i] = new DecorativeSprite(source, file: GetSpritePath(source, Params.decorativeSpriteParams[i]));
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateHeadPosition(Sprite sprite)
|
||||
{
|
||||
if (type != LimbType.Head) { return; }
|
||||
character.Info?.CalculateHeadPosition(sprite);
|
||||
}
|
||||
|
||||
private string GetSpritePath(XElement element, SpriteParams spriteParams)
|
||||
{
|
||||
string texturePath = element.GetAttributeString("texture", null);
|
||||
if (string.IsNullOrWhiteSpace(texturePath) && spriteParams != null)
|
||||
{
|
||||
texturePath = spriteParams.Ragdoll.Texture;
|
||||
}
|
||||
return GetSpritePath(texturePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the full path of a limb sprite, taking into account tags, gender and head id
|
||||
/// </summary>
|
||||
private string GetSpritePath(string texturePath)
|
||||
{
|
||||
string spritePath = texturePath;
|
||||
string spritePathWithTags = spritePath;
|
||||
if (character.Info != null && character.IsHumanoid)
|
||||
{
|
||||
spritePath = spritePath.Replace("[GENDER]", (character.Info.Gender == Gender.Female) ? "female" : "male");
|
||||
spritePath = spritePath.Replace("[RACE]", character.Info.Race.ToString().ToLowerInvariant());
|
||||
spritePath = spritePath.Replace("[HEADID]", character.Info.HeadSpriteId.ToString());
|
||||
|
||||
if (character.Info.HeadSprite != null && character.Info.SpriteTags.Any())
|
||||
{
|
||||
string tags = "";
|
||||
character.Info.SpriteTags.ForEach(tag => tags += "[" + tag + "]");
|
||||
|
||||
spritePathWithTags = Path.Combine(
|
||||
Path.GetDirectoryName(spritePath),
|
||||
Path.GetFileNameWithoutExtension(spritePath) + tags + Path.GetExtension(spritePath));
|
||||
}
|
||||
}
|
||||
return File.Exists(spritePathWithTags) ? spritePathWithTags : spritePath;
|
||||
}
|
||||
|
||||
partial void LoadParamsProjSpecific()
|
||||
{
|
||||
bool isFlipped = dir == Direction.Left;
|
||||
Sprite?.LoadParams(Params.normalSpriteParams, isFlipped);
|
||||
DamagedSprite?.LoadParams(Params.damagedSpriteParams, isFlipped);
|
||||
_deformSprite?.Sprite.LoadParams(Params.deformSpriteParams, isFlipped);
|
||||
for (int i = 0; i < DecorativeSprites.Count; i++)
|
||||
{
|
||||
DecorativeSprites[i].Sprite?.LoadParams(Params.decorativeSpriteParams[i], isFlipped);
|
||||
}
|
||||
}
|
||||
|
||||
partial void AddDamageProjSpecific(Vector2 simPosition, List<Affliction> afflictions, bool playSound, List<DamageModifier> appliedDamageModifiers)
|
||||
{
|
||||
float bleedingDamage = character.CharacterHealth.DoesBleed ? afflictions.FindAll(a => a is AfflictionBleeding).Sum(a => a.GetVitalityDecrease(character.CharacterHealth)) : 0;
|
||||
float damage = afflictions.FindAll(a => a.Prefab.AfflictionType == "damage").Sum(a => a.GetVitalityDecrease(character.CharacterHealth));
|
||||
float damageMultiplier = 1;
|
||||
foreach (DamageModifier damageModifier in appliedDamageModifiers)
|
||||
{
|
||||
damageMultiplier *= damageModifier.DamageMultiplier;
|
||||
}
|
||||
if (playSound)
|
||||
{
|
||||
string damageSoundType = (bleedingDamage > damage) ? "LimbSlash" : "LimbBlunt";
|
||||
foreach (DamageModifier damageModifier in appliedDamageModifiers)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(damageModifier.DamageSound))
|
||||
{
|
||||
damageSoundType = damageModifier.DamageSound;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SoundPlayer.PlayDamageSound(damageSoundType, Math.Max(damage, bleedingDamage), WorldPosition);
|
||||
}
|
||||
|
||||
// spawn damage particles
|
||||
float damageParticleAmount = Math.Min(damage / 10, 1.0f) * damageMultiplier;
|
||||
if (damageParticleAmount > 0.001f)
|
||||
{
|
||||
foreach (ParticleEmitter emitter in character.DamageEmitters)
|
||||
{
|
||||
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) continue;
|
||||
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) continue;
|
||||
|
||||
emitter.Emit(1.0f, WorldPosition, character.CurrentHull, amountMultiplier: damageParticleAmount);
|
||||
}
|
||||
}
|
||||
|
||||
if (bleedingDamage > 0)
|
||||
{
|
||||
float bloodParticleAmount = Math.Min(bleedingDamage / 5, 1.0f) * damageMultiplier;
|
||||
float bloodParticleSize = MathHelper.Clamp(bleedingDamage / 5, 0.1f, 1.0f);
|
||||
|
||||
foreach (ParticleEmitter emitter in character.BloodEmitters)
|
||||
{
|
||||
if (inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Air) continue;
|
||||
if (!inWater && emitter.Prefab.ParticlePrefab.DrawTarget == ParticlePrefab.DrawTargetType.Water) continue;
|
||||
|
||||
emitter.Emit(1.0f, WorldPosition, character.CurrentHull, sizeMultiplier: bloodParticleSize, amountMultiplier: bloodParticleAmount);
|
||||
}
|
||||
|
||||
if (bloodParticleAmount > 0 && character.CurrentHull != null && !string.IsNullOrEmpty(character.BloodDecalName))
|
||||
{
|
||||
character.CurrentHull.AddDecal(character.BloodDecalName, WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
if (!body.Enabled) return;
|
||||
|
||||
if (!character.IsDead)
|
||||
{
|
||||
DamageOverlayStrength -= deltaTime;
|
||||
BurnOverlayStrength -= deltaTime;
|
||||
}
|
||||
|
||||
if (inWater)
|
||||
{
|
||||
wetTimer = 1.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
wetTimer -= deltaTime * 0.1f;
|
||||
if (wetTimer > 0.0f)
|
||||
{
|
||||
dripParticleTimer += wetTimer * deltaTime * Mass * (wetTimer > 0.9f ? 50.0f : 5.0f);
|
||||
if (dripParticleTimer > 1.0f)
|
||||
{
|
||||
float dropRadius = body.BodyShape == PhysicsBody.Shape.Rectangle ? Math.Min(body.width, body.height) : body.radius;
|
||||
GameMain.ParticleManager.CreateParticle(
|
||||
"waterdrop",
|
||||
WorldPosition + Rand.Vector(Rand.Range(0.0f, ConvertUnits.ToDisplayUnits(dropRadius))),
|
||||
ConvertUnits.ToDisplayUnits(body.LinearVelocity),
|
||||
0, character.CurrentHull);
|
||||
dripParticleTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (LightSource != null)
|
||||
{
|
||||
LightSource.ParentSub = body.Submarine;
|
||||
LightSource.Rotation = (dir == Direction.Right) ? body.Rotation : body.Rotation - MathHelper.Pi;
|
||||
if (LightSource.LightSprite != null)
|
||||
{
|
||||
LightSource.LightSprite.Depth = ActiveSprite.Depth;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateSpriteStates(deltaTime);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Camera cam, Color? overrideColor = null)
|
||||
{
|
||||
float brightness = 1.0f - (burnOverLayStrength / 100.0f) * 0.5f;
|
||||
Color color = new Color(brightness, brightness, brightness);
|
||||
|
||||
color = overrideColor ?? color;
|
||||
|
||||
if (isSevered)
|
||||
{
|
||||
if (severedFadeOutTimer > SeveredFadeOutTime)
|
||||
{
|
||||
if (LightSource != null) { LightSource.Enabled = false; }
|
||||
return;
|
||||
}
|
||||
else if (severedFadeOutTimer > SeveredFadeOutTime - 1.0f)
|
||||
{
|
||||
color *= SeveredFadeOutTime - severedFadeOutTimer;
|
||||
}
|
||||
}
|
||||
|
||||
body.Dir = Dir;
|
||||
|
||||
float herpesStrength = character.CharacterHealth.GetAfflictionStrength("spaceherpes");
|
||||
|
||||
bool hideLimb = Params.Hide ||
|
||||
OtherWearables.Any(w => w.HideLimb) ||
|
||||
wearingItems.Any(w => w != null && w.HideLimb);
|
||||
|
||||
var activeSprite = ActiveSprite;
|
||||
if (type == LimbType.Head)
|
||||
{
|
||||
CalculateHeadPosition(activeSprite);
|
||||
}
|
||||
|
||||
body.UpdateDrawPosition();
|
||||
|
||||
if (!hideLimb)
|
||||
{
|
||||
var deformSprite = DeformSprite;
|
||||
if (deformSprite != null)
|
||||
{
|
||||
if (Deformations != null && Deformations.Any())
|
||||
{
|
||||
var deformation = SpriteDeformation.GetDeformation(Deformations, deformSprite.Size);
|
||||
deformSprite.Deform(deformation);
|
||||
}
|
||||
else
|
||||
{
|
||||
deformSprite.Reset();
|
||||
}
|
||||
body.Draw(deformSprite, cam, Vector2.One * Scale * TextureScale, color, Params.MirrorHorizontally);
|
||||
}
|
||||
else
|
||||
{
|
||||
body.Draw(spriteBatch, activeSprite, color, null, Scale * TextureScale, Params.MirrorHorizontally, Params.MirrorVertically);
|
||||
}
|
||||
}
|
||||
SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
|
||||
if (LightSource != null)
|
||||
{
|
||||
LightSource.LightSpriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
}
|
||||
if (damageOverlayStrength > 0.0f && DamagedSprite != null && !hideLimb)
|
||||
{
|
||||
DamagedSprite.Draw(spriteBatch,
|
||||
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
|
||||
color * Math.Min(damageOverlayStrength, 1.0f), activeSprite.Origin,
|
||||
-body.DrawRotation,
|
||||
Scale, spriteEffect, activeSprite.Depth - 0.0000015f);
|
||||
}
|
||||
foreach (var decorativeSprite in DecorativeSprites)
|
||||
{
|
||||
if (!spriteAnimState[decorativeSprite].IsActive) { continue; }
|
||||
float rotation = decorativeSprite.GetRotation(ref spriteAnimState[decorativeSprite].RotationState);
|
||||
Vector2 offset = decorativeSprite.GetOffset(ref spriteAnimState[decorativeSprite].OffsetState) * Scale;
|
||||
var ca = (float)Math.Cos(-body.Rotation);
|
||||
var sa = (float)Math.Sin(-body.Rotation);
|
||||
Vector2 transformedOffset = new Vector2(ca * offset.X + sa * offset.Y, -sa * offset.X + ca * offset.Y);
|
||||
decorativeSprite.Sprite.Draw(spriteBatch, new Vector2(body.DrawPosition.X + transformedOffset.X, -(body.DrawPosition.Y + transformedOffset.Y)), color,
|
||||
-body.Rotation + rotation, Scale, spriteEffect,
|
||||
depth: decorativeSprite.Sprite.Depth);
|
||||
}
|
||||
float depthStep = 0.000001f;
|
||||
float step = depthStep;
|
||||
WearableSprite onlyDrawable = wearingItems.Find(w => w.HideOtherWearables);
|
||||
if (Params.MirrorHorizontally)
|
||||
{
|
||||
spriteEffect = spriteEffect == SpriteEffects.None ? SpriteEffects.FlipHorizontally : SpriteEffects.None;
|
||||
}
|
||||
if (Params.MirrorVertically)
|
||||
{
|
||||
spriteEffect |= SpriteEffects.FlipVertically;
|
||||
}
|
||||
if (onlyDrawable == null)
|
||||
{
|
||||
if (HerpesSprite != null && !wearableTypesToHide.Contains(WearableType.Herpes))
|
||||
{
|
||||
DrawWearable(HerpesSprite, depthStep, spriteBatch, color * Math.Min(herpesStrength / 10.0f, 1.0f), spriteEffect);
|
||||
depthStep += step;
|
||||
}
|
||||
foreach (WearableSprite wearable in OtherWearables)
|
||||
{
|
||||
if (wearableTypesToHide.Contains(wearable.Type)) { continue; }
|
||||
DrawWearable(wearable, depthStep, spriteBatch, color, spriteEffect);
|
||||
//if there are multiple sprites on this limb, make the successive ones be drawn in front
|
||||
depthStep += step;
|
||||
}
|
||||
}
|
||||
foreach (WearableSprite wearable in WearingItems)
|
||||
{
|
||||
if (onlyDrawable != null && onlyDrawable != wearable) continue;
|
||||
DrawWearable(wearable, depthStep, spriteBatch, color, spriteEffect);
|
||||
//if there are multiple sprites on this limb, make the successive ones be drawn in front
|
||||
depthStep += step;
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
if (pullJoint != null)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(pullJoint.WorldAnchorB);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)pos.X, (int)-pos.Y, 5, 5), GUI.Style.Red, true);
|
||||
}
|
||||
var bodyDrawPos = body.DrawPosition;
|
||||
bodyDrawPos.Y = -bodyDrawPos.Y;
|
||||
if (IsStuck)
|
||||
{
|
||||
Vector2 from = ConvertUnits.ToDisplayUnits(attachJoint.WorldAnchorA);
|
||||
from.Y = -from.Y;
|
||||
Vector2 to = ConvertUnits.ToDisplayUnits(attachJoint.WorldAnchorB);
|
||||
to.Y = -to.Y;
|
||||
var localFront = body.GetLocalFront(Params.GetSpriteOrientation());
|
||||
var front = ConvertUnits.ToDisplayUnits(body.FarseerBody.GetWorldPoint(localFront));
|
||||
front.Y = -front.Y;
|
||||
GUI.DrawLine(spriteBatch, bodyDrawPos, front, Color.Yellow, width: 2);
|
||||
GUI.DrawLine(spriteBatch, from, to, GUI.Style.Red, width: 1);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)from.X, (int)from.Y, 12, 12), Color.White, true);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)to.X, (int)to.Y, 12, 12), Color.White, true);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)from.X, (int)from.Y, 10, 10), Color.Blue, true);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)to.X, (int)to.Y, 10, 10), GUI.Style.Red, true);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle((int)front.X, (int)front.Y, 10, 10), Color.Yellow, true);
|
||||
|
||||
//Vector2 mainLimbFront = ConvertUnits.ToDisplayUnits(ragdoll.MainLimb.body.FarseerBody.GetWorldPoint(ragdoll.MainLimb.body.GetFrontLocal(MathHelper.ToRadians(limbParams.Orientation))));
|
||||
//mainLimbFront.Y = -mainLimbFront.Y;
|
||||
//var mainLimbDrawPos = ragdoll.MainLimb.body.DrawPosition;
|
||||
//mainLimbDrawPos.Y = -mainLimbDrawPos.Y;
|
||||
//GUI.DrawLine(spriteBatch, mainLimbDrawPos, mainLimbFront, Color.White, width: 5);
|
||||
//GUI.DrawRectangle(spriteBatch, new Rectangle((int)mainLimbFront.X, (int)mainLimbFront.Y, 10, 10), Color.Yellow, true);
|
||||
}
|
||||
//DrawDamageModifiers(spriteBatch, cam, bodyDrawPos, isScreenSpace: false);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateWearableTypesToHide()
|
||||
{
|
||||
wearableTypeHidingSprites.Clear();
|
||||
if (WearingItems != null && WearingItems.Count > 0)
|
||||
{
|
||||
wearableTypeHidingSprites.AddRange(
|
||||
WearingItems.FindAll(w => w.HideWearablesOfType != null && w.HideWearablesOfType.Count > 0));
|
||||
}
|
||||
if (OtherWearables != null && OtherWearables.Count > 0)
|
||||
{
|
||||
wearableTypeHidingSprites.AddRange(
|
||||
OtherWearables.FindAll(w => w.HideWearablesOfType != null && w.HideWearablesOfType.Count > 0));
|
||||
}
|
||||
|
||||
wearableTypesToHide.Clear();
|
||||
if (wearableTypeHidingSprites.Count > 0)
|
||||
{
|
||||
foreach (WearableSprite sprite in wearableTypeHidingSprites)
|
||||
{
|
||||
foreach (WearableType type in sprite.HideWearablesOfType)
|
||||
{
|
||||
if (!wearableTypesToHide.Contains(type))
|
||||
{
|
||||
wearableTypesToHide.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSpriteStates(float deltaTime)
|
||||
{
|
||||
foreach (int spriteGroup in DecorativeSpriteGroups.Keys)
|
||||
{
|
||||
for (int i = 0; i < DecorativeSpriteGroups[spriteGroup].Count; i++)
|
||||
{
|
||||
var decorativeSprite = DecorativeSpriteGroups[spriteGroup][i];
|
||||
if (decorativeSprite == null) { continue; }
|
||||
if (spriteGroup > 0)
|
||||
{
|
||||
// TODO
|
||||
//int activeSpriteIndex = ID % DecorativeSpriteGroups[spriteGroup].Count;
|
||||
//if (i != activeSpriteIndex)
|
||||
//{
|
||||
// spriteAnimState[decorativeSprite].IsActive = false;
|
||||
// continue;
|
||||
//}
|
||||
}
|
||||
|
||||
//check if the sprite is active (whether it should be drawn or not)
|
||||
var spriteState = spriteAnimState[decorativeSprite];
|
||||
spriteState.IsActive = true;
|
||||
foreach (PropertyConditional conditional in decorativeSprite.IsActiveConditionals)
|
||||
{
|
||||
if (!conditional.Matches(this))
|
||||
{
|
||||
spriteState.IsActive = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!spriteState.IsActive) { continue; }
|
||||
|
||||
//check if the sprite should be animated
|
||||
bool animate = true;
|
||||
foreach (PropertyConditional conditional in decorativeSprite.AnimationConditionals)
|
||||
{
|
||||
if (!conditional.Matches(this)) { animate = false; break; }
|
||||
}
|
||||
if (!animate) { continue; }
|
||||
spriteState.OffsetState += deltaTime;
|
||||
spriteState.RotationState += deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawDamageModifiers(SpriteBatch spriteBatch, Camera cam, Vector2 startPos, bool isScreenSpace)
|
||||
{
|
||||
foreach (var modifier in DamageModifiers)
|
||||
{
|
||||
//Vector2 up = VectorExtensions.Backward(-body.TransformedRotation + Params.GetSpriteOrientation() * Dir);
|
||||
//int width = 4;
|
||||
//if (!isScreenSpace)
|
||||
//{
|
||||
// width = (int)Math.Round(width / cam.Zoom);
|
||||
//}
|
||||
//GUI.DrawLine(spriteBatch, startPos, startPos + Vector2.Normalize(up) * size, GUI.Style.Red, width: width);
|
||||
Color color = modifier.DamageMultiplier > 1 ? GUI.Style.Red : GUI.Style.Green;
|
||||
float size = ConvertUnits.ToDisplayUnits(body.GetSize().Length() / 2);
|
||||
if (isScreenSpace)
|
||||
{
|
||||
size *= cam.Zoom;
|
||||
}
|
||||
int thickness = 2;
|
||||
if (!isScreenSpace)
|
||||
{
|
||||
thickness = (int)Math.Round(thickness / cam.Zoom);
|
||||
}
|
||||
float bodyRotation = -body.Rotation;
|
||||
float constantOffset = -MathHelper.PiOver2;
|
||||
Vector2 armorSector = modifier.ArmorSectorInRadians;
|
||||
float armorSectorSize = Math.Abs(armorSector.X - armorSector.Y);
|
||||
float radians = armorSectorSize * Dir;
|
||||
float armorSectorOffset = armorSector.X * Dir;
|
||||
float finalOffset = bodyRotation + constantOffset + armorSectorOffset;
|
||||
ShapeExtensions.DrawSector(spriteBatch, startPos, size, radians, 40, color, finalOffset, thickness);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawWearable(WearableSprite wearable, float depthStep, SpriteBatch spriteBatch, Color color, SpriteEffects spriteEffect)
|
||||
{
|
||||
var sprite = ActiveSprite;
|
||||
if (wearable.InheritSourceRect)
|
||||
{
|
||||
if (wearable.SheetIndex.HasValue)
|
||||
{
|
||||
wearable.Sprite.SourceRect = new Rectangle(CharacterInfo.CalculateOffset(sprite, wearable.SheetIndex.Value), sprite.SourceRect.Size);
|
||||
}
|
||||
else if (type == LimbType.Head && character.Info != null && character.Info.Head.SheetIndex.HasValue)
|
||||
{
|
||||
wearable.Sprite.SourceRect = new Rectangle(CharacterInfo.CalculateOffset(sprite, character.Info.Head.SheetIndex.Value.ToPoint()), sprite.SourceRect.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
wearable.Sprite.SourceRect = sprite.SourceRect;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 origin = wearable.Sprite.Origin;
|
||||
if (wearable.InheritOrigin)
|
||||
{
|
||||
origin = sprite.Origin;
|
||||
wearable.Sprite.Origin = origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
origin = wearable.Sprite.Origin;
|
||||
// If the wearable inherits the origin, flipping is already handled.
|
||||
if (body.Dir == -1.0f)
|
||||
{
|
||||
origin.X = wearable.Sprite.SourceRect.Width - origin.X;
|
||||
}
|
||||
}
|
||||
|
||||
float depth = wearable.Sprite.Depth;
|
||||
|
||||
if (wearable.InheritLimbDepth)
|
||||
{
|
||||
depth = sprite.Depth - depthStep;
|
||||
Limb depthLimb = (wearable.DepthLimb == LimbType.None) ? this : character.AnimController.GetLimb(wearable.DepthLimb);
|
||||
if (depthLimb != null)
|
||||
{
|
||||
depth = depthLimb.ActiveSprite.Depth - depthStep;
|
||||
}
|
||||
}
|
||||
var wearableItemComponent = wearable.WearableComponent;
|
||||
Color wearableColor = Color.White;
|
||||
if (wearableItemComponent != null)
|
||||
{
|
||||
// Draw outer cloths on top of inner cloths.
|
||||
if (wearableItemComponent.AllowedSlots.Contains(InvSlotType.OuterClothes))
|
||||
{
|
||||
depth -= depthStep;
|
||||
}
|
||||
wearableColor = wearableItemComponent.Item.GetSpriteColor();
|
||||
}
|
||||
float textureScale = wearable.InheritTextureScale ? TextureScale : 1;
|
||||
|
||||
wearable.Sprite.Draw(spriteBatch,
|
||||
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
|
||||
new Color((color.R * wearableColor.R) / (255.0f * 255.0f), (color.G * wearableColor.G) / (255.0f * 255.0f), (color.B * wearableColor.B) / (255.0f * 255.0f)) * ((color.A * wearableColor.A) / (255.0f * 255.0f)),
|
||||
origin, -body.DrawRotation,
|
||||
Scale * textureScale, spriteEffect, depth);
|
||||
}
|
||||
|
||||
private WearableSprite GetWearableSprite(WearableType type, bool random = false)
|
||||
{
|
||||
var info = character.Info;
|
||||
if (info == null) { return null; }
|
||||
XElement element;
|
||||
if (random)
|
||||
{
|
||||
element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), type)?.GetRandom(Rand.RandSync.ClientOnly);
|
||||
}
|
||||
else
|
||||
{
|
||||
element = info.FilterByTypeAndHeadID(character.Info.FilterElementsByGenderAndRace(character.Info.Wearables), type)?.FirstOrDefault();
|
||||
}
|
||||
if (element != null)
|
||||
{
|
||||
return new WearableSprite(element.Element("sprite"), type);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
partial void RemoveProjSpecific()
|
||||
{
|
||||
Sprite?.Remove();
|
||||
Sprite = null;
|
||||
|
||||
DamagedSprite?.Remove();
|
||||
DamagedSprite = null;
|
||||
|
||||
_deformSprite?.Sprite?.Remove();
|
||||
_deformSprite = null;
|
||||
|
||||
DecorativeSprites.ForEach(s => s.Remove());
|
||||
ConditionalSprites.Clear();
|
||||
|
||||
ConditionalSprites.ForEach(s => s.Remove());
|
||||
ConditionalSprites.Clear();
|
||||
|
||||
LightSource?.Remove();
|
||||
LightSource = null;
|
||||
|
||||
OtherWearables?.ForEach(w => w.Sprite.Remove());
|
||||
OtherWearables = null;
|
||||
|
||||
HuskSprite?.Sprite.Remove();
|
||||
HuskSprite = null;
|
||||
|
||||
HerpesSprite?.Sprite.Remove();
|
||||
HerpesSprite = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
192
Barotrauma/BarotraumaClient/ClientSource/ChatManager.cs
Normal file
192
Barotrauma/BarotraumaClient/ClientSource/ChatManager.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
// TODO decide what folder this falls under. Utils? GUI? or just leave where it is. Also probably a better class name name [<- no need to create a separate namespace, maybe a folder?]
|
||||
/// <summary>
|
||||
/// A class used for handling special key actions in chat boxes.
|
||||
/// For example tab completion or up/down arrow key history.
|
||||
/// </summary>
|
||||
public class ChatManager
|
||||
{
|
||||
private readonly bool loop;
|
||||
|
||||
// Maximum items we want to store in the history
|
||||
private readonly short maxCount = 10;
|
||||
|
||||
// List of previously stored messages
|
||||
private readonly List<string> messageList = new List<string> { string.Empty };
|
||||
|
||||
/// Keep track of the registered fields so we don't register them twice
|
||||
/// I couldn't figure out where to register this in <see cref="NetLobbyScreen"/> where it wouldn't register twice
|
||||
/// It's probably not the most optimal way of doing this so feel free to change this
|
||||
/// <seealso cref="NetLobbyScreen.Select"/> where I'm utilizing this
|
||||
private readonly List<GUITextBox> registers = new List<GUITextBox>();
|
||||
|
||||
// Selector index
|
||||
private int index;
|
||||
|
||||
// Local changes we've made into previously stored messages
|
||||
private string[] localChanges;
|
||||
|
||||
public ChatManager(bool loop, short maxCount)
|
||||
{
|
||||
this.loop = loop;
|
||||
this.maxCount = maxCount;
|
||||
localChanges = new string[maxCount];
|
||||
}
|
||||
|
||||
public ChatManager()
|
||||
{
|
||||
localChanges = new string[maxCount];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Registers special input actions to the selected input field
|
||||
/// </summary>
|
||||
/// <param name="element">GUI Element we want to register</param>
|
||||
/// <param name="manager">Instance</param>
|
||||
public static void RegisterKeys(GUITextBox element, ChatManager manager)
|
||||
{
|
||||
// If already registered then don't register it again
|
||||
if (manager.registers.Any(p => element == p)) { return; }
|
||||
element.OnKeyHit += (sender, key) =>
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
// Up/Down Arrow key history action
|
||||
case Keys.Up:
|
||||
case Keys.Down:
|
||||
{
|
||||
// Up arrow key? go up. Down arrow key? go down. Everything else gets binned
|
||||
Direction direction = key == Keys.Up ? Direction.Up : (key == Keys.Down ? Direction.Down : Direction.Other);
|
||||
|
||||
string newMessage = manager.SelectMessage(direction, element.Text);
|
||||
// Don't do anything if we didn't find anything
|
||||
if (newMessage == null) { return; }
|
||||
|
||||
element.Text = newMessage;
|
||||
break;
|
||||
}
|
||||
case Keys.Tab:
|
||||
// TODO tab completion behavior, maybe?
|
||||
break;
|
||||
}
|
||||
};
|
||||
manager.registers.Add(element);
|
||||
}
|
||||
|
||||
// Store a new message
|
||||
public void Store(string message)
|
||||
{
|
||||
Clear();
|
||||
var strip = StripMessage(message);
|
||||
if (string.IsNullOrWhiteSpace(strip)) { return; }
|
||||
|
||||
if (messageList.Count > 1 && messageList[1] == message) { return; }
|
||||
|
||||
// insert to the second position as the first position is reserved for the original message if any
|
||||
messageList.Insert(1, message);
|
||||
// we don't want to add too many messages
|
||||
if (messageList.Count > maxCount)
|
||||
{
|
||||
messageList.RemoveAt(messageList.Count - 1);
|
||||
}
|
||||
|
||||
// [It's also possible to lambdas too in short methods, if you like: string StripMessage(string text) => ChatMessage.GetChatMessageCommand(text, out string msg);]
|
||||
static string StripMessage(string text)
|
||||
{
|
||||
ChatMessage.GetChatMessageCommand(text, out string msg);
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this function whenever we should stop doing special stuff and return normal behavior.
|
||||
/// For example when you deselect the chat box.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
index = 0;
|
||||
localChanges = new string[maxCount];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scroll up or down on the message history and return a message
|
||||
/// </summary>
|
||||
/// <param name="direction">Direction we want to scroll the stack</param>
|
||||
/// <param name="original">Leftover text that is in the chat box when we override it</param>
|
||||
/// <returns>A message or null</returns>
|
||||
private string SelectMessage(Direction direction, string original)
|
||||
{
|
||||
var originalIndex = index;
|
||||
while (true)
|
||||
{
|
||||
if (direction == Direction.Other)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// temporarily save our changes in case we fat-finger and want to go back
|
||||
localChanges[index] = original;
|
||||
|
||||
var dir = (int) direction;
|
||||
|
||||
var nextIndex = (index + dir);
|
||||
|
||||
if (loop && messageList.Count > 1)
|
||||
{
|
||||
nextIndex = LoopAround(nextIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nextIndex > messageList.Count - 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextIndex >= 0 && EntryAt(nextIndex) == original && nextIndex != originalIndex && originalIndex != 0)
|
||||
{
|
||||
index = nextIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
return nextIndex < 0 ? localChanges.FirstOrDefault() : EntryAt(index = nextIndex);
|
||||
|
||||
string EntryAt(int i)
|
||||
{
|
||||
// if we've previously edited the entry then give us that, else give us the original message
|
||||
return localChanges[i] ?? messageList[i];
|
||||
}
|
||||
|
||||
int LoopAround(int next)
|
||||
{
|
||||
if (next > (messageList.Count - 1))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (next < 1)
|
||||
{
|
||||
return messageList.Count - 1;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Used for Up/Down arrow key action
|
||||
private enum Direction
|
||||
{
|
||||
Up = 1,
|
||||
Down = -1,
|
||||
Other = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
2399
Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
Normal file
2399
Barotrauma/BarotraumaClient/ClientSource/DebugConsole.cs
Normal file
File diff suppressed because it is too large
Load Diff
129
Barotrauma/BarotraumaClient/ClientSource/Events/EventManager.cs
Normal file
129
Barotrauma/BarotraumaClient/ClientSource/Events/EventManager.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class EventManager
|
||||
{
|
||||
private Graph intensityGraph;
|
||||
private Graph targetIntensityGraph;
|
||||
private float intensityGraphUpdateInterval;
|
||||
private float lastIntensityUpdate;
|
||||
|
||||
public void DebugDraw(SpriteBatch spriteBatch)
|
||||
{
|
||||
foreach (ScriptedEvent ev in activeEvents)
|
||||
{
|
||||
Vector2 drawPos = ev.DebugDrawPos;
|
||||
drawPos.Y = -drawPos.Y;
|
||||
|
||||
var textOffset = new Vector2(-150, 0);
|
||||
ShapeExtensions.DrawCircle(spriteBatch, drawPos, 600, 6, Color.White, thickness: 20);
|
||||
GUI.DrawString(spriteBatch, drawPos + textOffset, ev.ToString(), Color.White, Color.Black, 0, GUI.LargeFont);
|
||||
}
|
||||
}
|
||||
|
||||
public void DebugDrawHUD(SpriteBatch spriteBatch, int y)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, new Vector2(10, y), "EventManager", Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
GUI.DrawString(spriteBatch, new Vector2(15, y + 20), "Event cooldown: " + eventCoolDown, Color.White, Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
GUI.DrawString(spriteBatch, new Vector2(15, y + 35), "Current intensity: " + (int)Math.Round(currentIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, currentIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
GUI.DrawString(spriteBatch, new Vector2(15, y + 50), "Target intensity: " + (int)Math.Round(targetIntensity * 100), Color.Lerp(Color.White, GUI.Style.Red, targetIntensity), Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
|
||||
GUI.DrawString(spriteBatch, new Vector2(15, y + 65), "AvgHealth: " + (int)Math.Round(avgCrewHealth * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgCrewHealth), Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
GUI.DrawString(spriteBatch, new Vector2(15, y + 80), "AvgHullIntegrity: " + (int)Math.Round(avgHullIntegrity * 100), Color.Lerp(GUI.Style.Red, GUI.Style.Green, avgHullIntegrity), Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
GUI.DrawString(spriteBatch, new Vector2(15, y + 95), "FloodingAmount: " + (int)Math.Round(floodingAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, floodingAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
GUI.DrawString(spriteBatch, new Vector2(15, y + 110), "FireAmount: " + (int)Math.Round(fireAmount * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, fireAmount), Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
GUI.DrawString(spriteBatch, new Vector2(15, y + 125), "EnemyDanger: " + (int)Math.Round(enemyDanger * 100), Color.Lerp(GUI.Style.Green, GUI.Style.Red, enemyDanger), Color.Black * 0.6f, 0, GUI.SmallFont);
|
||||
|
||||
#if DEBUG
|
||||
if (PlayerInput.KeyDown(Microsoft.Xna.Framework.Input.Keys.LeftAlt) &&
|
||||
PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.T))
|
||||
{
|
||||
eventCoolDown = 1.0f;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (intensityGraph == null)
|
||||
{
|
||||
intensityGraph = new Graph();
|
||||
targetIntensityGraph = new Graph();
|
||||
}
|
||||
|
||||
intensityGraphUpdateInterval = 5.0f;
|
||||
if (Timing.TotalTime > lastIntensityUpdate + intensityGraphUpdateInterval)
|
||||
{
|
||||
intensityGraph.Update(currentIntensity);
|
||||
targetIntensityGraph.Update(targetIntensity);
|
||||
lastIntensityUpdate = (float)Timing.TotalTime;
|
||||
}
|
||||
|
||||
Rectangle graphRect = new Rectangle(15, y + 150, 150, 50);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, graphRect, Color.Black * 0.5f, true);
|
||||
intensityGraph.Draw(spriteBatch, graphRect, 1.0f, 0.0f, Color.Lerp(Color.White, GUI.Style.Red, currentIntensity));
|
||||
targetIntensityGraph.Draw(spriteBatch, graphRect, 1.0f, 0.0f, Color.Lerp(Color.White, GUI.Style.Red, targetIntensity) * 0.5f);
|
||||
|
||||
GUI.DrawLine(spriteBatch,
|
||||
new Vector2(graphRect.Right, graphRect.Y + graphRect.Height * (1.0f - eventThreshold)),
|
||||
new Vector2(graphRect.Right + 5, graphRect.Y + graphRect.Height * (1.0f - eventThreshold)), Color.Orange, 0, 1);
|
||||
|
||||
y = graphRect.Bottom + 20;
|
||||
if (eventCoolDown > 0.0f)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "Event cooldown active: " + (int)eventCoolDown, Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
|
||||
y += 15;
|
||||
}
|
||||
else if (currentIntensity > eventThreshold)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
|
||||
"Intensity too high for new events: " + (int)(currentIntensity * 100) + "%/" + (int)(eventThreshold * 100) + "%", Color.LightGreen * 0.8f, null, 0, GUI.SmallFont);
|
||||
y += 15;
|
||||
}
|
||||
foreach (ScriptedEventSet eventSet in pendingEventSets)
|
||||
{
|
||||
float distanceTraveled = MathHelper.Clamp(
|
||||
(Submarine.MainSub.WorldPosition.X - level.StartPosition.X) / (level.EndPosition.X - level.StartPosition.X),
|
||||
0.0f, 1.0f);
|
||||
|
||||
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "New event (ID " + eventSet.DebugIdentifier + ") after: ", Color.Orange * 0.8f, null, 0, GUI.SmallFont);
|
||||
y += 12;
|
||||
|
||||
if ((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) &&
|
||||
roundDuration < eventSet.MinMissionTime)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
|
||||
" " + (int)(eventSet.MinDistanceTraveled * 100.0f) + "% travelled (current: " + (int)(distanceTraveled * 100.0f) + " %)",
|
||||
Color.Orange * 0.8f, null, 0, GUI.SmallFont);
|
||||
y += 12;
|
||||
}
|
||||
if (CurrentIntensity < eventSet.MinIntensity || CurrentIntensity > eventSet.MaxIntensity)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
|
||||
" intensity between " + ((int)eventSet.MinIntensity) + " and " + ((int)eventSet.MaxIntensity),
|
||||
Color.Orange * 0.8f, null, 0, GUI.SmallFont);
|
||||
y += 12;
|
||||
}
|
||||
if (roundDuration < eventSet.MinMissionTime)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y),
|
||||
" " + (int)(eventSet.MinMissionTime - roundDuration) + " s",
|
||||
Color.Orange * 0.8f, null, 0, GUI.SmallFont);
|
||||
}
|
||||
|
||||
y += 15;
|
||||
}
|
||||
|
||||
|
||||
GUI.DrawString(spriteBatch, new Vector2(graphRect.X, y), "Current events: ", Color.White * 0.9f, null, 0, GUI.SmallFont);
|
||||
y += 12;
|
||||
foreach (ScriptedEvent scriptedEvent in activeEvents)
|
||||
{
|
||||
if (scriptedEvent.IsFinished) { continue; }
|
||||
GUI.DrawString(spriteBatch, new Vector2(graphRect.X + 5, y), scriptedEvent.ToString(), Color.White * 0.8f, null, 0, GUI.SmallFont);
|
||||
y += 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class CargoMission : Mission
|
||||
{
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
{
|
||||
items.Add(Item.ReadSpawnData(msg));
|
||||
}
|
||||
if (items.Contains(null))
|
||||
{
|
||||
throw new System.Exception("Error in CargoMission.ClientReadInitial: item list contains null (mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
if (items.Count != itemCount)
|
||||
{
|
||||
throw new System.Exception("Error in CargoMission.ClientReadInitial: item count does not match the server count (" + itemCount + " != " + items.Count + "mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
if (requiredDeliveryAmount == 0) { requiredDeliveryAmount = items.Count; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class CombatMission
|
||||
{
|
||||
public override string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
if (descriptions == null) return "";
|
||||
|
||||
if (GameMain.Client?.Character == null)
|
||||
{
|
||||
//non-team-specific description
|
||||
return descriptions[0];
|
||||
}
|
||||
|
||||
//team specific
|
||||
return descriptions[GameMain.Client.Character.TeamID == Character.TeamType.Team1 ? 1 : 2];
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Mission
|
||||
{
|
||||
partial void ShowMessageProjSpecific(int missionState)
|
||||
{
|
||||
int messageIndex = missionState - 1;
|
||||
if (messageIndex >= Headers.Count && messageIndex >= Messages.Count) { return; }
|
||||
if (messageIndex < 0) { return; }
|
||||
|
||||
string header = messageIndex < Headers.Count ? Headers[messageIndex] : "";
|
||||
string message = messageIndex < Messages.Count ? Messages[messageIndex] : "";
|
||||
|
||||
new GUIMessageBox(header, message, buttons: new string[0], type: GUIMessageBox.Type.InGame, icon: Prefab.Icon)
|
||||
{
|
||||
IconColor = Prefab.IconColor
|
||||
};
|
||||
}
|
||||
|
||||
public void ClientRead(IReadMessage msg)
|
||||
{
|
||||
State = msg.ReadInt16();
|
||||
}
|
||||
|
||||
public abstract void ClientReadInitial(IReadMessage msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class MissionPrefab
|
||||
{
|
||||
public Sprite Icon
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Color IconColor
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("icon", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
Icon = new Sprite(subElement);
|
||||
IconColor = subElement.GetAttributeColor("color", Color.White);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class MonsterMission : Mission
|
||||
{
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
byte monsterCount = msg.ReadByte();
|
||||
for (int i = 0; i < monsterCount; i++)
|
||||
{
|
||||
monsters.Add(Character.ReadSpawnData(msg));
|
||||
}
|
||||
if (monsters.Contains(null))
|
||||
{
|
||||
throw new System.Exception("Error in MonsterMission.ClientReadInitial: monster list contains null (mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
if (monsters.Count != monsterCount)
|
||||
{
|
||||
throw new System.Exception("Error in MonsterMission.ClientReadInitial: monster count does not match the server count (" + monsterCount + " != " + monsters.Count + "mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
InitializeMonsters(monsters);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class SalvageMission : Mission
|
||||
{
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
item = Item.ReadSpawnData(msg);
|
||||
if (item == null)
|
||||
{
|
||||
throw new System.Exception("Error in SalvageMission.ClientReadInitial: spawned item was null (mission: " + Prefab.Identifier + ")");
|
||||
}
|
||||
|
||||
item.body.FarseerBody.BodyType = BodyType.Kinematic;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Extensions
|
||||
{
|
||||
public static class ColorExtensions
|
||||
{
|
||||
public static Color Multiply(this Color color, float value, bool onlyAlpha = false)
|
||||
{
|
||||
return onlyAlpha ?
|
||||
new Color(color.R, color.G, color.B, (byte)(color.A * value)) :
|
||||
new Color((byte)(color.R * value), (byte)(color.G * value), (byte)(color.B * value), (byte)(color.A * value));
|
||||
}
|
||||
|
||||
public static Color Opaque(this Color color)
|
||||
{
|
||||
return new Color(color.R, color.G, color.B, (byte)255);
|
||||
}
|
||||
}
|
||||
}
|
||||
618
Barotrauma/BarotraumaClient/ClientSource/Fonts/ScalableFont.cs
Normal file
618
Barotrauma/BarotraumaClient/ClientSource/Fonts/ScalableFont.cs
Normal file
@@ -0,0 +1,618 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using SharpFont;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class ColorData
|
||||
{
|
||||
public int StartIndex, EndIndex;
|
||||
public Color Color;
|
||||
|
||||
private const char colorDefinitionIndicator = '‖';
|
||||
private const char lineChangeIndicator = '\n';
|
||||
private const string colorDefinitionStartString = "‖color:";
|
||||
private const string coloringEndDefinition = "‖color:end‖";
|
||||
|
||||
public static List<ColorData> GetColorData(string text, out string sanitizedText)
|
||||
{
|
||||
List<ColorData> textColors = null;
|
||||
if (text != null && text.IndexOf(colorDefinitionIndicator) != -1 && text.Contains(colorDefinitionStartString))
|
||||
{
|
||||
textColors = new List<ColorData>();
|
||||
List<int> lineChangeIndexes = null;
|
||||
|
||||
int currentIndex = text.IndexOf(lineChangeIndicator);
|
||||
if (currentIndex != -1)
|
||||
{
|
||||
lineChangeIndexes = new List<int>();
|
||||
lineChangeIndexes.Add(currentIndex);
|
||||
int startIndex = currentIndex + 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (startIndex >= text.Length) break;
|
||||
currentIndex = text.IndexOf(lineChangeIndicator, startIndex);
|
||||
if (currentIndex == -1) break;
|
||||
lineChangeIndexes.Add(currentIndex);
|
||||
startIndex = currentIndex + 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (text.IndexOf(colorDefinitionStartString) != -1)
|
||||
{
|
||||
ColorData colorData = new ColorData();
|
||||
|
||||
int colorDefinitionStartIndex = text.IndexOf(colorDefinitionStartString);
|
||||
int colorDefinitionEndIndex = text.IndexOf(colorDefinitionIndicator, colorDefinitionStartIndex + 1);
|
||||
|
||||
string[] colorDefinition = text.Substring(colorDefinitionStartIndex + colorDefinitionStartString.Length, colorDefinitionEndIndex - colorDefinitionStartIndex - colorDefinitionStartString.Length).Split(',');
|
||||
|
||||
colorData.StartIndex = colorDefinitionStartIndex;
|
||||
colorData.Color = new Color(int.Parse(colorDefinition[0]), int.Parse(colorDefinition[1]), int.Parse(colorDefinition[2]));
|
||||
text = text.Remove(colorDefinitionStartIndex, colorDefinitionEndIndex - colorDefinitionStartIndex + 1);
|
||||
colorData.EndIndex = text.IndexOf(coloringEndDefinition);
|
||||
text = text.Remove(colorData.EndIndex, coloringEndDefinition.Length);
|
||||
|
||||
if (lineChangeIndexes != null)
|
||||
{
|
||||
for (int i = 0; i < lineChangeIndexes.Count; i++)
|
||||
{
|
||||
if (colorData.StartIndex > lineChangeIndexes[i])
|
||||
{
|
||||
colorData.StartIndex--;
|
||||
colorData.EndIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
textColors.Add(colorData);
|
||||
}
|
||||
}
|
||||
|
||||
sanitizedText = text;
|
||||
return textColors;
|
||||
}
|
||||
}
|
||||
public class ScalableFont : IDisposable
|
||||
{
|
||||
private static List<ScalableFont> FontList = new List<ScalableFont>();
|
||||
private static Library Lib = null;
|
||||
private static object mutex = new object();
|
||||
|
||||
private string filename;
|
||||
private Face face;
|
||||
private uint size;
|
||||
private int baseHeight;
|
||||
//private int lineHeight;
|
||||
private Dictionary<uint, GlyphData> texCoords;
|
||||
private List<Texture2D> textures;
|
||||
private GraphicsDevice graphicsDevice;
|
||||
|
||||
private Vector2 currentDynamicAtlasCoords;
|
||||
private int currentDynamicAtlasNextY;
|
||||
uint[] currentDynamicPixelBuffer;
|
||||
|
||||
public bool DynamicLoading
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool IsCJK
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public uint Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return size;
|
||||
}
|
||||
set
|
||||
{
|
||||
size = value;
|
||||
if (graphicsDevice != null) RenderAtlas(graphicsDevice, charRanges, texDims, baseChar);
|
||||
}
|
||||
}
|
||||
|
||||
private uint[] charRanges;
|
||||
private int texDims;
|
||||
private uint baseChar;
|
||||
|
||||
private struct GlyphData
|
||||
{
|
||||
public int texIndex;
|
||||
public Vector2 drawOffset;
|
||||
public float advance;
|
||||
public Rectangle texCoords;
|
||||
}
|
||||
|
||||
public ScalableFont(XElement element, GraphicsDevice gd = null)
|
||||
: this(
|
||||
element.GetAttributeString("file", ""),
|
||||
(uint)element.GetAttributeInt("size", 14),
|
||||
gd,
|
||||
element.GetAttributeBool("dynamicloading", false),
|
||||
element.GetAttributeBool("iscjk", false))
|
||||
{
|
||||
}
|
||||
|
||||
public ScalableFont(string filename, uint size, GraphicsDevice gd = null, bool dynamicLoading = false, bool isCJK = false)
|
||||
{
|
||||
lock (mutex)
|
||||
{
|
||||
if (Lib == null) Lib = new Library();
|
||||
this.filename = filename;
|
||||
this.face = null;
|
||||
foreach (ScalableFont font in FontList)
|
||||
{
|
||||
if (font.filename == filename)
|
||||
{
|
||||
this.face = font.face;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.face == null)
|
||||
{
|
||||
this.face = new Face(Lib, filename);
|
||||
}
|
||||
this.size = size;
|
||||
this.textures = new List<Texture2D>();
|
||||
this.texCoords = new Dictionary<uint, GlyphData>();
|
||||
this.DynamicLoading = dynamicLoading;
|
||||
this.IsCJK = isCJK;
|
||||
this.graphicsDevice = gd;
|
||||
|
||||
if (gd != null && !dynamicLoading)
|
||||
{
|
||||
RenderAtlas(gd);
|
||||
}
|
||||
|
||||
FontList.Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the font into at least one texture atlas, which is simply a collection of all glyphs in the ranges defined by charRanges.
|
||||
/// Don't call this too often or with very large sizes.
|
||||
/// </summary>
|
||||
/// <param name="gd">Graphics device, required to create textures.</param>
|
||||
/// <param name="charRanges">Character ranges between each even element with their corresponding odd element. Default is 0x20 to 0xFFFF.</param>
|
||||
/// <param name="texDims">Texture dimensions. Default is 512x512.</param>
|
||||
/// <param name="baseChar">Base character used to shift all other characters downwards when rendering. Defaults to T.</param>
|
||||
public void RenderAtlas(GraphicsDevice gd, uint[] charRanges = null, int texDims = 1024, uint baseChar = 0x54)
|
||||
{
|
||||
if (DynamicLoading) { return; }
|
||||
|
||||
if (charRanges == null)
|
||||
{
|
||||
charRanges = new uint[] { 0x20, 0xFFFF };
|
||||
}
|
||||
this.charRanges = charRanges;
|
||||
this.texDims = texDims;
|
||||
this.baseChar = baseChar;
|
||||
|
||||
textures.ForEach(t => t.Dispose());
|
||||
textures.Clear();
|
||||
texCoords.Clear();
|
||||
|
||||
uint[] pixelBuffer = new uint[texDims * texDims];
|
||||
for (int i = 0; i < texDims * texDims; i++)
|
||||
{
|
||||
pixelBuffer[i] = 0;
|
||||
}
|
||||
|
||||
CrossThread.RequestExecutionOnMainThread(() =>
|
||||
{
|
||||
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
|
||||
});
|
||||
int texIndex = 0;
|
||||
|
||||
Vector2 currentCoords = Vector2.Zero;
|
||||
int nextY = 0;
|
||||
|
||||
lock (mutex)
|
||||
{
|
||||
face.SetPixelSizes(0, size);
|
||||
face.LoadGlyph(face.GetCharIndex(baseChar), LoadFlags.Default, LoadTarget.Normal);
|
||||
baseHeight = face.Glyph.Metrics.Height.ToInt32();
|
||||
|
||||
for (int i = 0; i < charRanges.Length; i += 2)
|
||||
{
|
||||
uint start = charRanges[i];
|
||||
uint end = charRanges[i + 1];
|
||||
for (uint j = start; j <= end; j++)
|
||||
{
|
||||
uint glyphIndex = face.GetCharIndex(j);
|
||||
if (glyphIndex == 0) continue;
|
||||
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
|
||||
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
|
||||
{
|
||||
if (face.Glyph.Metrics.HorizontalAdvance > 0)
|
||||
{
|
||||
//glyph is empty, but char still applies advance
|
||||
GlyphData blankData = new GlyphData();
|
||||
blankData.advance = (float)face.Glyph.Metrics.HorizontalAdvance;
|
||||
blankData.texIndex = -1; //indicates no texture because the glyph is empty
|
||||
texCoords.Add(j, blankData);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//stacktrace doesn't really work that well when RenderGlyph throws an exception
|
||||
face.Glyph.RenderGlyph(RenderMode.Normal);
|
||||
byte[] bitmap = face.Glyph.Bitmap.BufferData;
|
||||
int glyphWidth = face.Glyph.Bitmap.Width;
|
||||
int glyphHeight = bitmap.Length / glyphWidth;
|
||||
|
||||
//if (glyphHeight>lineHeight) lineHeight=glyphHeight;
|
||||
|
||||
if (glyphWidth > texDims - 1 || glyphHeight > texDims - 1)
|
||||
{
|
||||
throw new Exception(filename + ", " + size.ToString() + ", " + (char)j + "; Glyph dimensions exceed texture atlas dimensions");
|
||||
}
|
||||
|
||||
nextY = Math.Max(nextY, glyphHeight + 2);
|
||||
|
||||
if (currentCoords.X + glyphWidth + 2 > texDims - 1)
|
||||
{
|
||||
currentCoords.X = 0;
|
||||
currentCoords.Y += nextY;
|
||||
nextY = 0;
|
||||
}
|
||||
if (currentCoords.Y + glyphHeight + 2 > texDims - 1)
|
||||
{
|
||||
currentCoords.X = 0;
|
||||
currentCoords.Y = 0;
|
||||
CrossThread.RequestExecutionOnMainThread(() =>
|
||||
{
|
||||
textures[texIndex].SetData<uint>(pixelBuffer);
|
||||
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
|
||||
});
|
||||
texIndex++;
|
||||
for (int k = 0; k < texDims * texDims; k++)
|
||||
{
|
||||
pixelBuffer[k] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GlyphData newData = new GlyphData
|
||||
{
|
||||
advance = (float)face.Glyph.Metrics.HorizontalAdvance,
|
||||
texIndex = texIndex,
|
||||
texCoords = new Rectangle((int)currentCoords.X, (int)currentCoords.Y, glyphWidth, glyphHeight),
|
||||
drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop)
|
||||
};
|
||||
texCoords.Add(j, newData);
|
||||
|
||||
for (int y = 0; y < glyphHeight; y++)
|
||||
{
|
||||
for (int x = 0; x < glyphWidth; x++)
|
||||
{
|
||||
byte byteColor = bitmap[x + y * glyphWidth];
|
||||
pixelBuffer[((int)currentCoords.X + x) + ((int)currentCoords.Y + y) * texDims] = (uint)(byteColor << 24 | 0x00ffffff);
|
||||
}
|
||||
}
|
||||
|
||||
currentCoords.X += glyphWidth + 2;
|
||||
}
|
||||
CrossThread.RequestExecutionOnMainThread(() =>
|
||||
{
|
||||
textures[texIndex].SetData<uint>(pixelBuffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DynamicRenderAtlas(GraphicsDevice gd, uint character, int texDims = 1024, uint baseChar = 0x54)
|
||||
{
|
||||
if (System.Threading.Thread.CurrentThread != GameMain.MainThread)
|
||||
{
|
||||
CrossThread.RequestExecutionOnMainThread(() =>
|
||||
{
|
||||
DynamicRenderAtlas(gd, character, texDims, baseChar);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] bitmap;
|
||||
int glyphWidth; int glyphHeight;
|
||||
Fixed26Dot6 horizontalAdvance;
|
||||
Vector2 drawOffset;
|
||||
|
||||
lock (mutex)
|
||||
{
|
||||
if (texCoords.ContainsKey(character)) { return; }
|
||||
if (textures.Count == 0)
|
||||
{
|
||||
this.texDims = texDims;
|
||||
this.baseChar = baseChar;
|
||||
face.SetPixelSizes(0, size);
|
||||
face.LoadGlyph(face.GetCharIndex(baseChar), LoadFlags.Default, LoadTarget.Normal);
|
||||
baseHeight = face.Glyph.Metrics.Height.ToInt32();
|
||||
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
|
||||
}
|
||||
|
||||
uint glyphIndex = face.GetCharIndex(character);
|
||||
if (glyphIndex == 0) { return; }
|
||||
|
||||
face.SetPixelSizes(0, size);
|
||||
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
|
||||
if (face.Glyph.Metrics.Width == 0 || face.Glyph.Metrics.Height == 0)
|
||||
{
|
||||
if (face.Glyph.Metrics.HorizontalAdvance > 0)
|
||||
{
|
||||
//glyph is empty, but char still applies advance
|
||||
GlyphData blankData = new GlyphData();
|
||||
blankData.advance = (float)face.Glyph.Metrics.HorizontalAdvance;
|
||||
blankData.texIndex = -1; //indicates no texture because the glyph is empty
|
||||
texCoords.Add(character, blankData);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//stacktrace doesn't really work that well when RenderGlyph throws an exception
|
||||
face.Glyph.RenderGlyph(RenderMode.Normal);
|
||||
bitmap = (byte[])face.Glyph.Bitmap.BufferData.Clone();
|
||||
glyphWidth = face.Glyph.Bitmap.Width;
|
||||
glyphHeight = bitmap.Length / glyphWidth;
|
||||
horizontalAdvance = face.Glyph.Metrics.HorizontalAdvance;
|
||||
drawOffset = new Vector2(face.Glyph.BitmapLeft, baseHeight * 14 / 10 - face.Glyph.BitmapTop);
|
||||
|
||||
if (glyphWidth > texDims - 1 || glyphHeight > texDims - 1)
|
||||
{
|
||||
throw new Exception(filename + ", " + size.ToString() + ", " + (char)character + "; Glyph dimensions exceed texture atlas dimensions");
|
||||
}
|
||||
|
||||
currentDynamicAtlasNextY = Math.Max(currentDynamicAtlasNextY, glyphHeight + 2);
|
||||
if (currentDynamicAtlasCoords.X + glyphWidth + 2 > texDims - 1)
|
||||
{
|
||||
currentDynamicAtlasCoords.X = 0;
|
||||
currentDynamicAtlasCoords.Y += currentDynamicAtlasNextY;
|
||||
currentDynamicAtlasNextY = 0;
|
||||
}
|
||||
//no more room in current texture atlas, create a new one
|
||||
if (currentDynamicAtlasCoords.Y + glyphHeight + 2 > texDims - 1)
|
||||
{
|
||||
currentDynamicAtlasCoords.X = 0;
|
||||
currentDynamicAtlasCoords.Y = 0;
|
||||
currentDynamicAtlasNextY = 0;
|
||||
textures.Add(new Texture2D(gd, texDims, texDims, false, SurfaceFormat.Color));
|
||||
currentDynamicPixelBuffer = null;
|
||||
}
|
||||
|
||||
GlyphData newData = new GlyphData
|
||||
{
|
||||
advance = (float)horizontalAdvance,
|
||||
texIndex = textures.Count - 1,
|
||||
texCoords = new Rectangle((int)currentDynamicAtlasCoords.X, (int)currentDynamicAtlasCoords.Y, glyphWidth, glyphHeight),
|
||||
drawOffset = drawOffset
|
||||
};
|
||||
texCoords.Add(character, newData);
|
||||
|
||||
if (currentDynamicPixelBuffer == null)
|
||||
{
|
||||
currentDynamicPixelBuffer = new uint[texDims * texDims];
|
||||
textures[newData.texIndex].GetData<uint>(currentDynamicPixelBuffer, 0, texDims * texDims);
|
||||
}
|
||||
|
||||
for (int y = 0; y < glyphHeight; y++)
|
||||
{
|
||||
for (int x = 0; x < glyphWidth; x++)
|
||||
{
|
||||
byte byteColor = bitmap[x + y * glyphWidth];
|
||||
currentDynamicPixelBuffer[((int)currentDynamicAtlasCoords.X + x) + ((int)currentDynamicAtlasCoords.Y + y) * texDims] = (uint)(byteColor << 24 | 0x00ffffff);
|
||||
}
|
||||
}
|
||||
textures[newData.texIndex].SetData<uint>(currentDynamicPixelBuffer);
|
||||
|
||||
currentDynamicAtlasCoords.X += glyphWidth + 2;
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth)
|
||||
{
|
||||
if (textures.Count == 0 && !DynamicLoading) { return; }
|
||||
|
||||
int lineNum = 0;
|
||||
Vector2 currentPos = position;
|
||||
Vector2 advanceUnit = rotation == 0.0f ? Vector2.UnitX : new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (text[i] == '\n')
|
||||
{
|
||||
lineNum++;
|
||||
currentPos = position;
|
||||
currentPos.X -= baseHeight * 1.8f * lineNum * advanceUnit.Y * scale.Y;
|
||||
currentPos.Y += baseHeight * 1.8f * lineNum * advanceUnit.X * scale.Y;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint charIndex = text[i];
|
||||
if (DynamicLoading)
|
||||
{
|
||||
DynamicRenderAtlas(graphicsDevice, charIndex);
|
||||
}
|
||||
|
||||
if (texCoords.TryGetValue(charIndex, out GlyphData gd) || texCoords.TryGetValue(9633, out gd)) //9633 = white square
|
||||
{
|
||||
if (gd.texIndex >= 0)
|
||||
{
|
||||
Texture2D tex = textures[gd.texIndex];
|
||||
Vector2 drawOffset;
|
||||
drawOffset.X = gd.drawOffset.X * advanceUnit.X * scale.X - gd.drawOffset.Y * advanceUnit.Y * scale.Y;
|
||||
drawOffset.Y = gd.drawOffset.X * advanceUnit.Y * scale.Y + gd.drawOffset.Y * advanceUnit.X * scale.X;
|
||||
|
||||
sb.Draw(tex, currentPos + drawOffset, gd.texCoords, color, rotation, origin, scale, se, layerDepth);
|
||||
}
|
||||
currentPos += gd.advance * advanceUnit * scale.X;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth)
|
||||
{
|
||||
DrawString(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth);
|
||||
}
|
||||
|
||||
public void DrawString(SpriteBatch sb, string text, Vector2 position, Color color)
|
||||
{
|
||||
if (textures.Count == 0 && !DynamicLoading) { return; }
|
||||
|
||||
Vector2 currentPos = position;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (text[i] == '\n')
|
||||
{
|
||||
currentPos.X = position.X;
|
||||
currentPos.Y += baseHeight * 1.8f;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint charIndex = text[i];
|
||||
if (DynamicLoading)
|
||||
{
|
||||
DynamicRenderAtlas(graphicsDevice, charIndex);
|
||||
}
|
||||
|
||||
if (texCoords.TryGetValue(charIndex, out GlyphData gd) || texCoords.TryGetValue(9633, out gd)) //9633 = white square
|
||||
{
|
||||
if (gd.texIndex >= 0)
|
||||
{
|
||||
Texture2D tex = textures[gd.texIndex];
|
||||
sb.Draw(tex, currentPos + gd.drawOffset, gd.texCoords, color);
|
||||
}
|
||||
currentPos.X += gd.advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, float scale, SpriteEffects se, float layerDepth, List<ColorData> colorData)
|
||||
{
|
||||
DrawStringWithColors(sb, text, position, color, rotation, origin, new Vector2(scale), se, layerDepth, colorData);
|
||||
}
|
||||
|
||||
public void DrawStringWithColors(SpriteBatch sb, string text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects se, float layerDepth, List<ColorData> colorData)
|
||||
{
|
||||
if (textures.Count == 0 && !DynamicLoading) { return; }
|
||||
|
||||
int lineNum = 0;
|
||||
Vector2 currentPos = position;
|
||||
Vector2 advanceUnit = rotation == 0.0f ? Vector2.UnitX : new Vector2((float)Math.Cos(rotation), (float)Math.Sin(rotation));
|
||||
|
||||
int colorDataIndex = 0;
|
||||
ColorData currentColorData = colorData[colorDataIndex];
|
||||
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (text[i] == '\n')
|
||||
{
|
||||
lineNum++;
|
||||
currentPos = position;
|
||||
currentPos.X -= baseHeight * 1.8f * lineNum * advanceUnit.Y * scale.Y;
|
||||
currentPos.Y += baseHeight * 1.8f * lineNum * advanceUnit.X * scale.Y;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint charIndex = text[i];
|
||||
if (DynamicLoading && !texCoords.ContainsKey(charIndex))
|
||||
{
|
||||
DynamicRenderAtlas(graphicsDevice, charIndex);
|
||||
}
|
||||
|
||||
Color currentTextColor;
|
||||
|
||||
if (currentColorData != null && i > currentColorData.EndIndex + lineNum)
|
||||
{
|
||||
colorDataIndex++;
|
||||
currentColorData = colorDataIndex < colorData.Count ? colorData[colorDataIndex] : null;
|
||||
}
|
||||
|
||||
if (currentColorData != null && currentColorData.StartIndex + lineNum <= i && i <= currentColorData.EndIndex + lineNum)
|
||||
{
|
||||
currentTextColor = currentColorData.Color;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentTextColor = color;
|
||||
}
|
||||
|
||||
if (texCoords.TryGetValue(charIndex, out GlyphData gd) || texCoords.TryGetValue(9633, out gd)) //9633 = white square
|
||||
{
|
||||
if (gd.texIndex >= 0)
|
||||
{
|
||||
Texture2D tex = textures[gd.texIndex];
|
||||
Vector2 drawOffset;
|
||||
drawOffset.X = gd.drawOffset.X * advanceUnit.X * scale.X - gd.drawOffset.Y * advanceUnit.Y * scale.Y;
|
||||
drawOffset.Y = gd.drawOffset.X * advanceUnit.Y * scale.Y + gd.drawOffset.Y * advanceUnit.X * scale.X;
|
||||
|
||||
sb.Draw(tex, currentPos + drawOffset, gd.texCoords, currentTextColor, rotation, origin, scale, se, layerDepth);
|
||||
}
|
||||
currentPos += gd.advance * advanceUnit * scale.X;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 MeasureString(string text)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
float currentLineX = 0.0f;
|
||||
Vector2 retVal = Vector2.Zero;
|
||||
retVal.Y = baseHeight * 1.8f;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
if (text[i] == '\n')
|
||||
{
|
||||
currentLineX = 0.0f;
|
||||
retVal.Y += baseHeight * 1.8f;
|
||||
continue;
|
||||
}
|
||||
uint charIndex = text[i];
|
||||
if (DynamicLoading && !texCoords.ContainsKey(charIndex))
|
||||
{
|
||||
DynamicRenderAtlas(graphicsDevice, charIndex);
|
||||
}
|
||||
if (texCoords.TryGetValue(charIndex, out GlyphData gd))
|
||||
{
|
||||
currentLineX += gd.advance;
|
||||
}
|
||||
retVal.X = Math.Max(retVal.X, currentLineX);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public Vector2 MeasureChar(char c)
|
||||
{
|
||||
Vector2 retVal = Vector2.Zero;
|
||||
retVal.Y = baseHeight * 1.8f;
|
||||
if (DynamicLoading && !texCoords.ContainsKey(c))
|
||||
{
|
||||
DynamicRenderAtlas(graphicsDevice, c);
|
||||
}
|
||||
if (texCoords.TryGetValue(c, out GlyphData gd))
|
||||
{
|
||||
retVal.X = gd.advance;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FontList.Remove(this);
|
||||
foreach (Texture2D texture in textures)
|
||||
{
|
||||
texture.Dispose();
|
||||
}
|
||||
textures.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
409
Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs
Normal file
409
Barotrauma/BarotraumaClient/ClientSource/GUI/ChatBox.cs
Normal file
@@ -0,0 +1,409 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class ChatBox
|
||||
{
|
||||
public const string RadioChatString = "r; ";
|
||||
|
||||
private GUIListBox chatBox;
|
||||
private Point screenResolution;
|
||||
|
||||
public readonly ChatManager ChatManager = new ChatManager();
|
||||
|
||||
public bool IsSinglePlayer { get; private set; }
|
||||
|
||||
private bool _toggleOpen = true;
|
||||
public bool ToggleOpen
|
||||
{
|
||||
get { return _toggleOpen; }
|
||||
set
|
||||
{
|
||||
_toggleOpen = GameMain.Config.ChatOpen = value;
|
||||
if (value) hideableElements.Visible = true;
|
||||
}
|
||||
}
|
||||
private float openState;
|
||||
|
||||
public bool CloseAfterMessageSent;
|
||||
|
||||
private float prevUIScale;
|
||||
|
||||
//individual message texts that pop up when the chatbox is hidden
|
||||
const float PopupMessageDuration = 5.0f;
|
||||
private float popupMessageTimer;
|
||||
private Queue<GUIComponent> popupMessages = new Queue<GUIComponent>();
|
||||
|
||||
public GUITextBox.OnEnterHandler OnEnterMessage
|
||||
{
|
||||
get { return InputBox.OnEnterPressed; }
|
||||
set { InputBox.OnEnterPressed = value; }
|
||||
}
|
||||
|
||||
public GUIFrame GUIFrame { get; private set; }
|
||||
|
||||
public GUITextBox InputBox { get; private set; }
|
||||
|
||||
public GUIButton ToggleButton;
|
||||
|
||||
private GUIButton showNewMessagesButton;
|
||||
|
||||
private GUIFrame hideableElements;
|
||||
|
||||
public const int ToggleButtonWidthRaw = 30;
|
||||
private int popupMessageOffset;
|
||||
|
||||
public ChatBox(GUIComponent parent, bool isSinglePlayer)
|
||||
{
|
||||
this.IsSinglePlayer = isSinglePlayer;
|
||||
|
||||
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
|
||||
int toggleButtonWidth = (int)(ToggleButtonWidthRaw * GUI.Scale);
|
||||
GUIFrame = new GUIFrame(HUDLayoutSettings.ToRectTransform(HUDLayoutSettings.ChatBoxArea, parent.RectTransform), style: null);
|
||||
|
||||
hideableElements = new GUIFrame(new RectTransform(Vector2.One, GUIFrame.RectTransform), style: null);
|
||||
|
||||
var chatBoxHolder = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.875f), hideableElements.RectTransform), style: "ChatBox");
|
||||
chatBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.95f), chatBoxHolder.RectTransform, Anchor.CenterRight), style: null);
|
||||
|
||||
InputBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 0.125f), hideableElements.RectTransform, Anchor.BottomLeft),
|
||||
style: "ChatTextBox")
|
||||
{
|
||||
OverflowClip = true,
|
||||
Font = GUI.SmallFont,
|
||||
MaxTextLength = ChatMessage.MaxLength
|
||||
};
|
||||
|
||||
ChatManager.RegisterKeys(InputBox, ChatManager);
|
||||
|
||||
InputBox.OnDeselected += (gui, Keys) =>
|
||||
{
|
||||
ChatManager.Clear();
|
||||
ChatMessage.GetChatMessageCommand(InputBox.Text, out var message);
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
if (CloseAfterMessageSent)
|
||||
{
|
||||
_toggleOpen = false;
|
||||
CloseAfterMessageSent = false;
|
||||
}
|
||||
}
|
||||
|
||||
//gui.Text = "";
|
||||
};
|
||||
|
||||
var chatSendButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.7f), InputBox.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight), style: "GUIButtonToggleRight");
|
||||
chatSendButton.OnClicked += (GUIButton btn, object userdata) =>
|
||||
{
|
||||
InputBox.OnEnterPressed(InputBox, InputBox.Text);
|
||||
return true;
|
||||
};
|
||||
chatSendButton.RectTransform.AbsoluteOffset = new Point((int)(InputBox.Rect.Height * 0.15f), 0);
|
||||
InputBox.TextBlock.RectTransform.MaxSize
|
||||
= new Point((int)(InputBox.Rect.Width - chatSendButton.Rect.Width * 1.25f - InputBox.TextBlock.Padding.Z), int.MaxValue);
|
||||
|
||||
showNewMessagesButton = new GUIButton(new RectTransform(new Vector2(1f, 0.075f), GUIFrame.RectTransform, Anchor.BottomCenter) { RelativeOffset = new Vector2(0.0f, 0.125f) }, TextManager.Get("chat.shownewmessages"));
|
||||
showNewMessagesButton.OnClicked += (GUIButton btn, object userdata) =>
|
||||
{
|
||||
chatBox.ScrollBar.BarScrollValue = 1f;
|
||||
showNewMessagesButton.Visible = false;
|
||||
return true;
|
||||
};
|
||||
|
||||
showNewMessagesButton.Visible = false;
|
||||
ToggleOpen = GameMain.Config.ChatOpen;
|
||||
}
|
||||
|
||||
public bool TypingChatMessage(GUITextBox textBox, string text)
|
||||
{
|
||||
string command = ChatMessage.GetChatMessageCommand(text, out _);
|
||||
if (IsSinglePlayer)
|
||||
{
|
||||
//radio is the only allowed special message type in single player
|
||||
if (command != "r" && command != "radio")
|
||||
{
|
||||
command = "";
|
||||
}
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case "r":
|
||||
case "radio":
|
||||
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Radio];
|
||||
break;
|
||||
case "d":
|
||||
case "dead":
|
||||
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Dead];
|
||||
break;
|
||||
default:
|
||||
if (Character.Controlled != null && (Character.Controlled.IsDead || Character.Controlled.SpeechImpediment >= 100.0f))
|
||||
{
|
||||
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Dead];
|
||||
}
|
||||
else if (command != "") //PMing
|
||||
{
|
||||
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Private];
|
||||
}
|
||||
else
|
||||
{
|
||||
textBox.TextColor = ChatMessage.MessageColor[(int)ChatMessageType.Default];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddMessage(ChatMessage message)
|
||||
{
|
||||
while (chatBox.Content.CountChildren > 60)
|
||||
{
|
||||
chatBox.RemoveChild(chatBox.Content.Children.First());
|
||||
}
|
||||
|
||||
float prevSize = chatBox.BarSize;
|
||||
|
||||
string displayedText = message.TranslatedText;
|
||||
string senderName = "";
|
||||
Color senderColor = Color.White;
|
||||
if (!string.IsNullOrWhiteSpace(message.SenderName))
|
||||
{
|
||||
senderName = (message.Type == ChatMessageType.Private ? "[PM] " : "") + message.SenderName;
|
||||
}
|
||||
if (message.Sender?.Info?.Job != null)
|
||||
{
|
||||
senderColor = Color.Lerp(message.Sender.Info.Job.Prefab.UIColor, Color.White, 0.25f);
|
||||
}
|
||||
|
||||
var msgHolder = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.0f), chatBox.Content.RectTransform, Anchor.TopCenter), style: null,
|
||||
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f);
|
||||
|
||||
GUITextBlock senderNameBlock = new GUITextBlock(new RectTransform(new Vector2(0.98f, 0.0f), msgHolder.RectTransform) { AbsoluteOffset = new Point((int)(5 * GUI.Scale), 0) },
|
||||
ChatMessage.GetTimeStamp(), textColor: Color.LightGray, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null)
|
||||
{
|
||||
CanBeFocused = true
|
||||
};
|
||||
if (!string.IsNullOrEmpty(senderName))
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.8f, 1.0f), senderNameBlock.RectTransform) { AbsoluteOffset = new Point((int)(senderNameBlock.TextSize.X), 0) },
|
||||
senderName, textColor: senderColor, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null)
|
||||
{
|
||||
CanBeFocused = true
|
||||
};
|
||||
}
|
||||
|
||||
var msgText =new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), msgHolder.RectTransform)
|
||||
{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), senderNameBlock == null ? 0 : senderNameBlock.Rect.Height) },
|
||||
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.TopLeft, style: null, wrap: true,
|
||||
color: ((chatBox.Content.CountChildren % 2) == 0) ? Color.Transparent : Color.Black * 0.1f)
|
||||
{
|
||||
UserData = message.SenderName,
|
||||
CanBeFocused = true
|
||||
};
|
||||
|
||||
if (message is OrderChatMessage orderChatMsg &&
|
||||
Character.Controlled != null &&
|
||||
orderChatMsg.TargetCharacter == Character.Controlled)
|
||||
{
|
||||
msgHolder.Flash(Color.OrangeRed * 0.6f, flashDuration: 5.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
msgHolder.Flash(Color.Yellow * 0.6f);
|
||||
}
|
||||
msgHolder.RectTransform.SizeChanged += Recalculate;
|
||||
Recalculate();
|
||||
void Recalculate()
|
||||
{
|
||||
msgHolder.RectTransform.SizeChanged -= Recalculate;
|
||||
//resize the holder to match the size of the message and add some spacing
|
||||
msgText.RectTransform.MaxSize = new Point(msgHolder.Rect.Width - msgText.RectTransform.AbsoluteOffset.X, int.MaxValue);
|
||||
senderNameBlock.RectTransform.MaxSize = new Point(msgHolder.Rect.Width - senderNameBlock.RectTransform.AbsoluteOffset.X, int.MaxValue);
|
||||
msgHolder.Children.ForEach(c => (c as GUITextBlock)?.CalculateHeightFromText());
|
||||
msgHolder.RectTransform.Resize(new Point(msgHolder.Rect.Width, msgHolder.Children.Sum(c => c.Rect.Height) + (int)(10 * GUI.Scale)), resizeChildren: false);
|
||||
msgHolder.RectTransform.SizeChanged += Recalculate;
|
||||
chatBox.RecalculateChildren();
|
||||
chatBox.UpdateScrollBarSize();
|
||||
}
|
||||
|
||||
CoroutineManager.StartCoroutine(UpdateMessageAnimation(msgHolder, 0.5f));
|
||||
|
||||
chatBox.UpdateScrollBarSize();
|
||||
|
||||
if (chatBox.ScrollBar.Visible && chatBox.ScrollBar.BarScroll < 1f)
|
||||
{
|
||||
showNewMessagesButton.Visible = true;
|
||||
}
|
||||
|
||||
if (!ToggleOpen)
|
||||
{
|
||||
var popupMsg = new GUIFrame(new RectTransform(Vector2.One, GUIFrame.RectTransform), style: "GUIToolTip")
|
||||
{
|
||||
Visible = false,
|
||||
CanBeFocused = false
|
||||
};
|
||||
var content = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), popupMsg.RectTransform, Anchor.Center));
|
||||
Vector2 senderTextSize = Vector2.Zero;
|
||||
if (!string.IsNullOrEmpty(senderName))
|
||||
{
|
||||
var senderText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
|
||||
senderName, textColor: senderColor, style: null, font: GUI.SmallFont)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
senderTextSize = senderText.Font.MeasureString(senderText.WrappedText);
|
||||
senderText.RectTransform.MinSize = new Point(0, senderText.Rect.Height);
|
||||
}
|
||||
var msgPopupText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform),
|
||||
displayedText, textColor: message.Color, font: GUI.SmallFont, textAlignment: Alignment.BottomLeft, style: null, wrap: true)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
msgPopupText.RectTransform.MinSize = new Point(0, msgPopupText.Rect.Height);
|
||||
Vector2 msgSize = msgPopupText.Font.MeasureString(msgPopupText.WrappedText);
|
||||
int textWidth = (int)Math.Max(msgSize.X + msgPopupText.Padding.X + msgPopupText.Padding.Z, senderTextSize.X) + 10;
|
||||
popupMsg.RectTransform.Resize(new Point((int)(textWidth / content.RectTransform.RelativeSize.X) , (int)((senderTextSize.Y + msgSize.Y) / content.RectTransform.RelativeSize.Y)), resizeChildren: true);
|
||||
popupMsg.RectTransform.IsFixedSize = true;
|
||||
content.Recalculate();
|
||||
popupMessages.Enqueue(popupMsg);
|
||||
}
|
||||
|
||||
if ((prevSize == 1.0f && chatBox.BarScroll == 0.0f) || (prevSize < 1.0f && chatBox.BarScroll == 1.0f)) chatBox.BarScroll = 1.0f;
|
||||
|
||||
GUISoundType soundType = GUISoundType.ChatMessage;
|
||||
if (message.Type == ChatMessageType.Radio)
|
||||
{
|
||||
soundType = GUISoundType.RadioMessage;
|
||||
}
|
||||
else if (message.Type == ChatMessageType.Dead)
|
||||
{
|
||||
soundType = GUISoundType.DeadMessage;
|
||||
}
|
||||
|
||||
GUI.PlayUISound(soundType);
|
||||
}
|
||||
|
||||
public void SetVisibility(bool visible)
|
||||
{
|
||||
GUIFrame.Parent.Visible = visible;
|
||||
}
|
||||
|
||||
private IEnumerable<object> UpdateMessageAnimation(GUIComponent message, float animDuration)
|
||||
{
|
||||
float timer = 0.0f;
|
||||
while (timer < animDuration)
|
||||
{
|
||||
timer += CoroutineManager.DeltaTime;
|
||||
float wavePhase = timer / animDuration * MathHelper.TwoPi;
|
||||
message.RectTransform.ScreenSpaceOffset =
|
||||
new Point((int)(Math.Sin(wavePhase) * (1.0f - timer / animDuration) * 50.0f), 0);
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
message.RectTransform.ScreenSpaceOffset = Point.Zero;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
private void SetUILayout()
|
||||
{
|
||||
GUIFrame.RectTransform.AbsoluteOffset = Point.Zero;
|
||||
GUIFrame.RectTransform.RelativeOffset = new Vector2(
|
||||
HUDLayoutSettings.ChatBoxArea.X / (float)GameMain.GraphicsWidth,
|
||||
HUDLayoutSettings.ChatBoxArea.Y / (float)GameMain.GraphicsHeight);
|
||||
GUIFrame.RectTransform.NonScaledSize = HUDLayoutSettings.ChatBoxArea.Size;
|
||||
|
||||
int toggleButtonWidth = (int)(ToggleButtonWidthRaw * GUI.Scale);
|
||||
GUIFrame.RectTransform.NonScaledSize -= new Point(toggleButtonWidth, 0);
|
||||
GUIFrame.RectTransform.AbsoluteOffset += new Point(toggleButtonWidth, 0);
|
||||
|
||||
popupMessageOffset = GameMain.GameSession.CrewManager.ReportButtonFrame.Rect.Width + GUIFrame.Rect.Width + (int)(20 * GUI.Scale);
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y || prevUIScale != GUI.Scale)
|
||||
{
|
||||
SetUILayout();
|
||||
screenResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
prevUIScale = GUI.Scale;
|
||||
}
|
||||
|
||||
//hide chatbox when accessing the inventory of another character to prevent overlaps
|
||||
if (Character.Controlled?.SelectedCharacter?.Inventory != null &&
|
||||
Character.Controlled.SelectedCharacter.CanInventoryBeAccessed)
|
||||
{
|
||||
SetVisibility(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVisibility(true);
|
||||
}
|
||||
|
||||
if (showNewMessagesButton.Visible && chatBox.ScrollBar.BarScroll == 1f)
|
||||
{
|
||||
showNewMessagesButton.Visible = false;
|
||||
}
|
||||
|
||||
if (ToggleButton != null)
|
||||
{
|
||||
ToggleButton.RectTransform.AbsoluteOffset = new Point(GUIFrame.Rect.Right, GUIFrame.Rect.Y + HUDLayoutSettings.ChatBoxArea.Height - ToggleButton.Rect.Height);
|
||||
}
|
||||
|
||||
if (ToggleOpen)
|
||||
{
|
||||
openState += deltaTime * 5.0f;
|
||||
//delete all popup messages when the chatbox is open
|
||||
while (popupMessages.Count > 0)
|
||||
{
|
||||
var popupMsg = popupMessages.Dequeue();
|
||||
popupMsg.Parent.RemoveChild(popupMsg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
openState -= deltaTime * 5.0f;
|
||||
|
||||
//make the first popup message visible
|
||||
var popupMsg = popupMessages.Count > 0 ? popupMessages.Peek() : null;
|
||||
if (popupMsg != null)
|
||||
{
|
||||
popupMsg.Visible = true;
|
||||
//popup messages appear and disappear faster when there's more pending messages
|
||||
popupMessageTimer += deltaTime * popupMessages.Count * popupMessages.Count;
|
||||
if (popupMessageTimer > PopupMessageDuration)
|
||||
{
|
||||
//move the message out of the screen and delete it
|
||||
popupMsg.RectTransform.ScreenSpaceOffset =
|
||||
new Point((int)MathHelper.SmoothStep(popupMessageOffset, 10, (popupMessageTimer - PopupMessageDuration) * 5.0f), 0);
|
||||
if (popupMessageTimer > PopupMessageDuration + 1.0f)
|
||||
{
|
||||
popupMessageTimer = 0.0f;
|
||||
popupMsg.Parent.RemoveChild(popupMsg);
|
||||
popupMessages.Dequeue();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//move the message on the screen
|
||||
popupMsg.RectTransform.ScreenSpaceOffset = new Point(
|
||||
(int)MathHelper.SmoothStep(0, popupMessageOffset, popupMessageTimer * 5.0f), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
openState = MathHelper.Clamp(openState, 0.0f, 1.0f);
|
||||
int hiddenBoxOffset = -(GUIFrame.Rect.Width);
|
||||
GUIFrame.RectTransform.AbsoluteOffset =
|
||||
new Point((int)MathHelper.SmoothStep(hiddenBoxOffset, 0, openState), 0);
|
||||
hideableElements.Visible = openState > 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
176
Barotrauma/BarotraumaClient/ClientSource/GUI/ComponentStyle.cs
Normal file
176
Barotrauma/BarotraumaClient/ClientSource/GUI/ComponentStyle.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public enum TransitionMode
|
||||
{
|
||||
Linear,
|
||||
Smooth,
|
||||
Smoother,
|
||||
EaseIn,
|
||||
EaseOut,
|
||||
Exponential
|
||||
}
|
||||
|
||||
public enum SpriteFallBackState
|
||||
{
|
||||
None,
|
||||
Hover,
|
||||
Pressed,
|
||||
Selected,
|
||||
HoverSelected,
|
||||
Toggle
|
||||
}
|
||||
|
||||
public class GUIComponentStyle
|
||||
{
|
||||
public readonly Vector4 Padding;
|
||||
|
||||
public readonly Color Color;
|
||||
public readonly Color HoverColor;
|
||||
public readonly Color SelectedColor;
|
||||
public readonly Color PressedColor;
|
||||
public readonly Color DisabledColor;
|
||||
|
||||
public readonly Color TextColor;
|
||||
public readonly Color HoverTextColor;
|
||||
public readonly Color SelectedTextColor;
|
||||
public readonly Color DisabledTextColor;
|
||||
|
||||
public readonly float SpriteCrossFadeTime;
|
||||
public readonly float ColorCrossFadeTime;
|
||||
public readonly TransitionMode TransitionMode;
|
||||
|
||||
public readonly string Font;
|
||||
public readonly bool ForceUpperCase;
|
||||
|
||||
public readonly Color OutlineColor;
|
||||
|
||||
public readonly XElement Element;
|
||||
|
||||
public readonly Dictionary<GUIComponent.ComponentState, List<UISprite>> Sprites;
|
||||
|
||||
public SpriteFallBackState FallBackState;
|
||||
|
||||
public Dictionary<string, GUIComponentStyle> ChildStyles;
|
||||
|
||||
public readonly GUIStyle Style;
|
||||
|
||||
public readonly string Name;
|
||||
|
||||
public int? Width { get; private set; }
|
||||
public int? Height { get; private set; }
|
||||
|
||||
public GUIComponentStyle(XElement element, GUIStyle style)
|
||||
{
|
||||
Name = element.Name.LocalName;
|
||||
|
||||
Style = style;
|
||||
Element = element;
|
||||
|
||||
Sprites = new Dictionary<GUIComponent.ComponentState, List<UISprite>>();
|
||||
foreach (GUIComponent.ComponentState state in Enum.GetValues(typeof(GUIComponent.ComponentState)))
|
||||
{
|
||||
Sprites[state] = new List<UISprite>();
|
||||
}
|
||||
|
||||
ChildStyles = new Dictionary<string, GUIComponentStyle>();
|
||||
|
||||
Padding = element.GetAttributeVector4("padding", Vector4.Zero);
|
||||
|
||||
Color = element.GetAttributeColor("color", Color.Transparent);
|
||||
HoverColor = element.GetAttributeColor("hovercolor", Color);
|
||||
SelectedColor = element.GetAttributeColor("selectedcolor", Color);
|
||||
DisabledColor = element.GetAttributeColor("disabledcolor", Color);
|
||||
PressedColor = element.GetAttributeColor("pressedcolor", Color);
|
||||
OutlineColor = element.GetAttributeColor("outlinecolor", Color.Transparent);
|
||||
|
||||
TextColor = element.GetAttributeColor("textcolor", Color.Black);
|
||||
HoverTextColor = element.GetAttributeColor("hovertextcolor", TextColor);
|
||||
DisabledTextColor = element.GetAttributeColor("disabledtextcolor", TextColor);
|
||||
SelectedTextColor = element.GetAttributeColor("selectedtextcolor", TextColor);
|
||||
SpriteCrossFadeTime = element.GetAttributeFloat("spritefadetime", SpriteCrossFadeTime);
|
||||
ColorCrossFadeTime = element.GetAttributeFloat("colorfadetime", ColorCrossFadeTime);
|
||||
|
||||
if (Enum.TryParse(element.GetAttributeString("colortransition", string.Empty), ignoreCase: true, out TransitionMode transition))
|
||||
{
|
||||
TransitionMode = transition;
|
||||
}
|
||||
if (Enum.TryParse(element.GetAttributeString("fallbackstate", GUIComponent.ComponentState.None.ToString()), ignoreCase: true, out SpriteFallBackState s))
|
||||
{
|
||||
FallBackState = s;
|
||||
}
|
||||
|
||||
Font = element.GetAttributeString("font", "");
|
||||
ForceUpperCase = element.GetAttributeBool("forceuppercase", false);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
UISprite newSprite = new UISprite(subElement);
|
||||
|
||||
GUIComponent.ComponentState spriteState = GUIComponent.ComponentState.None;
|
||||
if (subElement.Attribute("state") != null)
|
||||
{
|
||||
string stateStr = subElement.GetAttributeString("state", "None");
|
||||
Enum.TryParse(stateStr, out spriteState);
|
||||
Sprites[spriteState].Add(newSprite);
|
||||
//use the same sprite for Hover and HoverSelected if latter is not specified
|
||||
if (spriteState == GUIComponent.ComponentState.HoverSelected && !Sprites.ContainsKey(GUIComponent.ComponentState.HoverSelected))
|
||||
{
|
||||
Sprites[GUIComponent.ComponentState.HoverSelected].Add(newSprite);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (GUIComponent.ComponentState state in Enum.GetValues(typeof(GUIComponent.ComponentState)))
|
||||
{
|
||||
Sprites[state].Add(newSprite);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "size":
|
||||
break;
|
||||
default:
|
||||
string styleName = subElement.Name.ToString().ToLowerInvariant();
|
||||
if (ChildStyles.ContainsKey(styleName))
|
||||
{
|
||||
DebugConsole.ThrowError("UI style \"" + element.Name.ToString() + "\" contains multiple child styles with the same name (\"" + styleName + "\")!");
|
||||
ChildStyles[styleName] = new GUIComponentStyle(subElement, style);
|
||||
}
|
||||
else
|
||||
{
|
||||
ChildStyles.Add(styleName, new GUIComponentStyle(subElement, style));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GetSize(element);
|
||||
}
|
||||
|
||||
public void GetSize(XElement element)
|
||||
{
|
||||
Point size = new Point(0, 0);
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("size", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
|
||||
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
|
||||
{
|
||||
size = new Point(
|
||||
subElement.GetAttributeInt("width", 0),
|
||||
subElement.GetAttributeInt("height", 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (size.X > 0) { Width = size.X; }
|
||||
if (size.Y > 0) { Height = size.Y; }
|
||||
}
|
||||
}
|
||||
}
|
||||
405
Barotrauma/BarotraumaClient/ClientSource/GUI/FileSelection.cs
Normal file
405
Barotrauma/BarotraumaClient/ClientSource/GUI/FileSelection.cs
Normal file
@@ -0,0 +1,405 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public static class FileSelection
|
||||
{
|
||||
private static bool open;
|
||||
public static bool Open
|
||||
{
|
||||
get
|
||||
{
|
||||
return open;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value && backgroundFrame == null) { Init(); }
|
||||
if (!value)
|
||||
{
|
||||
fileSystemWatcher?.Dispose();
|
||||
fileSystemWatcher = null;
|
||||
}
|
||||
open = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static GUIFrame backgroundFrame;
|
||||
private static GUIFrame window;
|
||||
private static GUIListBox sidebar;
|
||||
private static GUIListBox fileList;
|
||||
private static GUITextBox directoryBox;
|
||||
private static GUITextBox filterBox;
|
||||
private static GUITextBox fileBox;
|
||||
private static GUIDropDown fileTypeDropdown;
|
||||
private static GUIButton openButton;
|
||||
|
||||
private static FileSystemWatcher fileSystemWatcher;
|
||||
|
||||
private static string currentFileTypePattern;
|
||||
|
||||
private static readonly string[] ignoredDrivePrefixes = new string[]
|
||||
{
|
||||
"/sys/", "/snap/"
|
||||
};
|
||||
|
||||
private static string currentDirectory;
|
||||
public static string CurrentDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
return currentDirectory;
|
||||
}
|
||||
set
|
||||
{
|
||||
string[] dirSplit = value.Replace('\\', '/').Split('/');
|
||||
List<string> dirs = new List<string>();
|
||||
for (int i = 0; i < dirSplit.Length; i++)
|
||||
{
|
||||
if (dirSplit[i].Trim() == "..")
|
||||
{
|
||||
if (dirs.Count > 1)
|
||||
{
|
||||
dirs.RemoveAt(dirs.Count - 1);
|
||||
}
|
||||
}
|
||||
else if (dirSplit[i].Trim() != ".")
|
||||
{
|
||||
dirs.Add(dirSplit[i]);
|
||||
}
|
||||
}
|
||||
currentDirectory = string.Join("/", dirs);
|
||||
if (!currentDirectory.EndsWith("/"))
|
||||
{
|
||||
currentDirectory += "/";
|
||||
}
|
||||
fileSystemWatcher?.Dispose();
|
||||
fileSystemWatcher = new FileSystemWatcher(currentDirectory)
|
||||
{
|
||||
Filter = "*",
|
||||
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName
|
||||
};
|
||||
fileSystemWatcher.Created += OnFileSystemChanges;
|
||||
fileSystemWatcher.Deleted += OnFileSystemChanges;
|
||||
fileSystemWatcher.Renamed += OnFileSystemChanges;
|
||||
fileSystemWatcher.EnableRaisingEvents = true;
|
||||
RefreshFileList();
|
||||
}
|
||||
}
|
||||
|
||||
public static Action<string> OnFileSelected
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private static void OnFileSystemChanges(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
switch (e.ChangeType)
|
||||
{
|
||||
case WatcherChangeTypes.Created:
|
||||
{
|
||||
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), e.Name)
|
||||
{
|
||||
UserData = (bool?)Directory.Exists(e.FullPath)
|
||||
};
|
||||
if ((itemFrame.UserData as bool?) ?? false)
|
||||
{
|
||||
itemFrame.Text += "/";
|
||||
}
|
||||
fileList.Content.RectTransform.SortChildren(SortFiles);
|
||||
}
|
||||
break;
|
||||
case WatcherChangeTypes.Deleted:
|
||||
{
|
||||
var itemFrame = fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == e.Name || tb.Text == e.Name + "/"));
|
||||
if (itemFrame != null) { fileList.RemoveChild(itemFrame); }
|
||||
}
|
||||
break;
|
||||
case WatcherChangeTypes.Renamed:
|
||||
{
|
||||
RenamedEventArgs renameArgs = e as RenamedEventArgs;
|
||||
var itemFrame = fileList.Content.FindChild(c => (c is GUITextBlock tb) && (tb.Text == renameArgs.OldName || tb.Text == renameArgs.OldName + "/")) as GUITextBlock;
|
||||
itemFrame.UserData = (bool?)Directory.Exists(e.FullPath);
|
||||
itemFrame.Text = renameArgs.Name;
|
||||
if ((itemFrame.UserData as bool?) ?? false)
|
||||
{
|
||||
itemFrame.Text += "/";
|
||||
}
|
||||
fileList.Content.RectTransform.SortChildren(SortFiles);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static int SortFiles(RectTransform r1, RectTransform r2)
|
||||
{
|
||||
string file1 = (r1.GUIComponent as GUITextBlock)?.Text ?? "";
|
||||
string file2 = (r2.GUIComponent as GUITextBlock)?.Text ?? "";
|
||||
bool dir1 = (r1.GUIComponent.UserData as bool?) ?? false;
|
||||
bool dir2 = (r2.GUIComponent.UserData as bool?) ?? false;
|
||||
if (dir1 && !dir2)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (!dir1 && dir2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return string.Compare(file1, file2);
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
backgroundFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: null)
|
||||
{
|
||||
Color = Color.Black * 0.5f,
|
||||
HoverColor = Color.Black * 0.5f,
|
||||
SelectedColor = Color.Black * 0.5f,
|
||||
PressedColor = Color.Black * 0.5f,
|
||||
};
|
||||
|
||||
window = new GUIFrame(new RectTransform(Vector2.One * 0.8f, backgroundFrame.RectTransform, Anchor.Center));
|
||||
|
||||
var horizontalLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.9f, window.RectTransform, Anchor.Center), true);
|
||||
sidebar = new GUIListBox(new RectTransform(new Vector2(0.29f, 1.0f), horizontalLayout.RectTransform));
|
||||
|
||||
var drives = DriveInfo.GetDrives();
|
||||
foreach (var drive in drives)
|
||||
{
|
||||
if (drive.DriveType == DriveType.Ram) { continue; }
|
||||
if (ignoredDrivePrefixes.Any(p => drive.Name.StartsWith(p))) { continue; }
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), sidebar.Content.RectTransform), drive.Name.Replace('\\','/'));
|
||||
}
|
||||
|
||||
sidebar.OnSelected = (child, userdata) =>
|
||||
{
|
||||
CurrentDirectory = (child as GUITextBlock).Text;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
//spacing between sidebar and fileListLayout
|
||||
new GUIFrame(new RectTransform(new Vector2(0.01f, 1.0f), horizontalLayout.RectTransform), style: null);
|
||||
|
||||
var fileListLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.7f, 1.0f), horizontalLayout.RectTransform));
|
||||
var firstRow = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), fileListLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
new GUIButton(new RectTransform(new Vector2(0.05f, 1.0f), firstRow.RectTransform), "^")
|
||||
{
|
||||
OnClicked = MoveToParentDirectory
|
||||
};
|
||||
directoryBox = new GUITextBox(new RectTransform(new Vector2(0.7f, 1.0f), firstRow.RectTransform))
|
||||
{
|
||||
OverflowClip = true,
|
||||
OnEnterPressed = (tb, txt) =>
|
||||
{
|
||||
if (Directory.Exists(txt))
|
||||
{
|
||||
CurrentDirectory = txt;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tb.Text = CurrentDirectory;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
filterBox = new GUITextBox(new RectTransform(new Vector2(0.25f, 1.0f), firstRow.RectTransform))
|
||||
{
|
||||
OverflowClip = true
|
||||
};
|
||||
firstRow.RectTransform.MinSize = new Point(0, firstRow.RectTransform.Children.Max(c => c.MinSize.Y));
|
||||
|
||||
filterBox.OnTextChanged += (txtbox, txt) =>
|
||||
{
|
||||
RefreshFileList();
|
||||
return true;
|
||||
};
|
||||
//spacing between rows
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), fileListLayout.RectTransform), style: null);
|
||||
|
||||
fileList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.85f), fileListLayout.RectTransform))
|
||||
{
|
||||
OnSelected = (child, userdata) =>
|
||||
{
|
||||
if (userdata == null) { return false; }
|
||||
|
||||
var fileName = (child as GUITextBlock).Text;
|
||||
fileBox.Text = fileName;
|
||||
if (PlayerInput.DoubleClicked())
|
||||
{
|
||||
bool isDir = (userdata as bool?).Value;
|
||||
if (isDir)
|
||||
{
|
||||
CurrentDirectory += fileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
OnFileSelected?.Invoke(CurrentDirectory + fileName);
|
||||
Open = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//spacing between rows
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), fileListLayout.RectTransform), style: null);
|
||||
|
||||
var thirdRow = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), fileListLayout.RectTransform), true);
|
||||
fileBox = new GUITextBox(new RectTransform(new Vector2(0.7f, 1.0f), thirdRow.RectTransform))
|
||||
{
|
||||
OnEnterPressed = (tb, txt) => openButton?.OnClicked?.Invoke(openButton, null) ?? false
|
||||
};
|
||||
|
||||
fileTypeDropdown = new GUIDropDown(new RectTransform(new Vector2(0.3f, 1.0f), thirdRow.RectTransform), dropAbove: true)
|
||||
{
|
||||
OnSelected = (child, userdata) =>
|
||||
{
|
||||
currentFileTypePattern = (child as GUITextBlock).UserData as string;
|
||||
RefreshFileList();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
fileTypeDropdown.Select(4);
|
||||
|
||||
//spacing between rows
|
||||
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), fileListLayout.RectTransform), style: null);
|
||||
var fourthRow = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.04f), fileListLayout.RectTransform), true);
|
||||
|
||||
//padding for open/cancel buttons
|
||||
new GUIFrame(new RectTransform(new Vector2(0.7f, 1.0f), fourthRow.RectTransform), style: null);
|
||||
|
||||
openButton = new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), fourthRow.RectTransform), TextManager.Get("opensubbutton"))
|
||||
{
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(CurrentDirectory, fileBox.Text)))
|
||||
{
|
||||
CurrentDirectory += fileBox.Text;
|
||||
}
|
||||
if (!File.Exists(CurrentDirectory + fileBox.Text)) { return false; }
|
||||
OnFileSelected?.Invoke(CurrentDirectory + fileBox.Text);
|
||||
Open = false;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
new GUIButton(new RectTransform(new Vector2(0.15f, 1.0f), fourthRow.RectTransform), TextManager.Get("cancel"))
|
||||
{
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
Open = false;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
CurrentDirectory = Directory.GetCurrentDirectory();
|
||||
}
|
||||
|
||||
public static void ClearFileTypeFilters()
|
||||
{
|
||||
if (backgroundFrame == null) { Init(); }
|
||||
fileTypeDropdown.ClearChildren();
|
||||
}
|
||||
|
||||
public static void AddFileTypeFilter(string name, string pattern)
|
||||
{
|
||||
if (backgroundFrame == null) { Init(); }
|
||||
fileTypeDropdown.AddItem(name + " (" + pattern + ")", pattern);
|
||||
}
|
||||
|
||||
public static void SelectFileTypeFilter(string pattern)
|
||||
{
|
||||
if (backgroundFrame == null) { Init(); }
|
||||
fileTypeDropdown.SelectItem(pattern);
|
||||
}
|
||||
|
||||
public static void RefreshFileList()
|
||||
{
|
||||
fileList.Content.ClearChildren();
|
||||
fileList.BarScroll = 0.0f;
|
||||
|
||||
try
|
||||
{
|
||||
var directories = Directory.EnumerateDirectories(currentDirectory, "*" + filterBox.Text + "*");
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
string txt = directory;
|
||||
if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
|
||||
if (!txt.EndsWith("/")) { txt += "/"; }
|
||||
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
|
||||
{
|
||||
UserData = (bool?)true
|
||||
};
|
||||
var folderIcon = new GUIImage(new RectTransform(new Point((int)(itemFrame.Rect.Height * 0.8f)), itemFrame.RectTransform, Anchor.CenterLeft)
|
||||
{
|
||||
AbsoluteOffset = new Point((int)(itemFrame.Rect.Height * 0.25f), 0)
|
||||
}, style: "OpenButton", scaleToFit: true);
|
||||
itemFrame.Padding = new Vector4(folderIcon.Rect.Width * 1.5f, itemFrame.Padding.Y, itemFrame.Padding.Z, itemFrame.Padding.W);
|
||||
}
|
||||
|
||||
IEnumerable<string> files = null;
|
||||
foreach (string pattern in currentFileTypePattern.Split(','))
|
||||
{
|
||||
string patternTrimmed = pattern.Trim();
|
||||
patternTrimmed = "*" + filterBox.Text + "*" + patternTrimmed;
|
||||
if (files == null)
|
||||
{
|
||||
files = Directory.EnumerateFiles(currentDirectory, patternTrimmed);
|
||||
}
|
||||
else
|
||||
{
|
||||
files = files.Concat(Directory.EnumerateFiles(currentDirectory, patternTrimmed));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
string txt = file;
|
||||
if (txt.StartsWith(currentDirectory)) { txt = txt.Substring(currentDirectory.Length); }
|
||||
var itemFrame = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), txt)
|
||||
{
|
||||
UserData = (bool?)false
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.05f), fileList.Content.RectTransform), "Could not list items in directory: " + e.Message)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
}
|
||||
|
||||
directoryBox.Text = currentDirectory;
|
||||
fileBox.Text = "";
|
||||
fileList.Deselect();
|
||||
}
|
||||
|
||||
public static bool MoveToParentDirectory(GUIButton button, object userdata)
|
||||
{
|
||||
string dir = CurrentDirectory;
|
||||
if (dir.EndsWith("/")) { dir = dir.Substring(0, dir.Length - 1); }
|
||||
int index = dir.LastIndexOf("/");
|
||||
if (index < 0) { return false; }
|
||||
CurrentDirectory = CurrentDirectory.Substring(0, index+1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void AddToGUIUpdateList()
|
||||
{
|
||||
if (!Open) { return; }
|
||||
backgroundFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
}
|
||||
2056
Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs
Normal file
2056
Barotrauma/BarotraumaClient/ClientSource/GUI/GUI.cs
Normal file
File diff suppressed because it is too large
Load Diff
256
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIButton.cs
Normal file
256
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIButton.cs
Normal file
@@ -0,0 +1,256 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class GUIButton : GUIComponent
|
||||
{
|
||||
protected GUITextBlock textBlock;
|
||||
public GUITextBlock TextBlock { get { return textBlock; } }
|
||||
protected GUIFrame frame;
|
||||
public GUIFrame Frame { get { return frame; } }
|
||||
|
||||
public delegate bool OnClickedHandler(GUIButton button, object obj);
|
||||
public OnClickedHandler OnClicked;
|
||||
|
||||
public delegate bool OnPressedHandler();
|
||||
public OnPressedHandler OnPressed;
|
||||
|
||||
public delegate bool OnButtonDownHandler();
|
||||
public OnButtonDownHandler OnButtonDown;
|
||||
|
||||
public bool CanBeSelected = true;
|
||||
|
||||
public override bool Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value == enabled) { return; }
|
||||
enabled = frame.Enabled = textBlock.Enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override Color Color
|
||||
{
|
||||
get { return base.Color; }
|
||||
set
|
||||
{
|
||||
base.Color = value;
|
||||
frame.Color = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override Color HoverColor
|
||||
{
|
||||
get { return base.HoverColor; }
|
||||
set
|
||||
{
|
||||
base.HoverColor = value;
|
||||
frame.HoverColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override Color SelectedColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.SelectedColor;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.SelectedColor = value;
|
||||
frame.SelectedColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override Color PressedColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.PressedColor;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.PressedColor = value;
|
||||
frame.PressedColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override Color OutlineColor
|
||||
{
|
||||
get { return base.OutlineColor; }
|
||||
set
|
||||
{
|
||||
base.OutlineColor = value;
|
||||
if (frame != null) frame.OutlineColor = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Color TextColor
|
||||
{
|
||||
get { return textBlock.TextColor; }
|
||||
set { textBlock.TextColor = value; }
|
||||
}
|
||||
|
||||
public Color HoverTextColor
|
||||
{
|
||||
get { return textBlock.HoverTextColor; }
|
||||
set { textBlock.HoverTextColor = value; }
|
||||
}
|
||||
|
||||
public override float FlashTimer
|
||||
{
|
||||
get { return Frame.FlashTimer; }
|
||||
}
|
||||
|
||||
public override ScalableFont Font
|
||||
{
|
||||
get
|
||||
{
|
||||
return (textBlock == null) ? GUI.Font : textBlock.Font;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.Font = value;
|
||||
if (textBlock != null) textBlock.Font = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return textBlock.Text; }
|
||||
set { textBlock.Text = value; }
|
||||
}
|
||||
|
||||
public bool ForceUpperCase
|
||||
{
|
||||
get { return textBlock.ForceUpperCase; }
|
||||
set { textBlock.ForceUpperCase = value; }
|
||||
}
|
||||
|
||||
public override string ToolTip
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.ToolTip;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.ToolTip = value;
|
||||
textBlock.ToolTip = value;
|
||||
}
|
||||
}
|
||||
|
||||
public GUIButton(RectTransform rectT, string text = "", Alignment textAlignment = Alignment.Center, string style = "", Color? color = null) : base(style, rectT)
|
||||
{
|
||||
CanBeFocused = true;
|
||||
HoverCursor = CursorState.Hand;
|
||||
|
||||
frame = new GUIFrame(new RectTransform(Vector2.One, rectT), style) { CanBeFocused = false };
|
||||
if (style != null) { GUI.Style.Apply(frame, style == "" ? "GUIButton" : style); }
|
||||
if (color.HasValue)
|
||||
{
|
||||
this.color = frame.Color = color.Value;
|
||||
}
|
||||
textBlock = new GUITextBlock(new RectTransform(Vector2.One, rectT, Anchor.Center), text, textAlignment: textAlignment, style: null)
|
||||
{
|
||||
TextColor = this.style == null ? Color.Black : this.style.TextColor,
|
||||
HoverTextColor = this.style == null ? Color.Black : this.style.HoverTextColor,
|
||||
SelectedTextColor = this.style == null ? Color.Black : this.style.SelectedTextColor,
|
||||
CanBeFocused = false
|
||||
};
|
||||
if (rectT.Rect.Height == 0 && !string.IsNullOrEmpty(text))
|
||||
{
|
||||
RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(textBlock.Text).Y));
|
||||
RectTransform.MinSize = textBlock.RectTransform.MinSize = new Point(0, System.Math.Max(rectT.MinSize.Y, Rect.Height));
|
||||
TextBlock.SetTextPos();
|
||||
}
|
||||
GUI.Style.Apply(textBlock, "", this);
|
||||
|
||||
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
|
||||
//use the default CJK font as a fallback
|
||||
if (TextManager.IsCJK(textBlock.Text) && !textBlock.Font.IsCJK)
|
||||
{
|
||||
textBlock.Font = GUI.CJKFont;
|
||||
}
|
||||
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
public override void ApplyStyle(GUIComponentStyle style)
|
||||
{
|
||||
base.ApplyStyle(style);
|
||||
|
||||
if (frame != null) { frame.ApplyStyle(style); }
|
||||
}
|
||||
|
||||
public override void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectInflate = null)
|
||||
{
|
||||
Frame.Flash(color, flashDuration, useRectangleFlash, useCircularFlash, flashRectInflate);
|
||||
}
|
||||
|
||||
protected override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
if (!Visible) return;
|
||||
base.Update(deltaTime);
|
||||
if (Rect.Contains(PlayerInput.MousePosition) && CanBeSelected && CanBeFocused && Enabled && GUI.IsMouseOn(this))
|
||||
{
|
||||
State = Selected ?
|
||||
ComponentState.HoverSelected :
|
||||
ComponentState.Hover;
|
||||
if (PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
OnButtonDown?.Invoke();
|
||||
}
|
||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
if (OnPressed != null)
|
||||
{
|
||||
if (OnPressed())
|
||||
{
|
||||
State = ComponentState.Pressed;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
State = ComponentState.Pressed;
|
||||
}
|
||||
}
|
||||
else if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
GUI.PlayUISound(GUISoundType.Click);
|
||||
if (OnClicked != null)
|
||||
{
|
||||
if (OnClicked(this, UserData))
|
||||
{
|
||||
State = ComponentState.Selected;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Selected = !Selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
State = Selected ? ComponentState.Selected : ComponentState.None;
|
||||
}
|
||||
|
||||
foreach (GUIComponent child in Children)
|
||||
{
|
||||
child.State = State;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1131
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs
Normal file
1131
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIComponent.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,8 +13,9 @@ namespace Barotrauma
|
||||
public OnSelectedHandler OnSelected;
|
||||
public OnSelectedHandler OnDropped;
|
||||
|
||||
private GUIButton button;
|
||||
private GUIListBox listBox;
|
||||
private readonly GUIButton button;
|
||||
private readonly GUIImage icon;
|
||||
private readonly GUIListBox listBox;
|
||||
|
||||
private RectTransform currentHighestParent;
|
||||
private List<RectTransform> parentHierarchy = new List<RectTransform>();
|
||||
@@ -41,7 +42,11 @@ namespace Barotrauma
|
||||
public bool ButtonEnabled
|
||||
{
|
||||
get { return button.Enabled; }
|
||||
set { button.Enabled = value; }
|
||||
set
|
||||
{
|
||||
button.Enabled = value;
|
||||
if (icon != null) { icon.Enabled = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public GUIComponent SelectedComponent
|
||||
@@ -49,6 +54,7 @@ namespace Barotrauma
|
||||
get { return listBox.SelectedComponent; }
|
||||
}
|
||||
|
||||
// TODO: fix implicit hiding
|
||||
public bool Selected
|
||||
{
|
||||
get
|
||||
@@ -83,6 +89,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Color ButtonTextColor
|
||||
{
|
||||
get { return button.TextColor; }
|
||||
set { button.TextColor = value; }
|
||||
}
|
||||
|
||||
public void ReceiveTextInput(char inputChar)
|
||||
{
|
||||
GUI.KeyboardDispatcher.Subscriber = null;
|
||||
@@ -139,8 +151,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public GUIDropDown(RectTransform rectT, string text = "", int elementCount = 4, string style = "", bool selectMultiple = false) : base(style, rectT)
|
||||
public GUIDropDown(RectTransform rectT, string text = "", int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false) : base(style, rectT)
|
||||
{
|
||||
HoverCursor = CursorState.Hand;
|
||||
CanBeFocused = true;
|
||||
|
||||
this.selectMultiple = selectMultiple;
|
||||
@@ -150,14 +163,25 @@ namespace Barotrauma
|
||||
OnClicked = OnClicked
|
||||
};
|
||||
GUI.Style.Apply(button, "", this);
|
||||
|
||||
listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, Anchor.BottomCenter, Pivot.TopCenter)
|
||||
button.TextBlock.SetTextPos();
|
||||
|
||||
Anchor listAnchor = dropAbove ? Anchor.TopCenter : Anchor.BottomCenter;
|
||||
Pivot listPivot = dropAbove ? Pivot.BottomCenter : Pivot.TopCenter;
|
||||
listBox = new GUIListBox(new RectTransform(new Point(Rect.Width, Rect.Height * MathHelper.Clamp(elementCount, 2, 10)), rectT, listAnchor, listPivot)
|
||||
|
||||
{ IsFixedSize = false }, style: null)
|
||||
{
|
||||
Enabled = !selectMultiple,
|
||||
OnSelected = SelectItem
|
||||
};
|
||||
GUI.Style.Apply(listBox.Content, "GUIListBox", this);
|
||||
GUI.Style.Apply(listBox, "GUIListBox", this);
|
||||
GUI.Style.Apply(listBox.ContentBackground, "GUIListBox", this);
|
||||
|
||||
if (button.Style.ChildStyles.ContainsKey("dropdownicon"))
|
||||
{
|
||||
icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), button.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5, 0) }, null, scaleToFit: true);
|
||||
icon.ApplyStyle(button.Style.ChildStyles["dropdownicon"]);
|
||||
}
|
||||
|
||||
currentHighestParent = FindHighestParent();
|
||||
currentHighestParent.GUIComponent.OnAddedToGUIUpdateList += AddListBoxToGUIUpdateList;
|
||||
@@ -243,6 +267,7 @@ namespace Barotrauma
|
||||
i++;
|
||||
}
|
||||
button.Text = string.Join(", ", texts);
|
||||
// TODO: The callback is called at least twice, remove this?
|
||||
OnSelected?.Invoke(tb.Parent, tb.Parent.UserData);
|
||||
return true;
|
||||
}
|
||||
@@ -290,6 +315,7 @@ namespace Barotrauma
|
||||
button.Text = textBlock.Text;
|
||||
}
|
||||
Dropped = false;
|
||||
// TODO: OnSelected can be called multiple times and when it shouldn't be called -> turn into an event so that nobody else can call it.
|
||||
OnSelected?.Invoke(component, component.UserData);
|
||||
return true;
|
||||
}
|
||||
@@ -390,7 +416,7 @@ namespace Barotrauma
|
||||
if (!Visible) return;
|
||||
wasOpened = false;
|
||||
base.Update(deltaTime);
|
||||
if (Dropped && PlayerInput.LeftButtonClicked())
|
||||
if (Dropped && PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
Rectangle listBoxRect = listBox.Rect;
|
||||
if (!listBoxRect.Contains(PlayerInput.MousePosition) && !button.Rect.Contains(PlayerInput.MousePosition))
|
||||
@@ -8,6 +8,7 @@ namespace Barotrauma
|
||||
{
|
||||
public GUIFrame(RectTransform rectT, string style = "", Color? color = null) : base(style, rectT)
|
||||
{
|
||||
Enabled = true;
|
||||
if (color.HasValue)
|
||||
{
|
||||
this.color = color.Value;
|
||||
@@ -18,7 +19,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (!Visible) return;
|
||||
|
||||
Color currColor = GetCurrentColor(state);
|
||||
Color currColor = GetColor(State);
|
||||
|
||||
if (sprites == null || !sprites.Any(s => s.Value.Any())) GUI.DrawRectangle(spriteBatch, Rect, currColor * (currColor.A/255.0f), true);
|
||||
base.Draw(spriteBatch);
|
||||
@@ -87,7 +87,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (style == null)
|
||||
{
|
||||
color = hoverColor = selectedColor = pressedColor = Color.White;
|
||||
color = hoverColor = selectedColor = pressedColor = disabledColor = Color.White;
|
||||
}
|
||||
if (!scaleToFit)
|
||||
{
|
||||
@@ -97,15 +97,17 @@ namespace Barotrauma
|
||||
{
|
||||
rectT.SizeChanged += RecalculateScale;
|
||||
}
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
protected override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!Visible) return;
|
||||
|
||||
if (Parent != null) { state = Parent.State; }
|
||||
if (OverrideState != null) { state = OverrideState.Value; }
|
||||
Color currColor = GetCurrentColor(state);
|
||||
if (Parent != null) { State = Parent.State; }
|
||||
if (OverrideState != null) { State = OverrideState.Value; }
|
||||
|
||||
Color currentColor = GetColor(State);
|
||||
|
||||
if (BlendState != null)
|
||||
{
|
||||
@@ -115,23 +117,23 @@ namespace Barotrauma
|
||||
|
||||
if (style != null)
|
||||
{
|
||||
foreach (UISprite uiSprite in style.Sprites[state])
|
||||
foreach (UISprite uiSprite in style.Sprites[State])
|
||||
{
|
||||
if (Math.Abs(Rotation) > float.Epsilon)
|
||||
{
|
||||
float scale = Math.Min(Rect.Width / uiSprite.Sprite.size.X, Rect.Height / uiSprite.Sprite.size.Y);
|
||||
spriteBatch.Draw(uiSprite.Sprite.Texture, Rect.Center.ToVector2(), uiSprite.Sprite.SourceRect, currColor * (currColor.A / 255.0f), Rotation, uiSprite.Sprite.size / 2,
|
||||
spriteBatch.Draw(uiSprite.Sprite.Texture, Rect.Center.ToVector2(), uiSprite.Sprite.SourceRect, currentColor * (currentColor.A / 255.0f), Rotation, uiSprite.Sprite.size / 2,
|
||||
Scale * scale, SpriteEffects, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
uiSprite.Draw(spriteBatch, Rect, currColor * (currColor.A / 255.0f), SpriteEffects);
|
||||
uiSprite.Draw(spriteBatch, Rect, currentColor * (currentColor.A / 255.0f), SpriteEffects);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (sprite?.Texture != null)
|
||||
{
|
||||
spriteBatch.Draw(sprite.Texture, Rect.Center.ToVector2(), sourceRect, currColor * (currColor.A / 255.0f), Rotation, sprite.size / 2,
|
||||
spriteBatch.Draw(sprite.Texture, Rect.Center.ToVector2(), sourceRect, currentColor * (currentColor.A / 255.0f), Rotation, sprite.size / 2,
|
||||
Scale, SpriteEffects, 0.0f);
|
||||
}
|
||||
|
||||
197
Barotrauma/BarotraumaClient/ClientSource/GUI/GUILayoutGroup.cs
Normal file
197
Barotrauma/BarotraumaClient/ClientSource/GUI/GUILayoutGroup.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class GUILayoutGroup : GUIComponent
|
||||
{
|
||||
private bool isHorizontal;
|
||||
public bool IsHorizontal
|
||||
{
|
||||
get { return isHorizontal; }
|
||||
set
|
||||
{
|
||||
isHorizontal = value;
|
||||
needsToRecalculate = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool stretch;
|
||||
/// <summary>
|
||||
/// Note that stretching cannot be undone, because the previous child sizes are not stored.
|
||||
/// </summary>
|
||||
public bool Stretch
|
||||
{
|
||||
get { return stretch; }
|
||||
set
|
||||
{
|
||||
stretch = value;
|
||||
needsToRecalculate = true;
|
||||
}
|
||||
}
|
||||
|
||||
private int absoluteSpacing;
|
||||
public int AbsoluteSpacing
|
||||
{
|
||||
get { return absoluteSpacing; }
|
||||
set
|
||||
{
|
||||
absoluteSpacing = MathHelper.Clamp(value, 0, int.MaxValue);
|
||||
needsToRecalculate = true;
|
||||
}
|
||||
}
|
||||
|
||||
private float relativeSpacing;
|
||||
public float RelativeSpacing
|
||||
{
|
||||
get { return relativeSpacing; }
|
||||
set
|
||||
{
|
||||
relativeSpacing = MathHelper.Clamp(value, -1, 1);
|
||||
needsToRecalculate = true;
|
||||
}
|
||||
}
|
||||
|
||||
private Anchor childAnchor;
|
||||
public Anchor ChildAnchor
|
||||
{
|
||||
get { return childAnchor; }
|
||||
set
|
||||
{
|
||||
childAnchor = value;
|
||||
needsToRecalculate = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool needsToRecalculate;
|
||||
public bool NeedsToRecalculate
|
||||
{
|
||||
get { return needsToRecalculate; }
|
||||
set
|
||||
{
|
||||
if (value) { needsToRecalculate = true; }
|
||||
}
|
||||
}
|
||||
|
||||
public GUILayoutGroup(RectTransform rectT, bool isHorizontal = false, Anchor childAnchor = Anchor.TopLeft) : base(null, rectT)
|
||||
{
|
||||
CanBeFocused = false;
|
||||
|
||||
this.isHorizontal = isHorizontal;
|
||||
this.childAnchor = childAnchor;
|
||||
rectT.ChildrenChanged += (child) => needsToRecalculate = true;
|
||||
rectT.ScaleChanged += () => needsToRecalculate = true;
|
||||
rectT.SizeChanged += () => needsToRecalculate = true;
|
||||
}
|
||||
|
||||
public void Recalculate()
|
||||
{
|
||||
float stretchFactor = 1.0f;
|
||||
if (stretch && RectTransform.Children.Count() > 0)
|
||||
{
|
||||
foreach (RectTransform child in RectTransform.Children)
|
||||
{
|
||||
if (child.GUIComponent.IgnoreLayoutGroups) { continue; }
|
||||
if (child.ScaleBasis == ScaleBasis.BothHeight) { child.MinSize = new Point(child.Rect.Height, child.MinSize.Y); }
|
||||
if (child.ScaleBasis == ScaleBasis.BothWidth) { child.MinSize = new Point(child.MinSize.X, child.Rect.Width); }
|
||||
if (child.ScaleBasis == ScaleBasis.Smallest)
|
||||
{
|
||||
if (Rect.Width < Rect.Height)
|
||||
{
|
||||
child.MinSize = new Point(child.MinSize.X, child.Rect.Width);
|
||||
}
|
||||
else
|
||||
{
|
||||
child.MinSize = new Point(child.Rect.Height, child.MinSize.Y);
|
||||
}
|
||||
}
|
||||
if (child.ScaleBasis == ScaleBasis.Largest)
|
||||
{
|
||||
if (Rect.Width > Rect.Height)
|
||||
{
|
||||
child.MinSize = new Point(child.MinSize.X, child.Rect.Width);
|
||||
}
|
||||
else
|
||||
{
|
||||
child.MinSize = new Point(child.Rect.Height, child.MinSize.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float minSize = RectTransform.Children
|
||||
.Where(c => !c.GUIComponent.IgnoreLayoutGroups)
|
||||
.Sum(c => isHorizontal ? (c.IsFixedSize ? c.NonScaledSize.X : c.MinSize.X) : (c.IsFixedSize ? c.NonScaledSize.Y : c.MinSize.Y));
|
||||
|
||||
float totalSize = RectTransform.Children
|
||||
.Where(c => !c.GUIComponent.IgnoreLayoutGroups)
|
||||
.Sum(c => isHorizontal ?
|
||||
(c.IsFixedSize ? c.Rect.Width : MathHelper.Clamp(c.Rect.Width, c.MinSize.X, c.MaxSize.X)) :
|
||||
(c.IsFixedSize ? c.Rect.Height : MathHelper.Clamp(c.Rect.Height, c.MinSize.Y, c.MaxSize.Y)));
|
||||
|
||||
float thisSize = (isHorizontal ? Rect.Width : Rect.Height);
|
||||
|
||||
totalSize +=
|
||||
(RectTransform.Children.Count(c => !c.GUIComponent.IgnoreLayoutGroups) - 1) *
|
||||
(absoluteSpacing + relativeSpacing * thisSize);
|
||||
|
||||
stretchFactor = totalSize <= 0.0f || minSize >= thisSize ?
|
||||
1.0f :
|
||||
(thisSize - minSize) / (totalSize - minSize);
|
||||
}
|
||||
|
||||
int absPos = 0;
|
||||
float relPos = 0;
|
||||
foreach (var child in RectTransform.Children)
|
||||
{
|
||||
if (child.GUIComponent.IgnoreLayoutGroups) { continue; }
|
||||
child.SetPosition(childAnchor);
|
||||
if (isHorizontal)
|
||||
{
|
||||
child.RelativeOffset = new Vector2(relPos, child.RelativeOffset.Y);
|
||||
child.AbsoluteOffset = new Point(absPos, child.AbsoluteOffset.Y);
|
||||
if (child.IsFixedSize)
|
||||
{
|
||||
absPos += child.NonScaledSize.X + absoluteSpacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
absPos += (int)(MathHelper.Clamp(child.Rect.Width * stretchFactor, child.MinSize.X, child.MaxSize.X) + (absoluteSpacing * stretchFactor));
|
||||
if (stretch)
|
||||
{
|
||||
child.RelativeSize = new Vector2(child.RelativeSize.X * stretchFactor, child.RelativeSize.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child.RelativeOffset = new Vector2(child.RelativeOffset.X, relPos);
|
||||
child.AbsoluteOffset = new Point(child.AbsoluteOffset.X, absPos);
|
||||
if (child.IsFixedSize)
|
||||
{
|
||||
absPos += child.NonScaledSize.Y + absoluteSpacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
absPos += (int)(MathHelper.Clamp(child.Rect.Height * stretchFactor, child.MinSize.Y, child.MaxSize.Y) + (absoluteSpacing * stretchFactor));
|
||||
if (stretch)
|
||||
{
|
||||
child.RelativeSize = new Vector2(child.RelativeSize.X, child.RelativeSize.Y * stretchFactor);
|
||||
}
|
||||
}
|
||||
}
|
||||
relPos += relativeSpacing * stretchFactor;
|
||||
}
|
||||
needsToRecalculate = false;
|
||||
}
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
if (needsToRecalculate)
|
||||
{
|
||||
Recalculate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
818
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs
Normal file
818
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs
Normal file
@@ -0,0 +1,818 @@
|
||||
using EventInput;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class GUIListBox : GUIComponent, IKeyboardSubscriber
|
||||
{
|
||||
protected List<GUIComponent> selected;
|
||||
|
||||
public delegate bool OnSelectedHandler(GUIComponent component, object obj);
|
||||
public OnSelectedHandler OnSelected;
|
||||
|
||||
public delegate object CheckSelectedHandler();
|
||||
public CheckSelectedHandler CheckSelected;
|
||||
|
||||
public delegate void OnRearrangedHandler(GUIListBox listBox, object obj);
|
||||
public OnRearrangedHandler OnRearranged;
|
||||
|
||||
/// <summary>
|
||||
/// A frame drawn behind the content of the listbox
|
||||
/// </summary>
|
||||
public GUIFrame ContentBackground { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A frame that contains the contents of the listbox. The frame itself is not rendered.
|
||||
/// </summary>
|
||||
public GUIFrame Content { get; private set; }
|
||||
public GUIScrollBar ScrollBar { get; private set; }
|
||||
|
||||
private Dictionary<GUIComponent, bool> childVisible = new Dictionary<GUIComponent, bool>();
|
||||
|
||||
private int totalSize;
|
||||
private bool childrenNeedsRecalculation;
|
||||
private bool scrollBarNeedsRecalculation;
|
||||
private bool dimensionsNeedsRecalculation;
|
||||
|
||||
// TODO: Define in styles?
|
||||
private int ScrollBarSize
|
||||
{
|
||||
get
|
||||
{
|
||||
//use the average of the "desired" size and the scaled size
|
||||
//scaling the bar linearly with the resolution tends to make them too large on large resolutions
|
||||
float desiredSize = 25.0f;
|
||||
float scaledSize = desiredSize * GUI.Scale;
|
||||
return (int)((desiredSize + scaledSize) / 2.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public bool SelectMultiple;
|
||||
|
||||
public bool HideChildrenOutsideFrame = true;
|
||||
|
||||
private bool useGridLayout;
|
||||
|
||||
public bool UseGridLayout
|
||||
{
|
||||
get { return useGridLayout; }
|
||||
set
|
||||
{
|
||||
if (useGridLayout == value) return;
|
||||
useGridLayout = value;
|
||||
childrenNeedsRecalculation = true;
|
||||
scrollBarNeedsRecalculation = true;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector4? overridePadding;
|
||||
public Vector4 Padding
|
||||
{
|
||||
get
|
||||
{
|
||||
if (overridePadding.HasValue) { return overridePadding.Value; }
|
||||
if (Style == null) { return Vector4.Zero; }
|
||||
return Style.Padding;
|
||||
}
|
||||
set { overridePadding = value; }
|
||||
}
|
||||
|
||||
public GUIComponent SelectedComponent
|
||||
{
|
||||
get
|
||||
{
|
||||
return selected.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix implicit hiding
|
||||
public bool Selected { get; set; }
|
||||
|
||||
public List<GUIComponent> AllSelected
|
||||
{
|
||||
get { return selected; }
|
||||
}
|
||||
|
||||
public object SelectedData
|
||||
{
|
||||
get
|
||||
{
|
||||
return SelectedComponent?.UserData;
|
||||
}
|
||||
}
|
||||
|
||||
public int SelectedIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SelectedComponent == null) return -1;
|
||||
return Content.RectTransform.GetChildIndex(SelectedComponent.RectTransform);
|
||||
}
|
||||
}
|
||||
|
||||
public float BarScroll
|
||||
{
|
||||
get { return ScrollBar.BarScroll; }
|
||||
set { ScrollBar.BarScroll = value; }
|
||||
}
|
||||
|
||||
public float BarSize
|
||||
{
|
||||
get { return ScrollBar.BarSize; }
|
||||
}
|
||||
|
||||
public float TotalSize
|
||||
{
|
||||
get { return totalSize; }
|
||||
}
|
||||
|
||||
public int Spacing { get; set; }
|
||||
|
||||
public override Color Color
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Color;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.Color = value;
|
||||
|
||||
Content.Color = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables the scroll bar without hiding it.
|
||||
/// </summary>
|
||||
public bool ScrollBarEnabled { get; set; } = true;
|
||||
public bool KeepSpaceForScrollBar { get; set; }
|
||||
|
||||
public bool ScrollBarVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return ScrollBar.Visible;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (ScrollBar.Visible == value) { return; }
|
||||
ScrollBar.Visible = value;
|
||||
dimensionsNeedsRecalculation = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Automatically hides the scroll bar when the content fits in.
|
||||
/// </summary>
|
||||
public bool AutoHideScrollBar { get; set; } = true;
|
||||
private bool IsScrollBarOnDefaultSide { get; set; }
|
||||
|
||||
public bool CanDragElements { get; set; } = false;
|
||||
private GUIComponent draggedElement;
|
||||
private Rectangle draggedReferenceRectangle;
|
||||
private Point draggedReferenceOffset;
|
||||
|
||||
public GUIComponent DraggedElement => draggedElement;
|
||||
|
||||
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
|
||||
public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true) : base(style, rectT)
|
||||
{
|
||||
CanBeFocused = true;
|
||||
selected = new List<GUIComponent>();
|
||||
ContentBackground = new GUIFrame(new RectTransform(Vector2.One, rectT), style)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
Content = new GUIFrame(new RectTransform(Vector2.One, ContentBackground.RectTransform), style: null)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
Content.RectTransform.ChildrenChanged += (_) =>
|
||||
{
|
||||
scrollBarNeedsRecalculation = true;
|
||||
childrenNeedsRecalculation = true;
|
||||
};
|
||||
if (style != null)
|
||||
{
|
||||
GUI.Style.Apply(ContentBackground, "", this);
|
||||
}
|
||||
if (color.HasValue)
|
||||
{
|
||||
this.color = color.Value;
|
||||
}
|
||||
IsScrollBarOnDefaultSide = isScrollBarOnDefaultSide;
|
||||
Point size;
|
||||
Anchor anchor;
|
||||
if (isHorizontal)
|
||||
{
|
||||
size = new Point((int)(Rect.Width - Padding.X - Padding.Z), (int)(ScrollBarSize * GUI.Scale));
|
||||
anchor = isScrollBarOnDefaultSide ? Anchor.BottomCenter : Anchor.TopCenter;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Should this be multiplied by the GUI.Scale as well?
|
||||
size = new Point(ScrollBarSize, (int)(Rect.Height - Padding.Y - Padding.W));
|
||||
anchor = isScrollBarOnDefaultSide ? Anchor.CenterRight : Anchor.CenterLeft;
|
||||
}
|
||||
ScrollBar = new GUIScrollBar(
|
||||
new RectTransform(size, rectT, anchor)
|
||||
{
|
||||
AbsoluteOffset = isHorizontal ?
|
||||
new Point(0, IsScrollBarOnDefaultSide ? (int)Padding.W : (int)Padding.Y) :
|
||||
new Point(IsScrollBarOnDefaultSide ? (int)Padding.Z : (int)Padding.X, 0)
|
||||
},
|
||||
isHorizontal: isHorizontal);
|
||||
UpdateScrollBarSize();
|
||||
Enabled = true;
|
||||
ScrollBar.BarScroll = 0.0f;
|
||||
RectTransform.ScaleChanged += () => dimensionsNeedsRecalculation = true;
|
||||
RectTransform.SizeChanged += () => dimensionsNeedsRecalculation = true;
|
||||
UpdateDimensions();
|
||||
}
|
||||
|
||||
private void UpdateDimensions()
|
||||
{
|
||||
dimensionsNeedsRecalculation = false;
|
||||
ContentBackground.RectTransform.Resize(Rect.Size);
|
||||
bool reduceScrollbarSize = KeepSpaceForScrollBar ? ScrollBarEnabled : ScrollBarVisible;
|
||||
Point contentSize = reduceScrollbarSize ? CalculateFrameSize(ScrollBar.IsHorizontal, ScrollBarSize) : Rect.Size;
|
||||
Content.RectTransform.Resize(new Point((int)(contentSize.X - Padding.X - Padding.Z), (int)(contentSize.Y - Padding.Y - Padding.W)));
|
||||
if (!IsScrollBarOnDefaultSide) { Content.RectTransform.SetPosition(Anchor.BottomRight); }
|
||||
Content.RectTransform.AbsoluteOffset = new Point(
|
||||
IsScrollBarOnDefaultSide ? (int)Padding.X : (int)Padding.Z,
|
||||
IsScrollBarOnDefaultSide ? (int)Padding.Y : (int)Padding.W);
|
||||
ScrollBar.RectTransform.Resize(ScrollBar.IsHorizontal ?
|
||||
new Point((int)(Rect.Width - Padding.X - Padding.Z), ScrollBarSize) :
|
||||
new Point(ScrollBarSize, (int)(Rect.Height - Padding.Y - Padding.W)));
|
||||
ScrollBar.RectTransform.AbsoluteOffset = ScrollBar.IsHorizontal ?
|
||||
new Point(0, (int)Padding.W) :
|
||||
new Point((int)Padding.Z, 0);
|
||||
UpdateScrollBarSize();
|
||||
}
|
||||
|
||||
public void Select(object userData, bool force = false, bool autoScroll = true)
|
||||
{
|
||||
var children = Content.Children;
|
||||
int i = 0;
|
||||
foreach (GUIComponent child in children)
|
||||
{
|
||||
if ((child.UserData != null && child.UserData.Equals(userData)) ||
|
||||
(child.UserData == null && userData == null))
|
||||
{
|
||||
Select(i, force, autoScroll);
|
||||
if (!SelectMultiple) return;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private Point CalculateFrameSize(bool isHorizontal, int scrollBarSize)
|
||||
=> isHorizontal ? new Point(Rect.Width, Rect.Height - scrollBarSize) : new Point(Rect.Width - scrollBarSize, Rect.Height);
|
||||
|
||||
private void RepositionChildren()
|
||||
{
|
||||
int x = 0, y = 0;
|
||||
if (ScrollBar.BarSize < 1.0f)
|
||||
{
|
||||
if (ScrollBar.IsHorizontal)
|
||||
{
|
||||
x -= (int)((totalSize - Content.Rect.Width) * ScrollBar.BarScroll);
|
||||
}
|
||||
else
|
||||
{
|
||||
y -= (int)((totalSize - Content.Rect.Height) * ScrollBar.BarScroll);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Content.CountChildren; i++)
|
||||
{
|
||||
GUIComponent child = Content.GetChild(i);
|
||||
if (!child.Visible) { continue; }
|
||||
if (RectTransform != null)
|
||||
{
|
||||
if (child != draggedElement && (child.RectTransform.AbsoluteOffset.X != x || child.RectTransform.AbsoluteOffset.Y != y))
|
||||
{
|
||||
child.RectTransform.AbsoluteOffset = new Point(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (useGridLayout)
|
||||
{
|
||||
if (ScrollBar.IsHorizontal)
|
||||
{
|
||||
if (y + child.Rect.Height + Spacing > Content.Rect.Height)
|
||||
{
|
||||
y = 0;
|
||||
x += child.Rect.Width + Spacing;
|
||||
if (child != draggedElement && (child.RectTransform.AbsoluteOffset.X != x || child.RectTransform.AbsoluteOffset.Y != y))
|
||||
{
|
||||
child.RectTransform.AbsoluteOffset = new Point(x, y);
|
||||
}
|
||||
y += child.Rect.Height + Spacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
y += child.Rect.Height + Spacing;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x + child.Rect.Width + Spacing > Content.Rect.Width)
|
||||
{
|
||||
x = 0;
|
||||
y += child.Rect.Height + Spacing;
|
||||
if (child != draggedElement && (child.RectTransform.AbsoluteOffset.X != x || child.RectTransform.AbsoluteOffset.Y != y))
|
||||
{
|
||||
child.RectTransform.AbsoluteOffset = new Point(x, y);
|
||||
}
|
||||
x += child.Rect.Width + Spacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
x += child.Rect.Width + Spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ScrollBar.IsHorizontal)
|
||||
{
|
||||
x += child.Rect.Width + Spacing;
|
||||
}
|
||||
else
|
||||
{
|
||||
y += child.Rect.Height + Spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateChildrenRect()
|
||||
{
|
||||
//dragging
|
||||
if (CanDragElements && draggedElement != null)
|
||||
{
|
||||
if (!PlayerInput.LeftButtonHeld())
|
||||
{
|
||||
OnRearranged?.Invoke(this, draggedElement.UserData);
|
||||
draggedElement = null;
|
||||
RepositionChildren();
|
||||
}
|
||||
else
|
||||
{
|
||||
draggedElement.RectTransform.AbsoluteOffset = draggedReferenceOffset + new Point(0, (int)PlayerInput.MousePosition.Y - draggedReferenceRectangle.Center.Y);
|
||||
|
||||
int index = Content.RectTransform.GetChildIndex(draggedElement.RectTransform);
|
||||
int currIndex = index;
|
||||
|
||||
while (currIndex > 0 && PlayerInput.MousePosition.Y < draggedReferenceRectangle.Top)
|
||||
{
|
||||
currIndex--;
|
||||
draggedReferenceRectangle.Y -= draggedReferenceRectangle.Height;
|
||||
draggedReferenceOffset.Y -= draggedReferenceRectangle.Height;
|
||||
}
|
||||
while (currIndex < Content.CountChildren - 1 && PlayerInput.MousePosition.Y > draggedReferenceRectangle.Bottom)
|
||||
{
|
||||
currIndex++;
|
||||
draggedReferenceRectangle.Y += draggedReferenceRectangle.Height;
|
||||
draggedReferenceOffset.Y += draggedReferenceRectangle.Height;
|
||||
}
|
||||
|
||||
if (currIndex != index)
|
||||
{
|
||||
draggedElement.RectTransform.RepositionChildInHierarchy(currIndex);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Content.CountChildren; i++)
|
||||
{
|
||||
var child = Content.RectTransform.GetChild(i)?.GUIComponent;
|
||||
if (child == null) continue;
|
||||
|
||||
// selecting
|
||||
if (Enabled && CanBeFocused && child.CanBeFocused && (GUI.IsMouseOn(child)) && child.Rect.Contains(PlayerInput.MousePosition))
|
||||
{
|
||||
child.State = ComponentState.Hover;
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
Select(i, autoScroll: false);
|
||||
}
|
||||
|
||||
if (CanDragElements && PlayerInput.LeftButtonDown() && GUI.MouseOn == child)
|
||||
{
|
||||
draggedElement = child;
|
||||
draggedReferenceRectangle = child.Rect;
|
||||
draggedReferenceOffset = child.RectTransform.AbsoluteOffset;
|
||||
}
|
||||
}
|
||||
else if (selected.Contains(child))
|
||||
{
|
||||
child.State = ComponentState.Selected;
|
||||
|
||||
if (CheckSelected != null)
|
||||
{
|
||||
if (CheckSelected() != child.UserData) selected.Remove(child);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
child.State = ComponentState.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
|
||||
{
|
||||
if (!Visible) { return; }
|
||||
|
||||
if (!ignoreChildren)
|
||||
{
|
||||
foreach (GUIComponent child in Children)
|
||||
{
|
||||
if (child == Content || child == ScrollBar || child == ContentBackground) { continue; }
|
||||
child.AddToGUIUpdateList(ignoreChildren, order);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (GUIComponent child in Content.Children)
|
||||
{
|
||||
if (!childVisible.ContainsKey(child)) { childVisible[child] = child.Visible; }
|
||||
if (childVisible[child] != child.Visible)
|
||||
{
|
||||
childVisible[child] = child.Visible;
|
||||
childrenNeedsRecalculation = true;
|
||||
scrollBarNeedsRecalculation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (childrenNeedsRecalculation)
|
||||
{
|
||||
RecalculateChildren();
|
||||
childVisible.Clear();
|
||||
}
|
||||
|
||||
UpdateOrder = order;
|
||||
GUI.AddToUpdateList(this);
|
||||
|
||||
if (ignoreChildren)
|
||||
{
|
||||
OnAddedToGUIUpdateList?.Invoke(this);
|
||||
return;
|
||||
}
|
||||
|
||||
int lastVisible = 0;
|
||||
for (int i = 0; i < Content.CountChildren; i++)
|
||||
{
|
||||
var child = Content.GetChild(i);
|
||||
if (!child.Visible) continue;
|
||||
if (!IsChildInsideFrame(child))
|
||||
{
|
||||
if (lastVisible > 0) break;
|
||||
continue;
|
||||
}
|
||||
lastVisible = i;
|
||||
child.AddToGUIUpdateList(false, order);
|
||||
}
|
||||
if (ScrollBar.Enabled)
|
||||
{
|
||||
ScrollBar.AddToGUIUpdateList(false, order);
|
||||
}
|
||||
OnAddedToGUIUpdateList?.Invoke(this);
|
||||
}
|
||||
|
||||
public void RecalculateChildren()
|
||||
{
|
||||
foreach (GUIComponent child in Content.Children)
|
||||
{
|
||||
ClampChildMouseRects(child);
|
||||
}
|
||||
RepositionChildren();
|
||||
childrenNeedsRecalculation = false;
|
||||
}
|
||||
|
||||
private void ClampChildMouseRects(GUIComponent child)
|
||||
{
|
||||
child.ClampMouseRectToParent = true;
|
||||
|
||||
//no need to go through grandchildren if the child is a GUIListBox, it handles this by itself
|
||||
if (child is GUIListBox) { return; }
|
||||
|
||||
foreach (GUIComponent grandChild in child.Children)
|
||||
{
|
||||
ClampChildMouseRects(grandChild);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
if (!Visible) { return; }
|
||||
|
||||
UpdateChildrenRect();
|
||||
RepositionChildren();
|
||||
|
||||
if (scrollBarNeedsRecalculation)
|
||||
{
|
||||
UpdateScrollBarSize();
|
||||
}
|
||||
if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && PlayerInput.ScrollWheelSpeed != 0)
|
||||
{
|
||||
ScrollBar.BarScroll -= (PlayerInput.ScrollWheelSpeed / 500.0f) * BarSize;
|
||||
}
|
||||
ScrollBar.Enabled = ScrollBarEnabled && BarSize < 1.0f;
|
||||
if (AutoHideScrollBar)
|
||||
{
|
||||
ScrollBarVisible = ScrollBar.Enabled;
|
||||
}
|
||||
if (dimensionsNeedsRecalculation)
|
||||
{
|
||||
UpdateDimensions();
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectNext(bool force = false, bool autoScroll = true)
|
||||
{
|
||||
int index = SelectedIndex + 1;
|
||||
while (index < Content.CountChildren)
|
||||
{
|
||||
if (Content.GetChild(index).Visible)
|
||||
{
|
||||
Select(index, force, autoScroll);
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public void SelectPrevious(bool force = false, bool autoScroll = true)
|
||||
{
|
||||
int index = SelectedIndex - 1;
|
||||
while (index >= 0)
|
||||
{
|
||||
if (Content.GetChild(index).Visible)
|
||||
{
|
||||
Select(index, force, autoScroll);
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
public void Select(int childIndex, bool force = false, bool autoScroll = true)
|
||||
{
|
||||
if (childIndex >= Content.CountChildren || childIndex < 0) { return; }
|
||||
|
||||
GUIComponent child = Content.GetChild(childIndex);
|
||||
|
||||
bool wasSelected = true;
|
||||
if (OnSelected != null)
|
||||
{
|
||||
// TODO: The callback is called twice, fix this!
|
||||
wasSelected = force || OnSelected(child, child.UserData);
|
||||
}
|
||||
|
||||
if (!wasSelected) { return; }
|
||||
|
||||
if (SelectMultiple)
|
||||
{
|
||||
if (selected.Contains(child))
|
||||
{
|
||||
selected.Remove(child);
|
||||
}
|
||||
else
|
||||
{
|
||||
selected.Add(child);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
selected.Clear();
|
||||
selected.Add(child);
|
||||
}
|
||||
|
||||
// Ensure that the selected element is visible. This may not be the case, if the selection is run from code. (e.g. if we have two list boxes that are synced)
|
||||
// TODO: This method only works when moving one item up/down (e.g. when using the up and down arrows)
|
||||
if (autoScroll)
|
||||
{
|
||||
if (ScrollBar.IsHorizontal)
|
||||
{
|
||||
if (child.Rect.X < MouseRect.X)
|
||||
{
|
||||
//child outside the left edge of the frame -> move left
|
||||
ScrollBar.BarScroll -= (float)(MouseRect.X - child.Rect.X) / (totalSize - Content.Rect.Width);
|
||||
}
|
||||
else if (child.Rect.Right > MouseRect.Right)
|
||||
{
|
||||
//child outside the right edge of the frame -> move right
|
||||
ScrollBar.BarScroll += (float)(child.Rect.Right - MouseRect.Right) / (totalSize - Content.Rect.Width);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (child.Rect.Y < MouseRect.Y)
|
||||
{
|
||||
//child above the top of the frame -> move up
|
||||
ScrollBar.BarScroll -= (float)(MouseRect.Y - child.Rect.Y) / (totalSize - Content.Rect.Height);
|
||||
}
|
||||
else if (child.Rect.Bottom > MouseRect.Bottom)
|
||||
{
|
||||
//child below the bottom of the frame -> move down
|
||||
ScrollBar.BarScroll += (float)(child.Rect.Bottom - MouseRect.Bottom) / (totalSize - Content.Rect.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If one of the children is the subscriber, we don't want to register, because it will unregister the child.
|
||||
if (RectTransform.GetAllChildren().None(rt => rt.GUIComponent == GUI.KeyboardDispatcher.Subscriber))
|
||||
{
|
||||
Selected = true;
|
||||
GUI.KeyboardDispatcher.Subscriber = this;
|
||||
}
|
||||
}
|
||||
|
||||
public void Deselect()
|
||||
{
|
||||
Selected = false;
|
||||
if (GUI.KeyboardDispatcher.Subscriber == this)
|
||||
{
|
||||
GUI.KeyboardDispatcher.Subscriber = null;
|
||||
}
|
||||
selected.Clear();
|
||||
}
|
||||
|
||||
public void UpdateScrollBarSize()
|
||||
{
|
||||
scrollBarNeedsRecalculation = false;
|
||||
if (Content == null) { return; }
|
||||
|
||||
totalSize = 0;
|
||||
var children = Content.Children.Where(c => c.Visible);
|
||||
if (useGridLayout)
|
||||
{
|
||||
int pos = 0;
|
||||
foreach (GUIComponent child in children)
|
||||
{
|
||||
if (ScrollBar.IsHorizontal)
|
||||
{
|
||||
if (pos + child.Rect.Height + Spacing > Content.Rect.Height)
|
||||
{
|
||||
pos = 0;
|
||||
totalSize += child.Rect.Width + Spacing;
|
||||
}
|
||||
pos += child.Rect.Height + Spacing;
|
||||
|
||||
if (child == children.Last())
|
||||
{
|
||||
totalSize += child.Rect.Width + Spacing;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos + child.Rect.Width + Spacing > Content.Rect.Width)
|
||||
{
|
||||
pos = 0;
|
||||
totalSize += child.Rect.Height + Spacing;
|
||||
}
|
||||
pos += child.Rect.Width + Spacing;
|
||||
|
||||
if (child == children.Last())
|
||||
{
|
||||
totalSize += child.Rect.Height + Spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (GUIComponent child in children)
|
||||
{
|
||||
totalSize += (ScrollBar.IsHorizontal) ? child.Rect.Width : child.Rect.Height;
|
||||
}
|
||||
totalSize += Content.CountChildren * Spacing;
|
||||
}
|
||||
|
||||
float minScrollBarSize = 20.0f;
|
||||
ScrollBar.BarSize = ScrollBar.IsHorizontal ?
|
||||
Math.Max(Math.Min(Content.Rect.Width / (float)totalSize, 1.0f), minScrollBarSize / Content.Rect.Width) :
|
||||
Math.Max(Math.Min(Content.Rect.Height / (float)totalSize, 1.0f), minScrollBarSize / Content.Rect.Height);
|
||||
}
|
||||
|
||||
public override void ClearChildren()
|
||||
{
|
||||
Content.ClearChildren();
|
||||
selected.Clear();
|
||||
}
|
||||
|
||||
public override void RemoveChild(GUIComponent child)
|
||||
{
|
||||
if (child == null) { return; }
|
||||
child.RectTransform.Parent = null;
|
||||
if (selected.Contains(child)) selected.Remove(child);
|
||||
UpdateScrollBarSize();
|
||||
}
|
||||
|
||||
public override void DrawChildren(SpriteBatch spriteBatch, bool recursive)
|
||||
{
|
||||
//do nothing (the children have to be drawn in the Draw method after the ScissorRectangle has been set)
|
||||
return;
|
||||
}
|
||||
|
||||
protected override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!Visible) { return; }
|
||||
|
||||
ContentBackground.DrawManually(spriteBatch, alsoChildren: false);
|
||||
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
RasterizerState prevRasterizerState = spriteBatch.GraphicsDevice.RasterizerState;
|
||||
if (HideChildrenOutsideFrame)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, Content.Rect);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
}
|
||||
|
||||
var children = Content.Children;
|
||||
int lastVisible = 0;
|
||||
|
||||
int i = 0;
|
||||
foreach (GUIComponent child in Content.Children)
|
||||
{
|
||||
if (!child.Visible) continue;
|
||||
if (!IsChildInsideFrame(child))
|
||||
{
|
||||
if (lastVisible > 0) break;
|
||||
continue;
|
||||
}
|
||||
lastVisible = i;
|
||||
child.DrawManually(spriteBatch, alsoChildren: true, recursive: true);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (HideChildrenOutsideFrame)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: prevRasterizerState);
|
||||
}
|
||||
|
||||
if (ScrollBarVisible)
|
||||
{
|
||||
ScrollBar.DrawManually(spriteBatch, alsoChildren: true, recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsChildInsideFrame(GUIComponent child)
|
||||
{
|
||||
if (child == null) { return false; }
|
||||
|
||||
if (ScrollBar.IsHorizontal)
|
||||
{
|
||||
if (child.Rect.Right < Content.Rect.X) { return false; }
|
||||
if (child.Rect.X > Content.Rect.Right) { return false; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (child.Rect.Bottom < Content.Rect.Y) { return false; }
|
||||
if (child.Rect.Y > Content.Rect.Bottom) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ReceiveTextInput(char inputChar)
|
||||
{
|
||||
GUI.KeyboardDispatcher.Subscriber = null;
|
||||
}
|
||||
public void ReceiveTextInput(string text) { }
|
||||
public void ReceiveCommandInput(char command) { }
|
||||
|
||||
public void ReceiveSpecialInput(Keys key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case Keys.Down:
|
||||
SelectNext();
|
||||
break;
|
||||
case Keys.Up:
|
||||
SelectPrevious();
|
||||
break;
|
||||
case Keys.Enter:
|
||||
case Keys.Space:
|
||||
case Keys.Escape:
|
||||
GUI.KeyboardDispatcher.Subscriber = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,9 @@ namespace Barotrauma
|
||||
get { return Math.Max(400, 400 * (GameMain.GraphicsWidth / 1920)); }
|
||||
}
|
||||
|
||||
private float inGameCloseTimer = 0.0f;
|
||||
private const float inGameCloseTime = 15f;
|
||||
|
||||
public enum Type
|
||||
{
|
||||
Default,
|
||||
@@ -87,7 +90,7 @@ namespace Barotrauma
|
||||
Content = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.85f), InnerFrame.RectTransform, Anchor.Center)) { AbsoluteSpacing = 5 };
|
||||
|
||||
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform),
|
||||
headerText, textAlignment: Alignment.Center, wrap: true);
|
||||
headerText, font: GUI.SubHeadingFont, textAlignment: Alignment.Center, wrap: true);
|
||||
GUI.Style.Apply(Header, "", this);
|
||||
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
|
||||
|
||||
@@ -100,21 +103,28 @@ namespace Barotrauma
|
||||
Text.RectTransform.IsFixedSize = true;
|
||||
}
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), Content.RectTransform, Anchor.BottomCenter, maxSize: new Point(1000, 50)),
|
||||
isHorizontal: true, childAnchor: buttons.Length > 1 ? Anchor.BottomLeft : Anchor.Center)
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), Content.RectTransform, Anchor.BottomCenter), childAnchor: Anchor.TopCenter)
|
||||
{
|
||||
AbsoluteSpacing = 5,
|
||||
IgnoreLayoutGroups = true
|
||||
};
|
||||
|
||||
int buttonSize = 35;
|
||||
var buttonStyle = GUI.Style.GetComponentStyle("GUIButton");
|
||||
if (buttonStyle != null && buttonStyle.Height.HasValue)
|
||||
{
|
||||
buttonSize = buttonStyle.Height.Value;
|
||||
}
|
||||
|
||||
buttonContainer.RectTransform.NonScaledSize = buttonContainer.RectTransform.MinSize = buttonContainer.RectTransform.MaxSize =
|
||||
new Point(buttonContainer.Rect.Width, (int)(30 * GUI.Scale));
|
||||
new Point(buttonContainer.Rect.Width, (int)((buttonSize + 5) * buttons.Length));
|
||||
buttonContainer.RectTransform.IsFixedSize = true;
|
||||
|
||||
if (height == 0)
|
||||
{
|
||||
height += Header.Rect.Height + Content.AbsoluteSpacing;
|
||||
height += (Text == null ? 0 : Text.Rect.Height) + Content.AbsoluteSpacing;
|
||||
height += buttonContainer.Rect.Height;
|
||||
height += buttonContainer.Rect.Height + 20;
|
||||
if (minSize.HasValue) { height = Math.Max(height, minSize.Value.Y); }
|
||||
|
||||
InnerFrame.RectTransform.NonScaledSize =
|
||||
@@ -126,7 +136,7 @@ namespace Barotrauma
|
||||
Buttons = new List<GUIButton>(buttons.Length);
|
||||
for (int i = 0; i < buttons.Length; i++)
|
||||
{
|
||||
var button = new GUIButton(new RectTransform(new Vector2(Math.Min(0.9f / buttons.Length, 0.5f), 1.0f), buttonContainer.RectTransform), buttons[i], style: "GUIButtonLarge");
|
||||
var button = new GUIButton(new RectTransform(new Vector2(0.6f, 1.0f / buttons.Length), buttonContainer.RectTransform), buttons[i]);
|
||||
Buttons.Add(button);
|
||||
}
|
||||
}
|
||||
@@ -154,7 +164,7 @@ namespace Barotrauma
|
||||
Buttons = new List<GUIButton>(1)
|
||||
{
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 0.5f), buttonContainer.RectTransform, Anchor.Center),
|
||||
style: GUI.Style.GetComponentStyle("GUIButtonSolidHorizontalArrow") != null ? "GUIButtonSolidHorizontalArrow" : "GUIButtonHorizontalArrow")
|
||||
style: "GUIButtonHorizontalArrow")
|
||||
{
|
||||
OnClicked = Close
|
||||
}
|
||||
@@ -168,10 +178,10 @@ namespace Barotrauma
|
||||
{
|
||||
Text = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), text, textAlignment: textAlignment, wrap: true);
|
||||
GUI.Style.Apply(Text, "", this);
|
||||
/*Content.Recalculate();
|
||||
Content.Recalculate();
|
||||
Text.RectTransform.NonScaledSize = Text.RectTransform.MinSize = Text.RectTransform.MaxSize =
|
||||
new Point(Text.Rect.Width, Text.Rect.Height);
|
||||
Text.RectTransform.IsFixedSize = true;*/
|
||||
Text.RectTransform.IsFixedSize = true;
|
||||
}
|
||||
|
||||
if (height == 0)
|
||||
@@ -238,6 +248,13 @@ namespace Barotrauma
|
||||
{
|
||||
InnerFrame.RectTransform.AbsoluteOffset = Vector2.SmoothStep(initialPos, defaultPos, openState).ToPoint();
|
||||
openState = Math.Min(openState + deltaTime * 2.0f, 1.0f);
|
||||
|
||||
inGameCloseTimer += deltaTime;
|
||||
|
||||
if (inGameCloseTimer >= inGameCloseTime)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
417
Barotrauma/BarotraumaClient/ClientSource/GUI/GUINumberInput.cs
Normal file
417
Barotrauma/BarotraumaClient/ClientSource/GUI/GUINumberInput.cs
Normal file
@@ -0,0 +1,417 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class GUINumberInput : GUIComponent
|
||||
{
|
||||
public enum NumberType
|
||||
{
|
||||
Int, Float
|
||||
}
|
||||
|
||||
public delegate void OnValueChangedHandler(GUINumberInput numberInput);
|
||||
public OnValueChangedHandler OnValueChanged;
|
||||
|
||||
public GUITextBox TextBox { get; private set; }
|
||||
|
||||
private GUIButton plusButton, minusButton;
|
||||
|
||||
private NumberType inputType;
|
||||
public NumberType InputType
|
||||
{
|
||||
get { return inputType; }
|
||||
set
|
||||
{
|
||||
if (inputType == value) { return; }
|
||||
inputType = value;
|
||||
if (inputType == NumberType.Int ||
|
||||
(inputType == NumberType.Float && MinValueFloat > float.MinValue && MaxValueFloat < float.MaxValue))
|
||||
{
|
||||
ShowPlusMinusButtons();
|
||||
}
|
||||
else
|
||||
{
|
||||
HidePlusMinusButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float? minValueFloat, maxValueFloat;
|
||||
public float? MinValueFloat
|
||||
{
|
||||
get { return minValueFloat; }
|
||||
set
|
||||
{
|
||||
minValueFloat = value;
|
||||
ClampFloatValue();
|
||||
if (inputType == NumberType.Int ||
|
||||
(inputType == NumberType.Float && MinValueFloat > float.MinValue && MaxValueFloat < float.MaxValue))
|
||||
{
|
||||
ShowPlusMinusButtons();
|
||||
}
|
||||
else
|
||||
{
|
||||
HidePlusMinusButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
public float? MaxValueFloat
|
||||
{
|
||||
get { return maxValueFloat; }
|
||||
set
|
||||
{
|
||||
maxValueFloat = value;
|
||||
ClampFloatValue();
|
||||
if (inputType == NumberType.Int ||
|
||||
(inputType == NumberType.Float && MinValueFloat > float.MinValue && MaxValueFloat < float.MaxValue))
|
||||
{
|
||||
ShowPlusMinusButtons();
|
||||
}
|
||||
else
|
||||
{
|
||||
HidePlusMinusButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float floatValue;
|
||||
public float FloatValue
|
||||
{
|
||||
get { return floatValue; }
|
||||
set
|
||||
{
|
||||
if (MathUtils.NearlyEqual(value, floatValue)) return;
|
||||
floatValue = value;
|
||||
ClampFloatValue();
|
||||
float newValue = floatValue;
|
||||
UpdateText();
|
||||
//UpdateText may remove decimals from the value, force to full accuracy
|
||||
floatValue = newValue;
|
||||
OnValueChanged?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
private int decimalsToDisplay = 1;
|
||||
public int DecimalsToDisplay
|
||||
{
|
||||
get { return decimalsToDisplay; }
|
||||
set
|
||||
{
|
||||
decimalsToDisplay = value;
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
private int? minValueInt, maxValueInt;
|
||||
public int? MinValueInt
|
||||
{
|
||||
get { return minValueInt; }
|
||||
set
|
||||
{
|
||||
minValueInt = value;
|
||||
ClampIntValue();
|
||||
}
|
||||
}
|
||||
public int? MaxValueInt
|
||||
{
|
||||
get { return maxValueInt; }
|
||||
set
|
||||
{
|
||||
maxValueInt = value;
|
||||
ClampIntValue();
|
||||
}
|
||||
}
|
||||
|
||||
private int intValue;
|
||||
public int IntValue
|
||||
{
|
||||
get { return intValue; }
|
||||
set
|
||||
{
|
||||
if (value == intValue) return;
|
||||
intValue = value;
|
||||
UpdateText();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Enabled
|
||||
{
|
||||
get => base.Enabled;
|
||||
set
|
||||
{
|
||||
plusButton.Enabled = true;
|
||||
minusButton.Enabled = true;
|
||||
if (InputType == NumberType.Int) { ClampIntValue(); } else { ClampFloatValue(); }
|
||||
TextBox.Enabled = value;
|
||||
if (!value)
|
||||
{
|
||||
plusButton.Enabled = false;
|
||||
minusButton.Enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override ScalableFont Font
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Font;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.Font = value;
|
||||
if (TextBox != null) { TextBox.Font = value; }
|
||||
}
|
||||
}
|
||||
|
||||
public GUILayoutGroup LayoutGroup
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public float valueStep;
|
||||
|
||||
private float pressedTimer;
|
||||
private float pressedDelay = 0.5f;
|
||||
private bool IsPressedTimerRunning { get { return pressedTimer > 0; } }
|
||||
|
||||
public GUINumberInput(RectTransform rectT, NumberType inputType, string style = "", Alignment textAlignment = Alignment.Center, float? relativeButtonAreaWidth = null) : base(style, rectT)
|
||||
{
|
||||
LayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, rectT), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
|
||||
float _relativeButtonAreaWidth = relativeButtonAreaWidth ?? MathHelper.Clamp(Rect.Height / (float)Rect.Width, 0.1f, 0.25f);
|
||||
|
||||
TextBox = new GUITextBox(new RectTransform(new Vector2(1.0f - _relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform), textAlignment: textAlignment, style: "GUITextBoxNoIcon")
|
||||
{
|
||||
ClampText = false
|
||||
};
|
||||
TextBox.CaretColor = TextBox.TextColor;
|
||||
TextBox.OnTextChanged += TextChanged;
|
||||
|
||||
var buttonArea = new GUIFrame(new RectTransform(new Vector2(_relativeButtonAreaWidth, 1.0f), LayoutGroup.RectTransform, Anchor.CenterRight), style: null);
|
||||
plusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform), style: null);
|
||||
GUI.Style.Apply(plusButton, "PlusButton", this);
|
||||
plusButton.OnButtonDown += () =>
|
||||
{
|
||||
pressedTimer = pressedDelay;
|
||||
return true;
|
||||
};
|
||||
plusButton.OnClicked += (button, data) =>
|
||||
{
|
||||
IncreaseValue();
|
||||
return true;
|
||||
};
|
||||
plusButton.OnPressed += () =>
|
||||
{
|
||||
if (!IsPressedTimerRunning)
|
||||
{
|
||||
IncreaseValue();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
minusButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.5f), buttonArea.RectTransform, Anchor.BottomRight), style: null);
|
||||
GUI.Style.Apply(minusButton, "MinusButton", this);
|
||||
minusButton.OnButtonDown += () =>
|
||||
{
|
||||
pressedTimer = pressedDelay;
|
||||
return true;
|
||||
};
|
||||
minusButton.OnClicked += (button, data) =>
|
||||
{
|
||||
ReduceValue();
|
||||
return true;
|
||||
};
|
||||
minusButton.OnPressed += () =>
|
||||
{
|
||||
if (!IsPressedTimerRunning)
|
||||
{
|
||||
ReduceValue();
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (inputType != NumberType.Int)
|
||||
{
|
||||
HidePlusMinusButtons();
|
||||
}
|
||||
|
||||
if (inputType == NumberType.Int)
|
||||
{
|
||||
UpdateText();
|
||||
TextBox.OnEnterPressed += (txtBox, txt) =>
|
||||
{
|
||||
UpdateText();
|
||||
TextBox.Deselect();
|
||||
return true;
|
||||
};
|
||||
TextBox.OnDeselected += (txtBox, key) => UpdateText();
|
||||
}
|
||||
else if (inputType == NumberType.Float)
|
||||
{
|
||||
UpdateText();
|
||||
TextBox.OnDeselected += (txtBox, key) => UpdateText();
|
||||
TextBox.OnEnterPressed += (txtBox, txt) =>
|
||||
{
|
||||
UpdateText();
|
||||
TextBox.Deselect();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
InputType = inputType;
|
||||
switch (InputType)
|
||||
{
|
||||
case NumberType.Int:
|
||||
TextBox.textFilterFunction = text => new string(text.Where(c => char.IsNumber(c) || c == '-').ToArray());
|
||||
break;
|
||||
case NumberType.Float:
|
||||
TextBox.textFilterFunction = text => new string(text.Where(c => char.IsDigit(c) || c == '.' || c == '-').ToArray());
|
||||
break;
|
||||
}
|
||||
|
||||
RectTransform.MinSize = TextBox.RectTransform.MinSize;
|
||||
LayoutGroup.Recalculate();
|
||||
}
|
||||
|
||||
private void HidePlusMinusButtons()
|
||||
{
|
||||
plusButton.Parent.Visible = false;
|
||||
plusButton.Parent.IgnoreLayoutGroups = true;
|
||||
TextBox.RectTransform.RelativeSize = Vector2.One;
|
||||
LayoutGroup.Recalculate();
|
||||
}
|
||||
|
||||
private void ShowPlusMinusButtons()
|
||||
{
|
||||
plusButton.Parent.Visible = true;
|
||||
plusButton.Parent.IgnoreLayoutGroups = false;
|
||||
TextBox.RectTransform.RelativeSize = new Vector2(1.0f - plusButton.Parent.RectTransform.RelativeSize.X, 1.0f);
|
||||
LayoutGroup.Recalculate();
|
||||
}
|
||||
|
||||
private void ReduceValue()
|
||||
{
|
||||
if (inputType == NumberType.Int)
|
||||
{
|
||||
IntValue -= valueStep > 0 ? (int)valueStep : 1;
|
||||
}
|
||||
else if (maxValueFloat.HasValue && minValueFloat.HasValue)
|
||||
{
|
||||
FloatValue -= valueStep > 0 ? valueStep : Round();
|
||||
}
|
||||
}
|
||||
|
||||
private void IncreaseValue()
|
||||
{
|
||||
if (inputType == NumberType.Int)
|
||||
{
|
||||
IntValue += valueStep > 0 ? (int)valueStep : 1;
|
||||
}
|
||||
else if (inputType == NumberType.Float)
|
||||
{
|
||||
FloatValue += valueStep > 0 ? valueStep : Round();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates one percent between the range as the increment/decrement.
|
||||
/// This value is rounded so that the bigger it is, the less decimals are used (min 0, max 3).
|
||||
/// Return value is clamped between 0.1f and 1000.
|
||||
/// </summary>
|
||||
private float Round()
|
||||
{
|
||||
if (!maxValueFloat.HasValue || !minValueFloat.HasValue) return 0;
|
||||
float onePercent = MathHelper.Lerp(minValueFloat.Value, maxValueFloat.Value, 0.01f);
|
||||
float diff = maxValueFloat.Value - minValueFloat.Value;
|
||||
int decimals = (int)MathHelper.Lerp(3, 0, MathUtils.InverseLerp(10, 1000, diff));
|
||||
return MathHelper.Clamp((float)Math.Round(onePercent, decimals), 0.1f, 1000);
|
||||
}
|
||||
|
||||
private bool TextChanged(GUITextBox textBox, string text)
|
||||
{
|
||||
switch (InputType)
|
||||
{
|
||||
case NumberType.Int:
|
||||
int newIntValue = IntValue;
|
||||
if (string.IsNullOrWhiteSpace(text) || text == "-")
|
||||
{
|
||||
intValue = 0;
|
||||
}
|
||||
else if (int.TryParse(text, out newIntValue))
|
||||
{
|
||||
intValue = newIntValue;
|
||||
}
|
||||
ClampIntValue();
|
||||
break;
|
||||
case NumberType.Float:
|
||||
float newFloatValue = FloatValue;
|
||||
if (string.IsNullOrWhiteSpace(text) || text == "-")
|
||||
{
|
||||
floatValue = 0;
|
||||
}
|
||||
else if (float.TryParse(text, NumberStyles.Any, CultureInfo.InvariantCulture, out newFloatValue))
|
||||
{
|
||||
floatValue = newFloatValue;
|
||||
}
|
||||
ClampFloatValue();
|
||||
break;
|
||||
}
|
||||
OnValueChanged?.Invoke(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ClampFloatValue()
|
||||
{
|
||||
if (MinValueFloat != null)
|
||||
{
|
||||
floatValue = Math.Max(floatValue, MinValueFloat.Value);
|
||||
minusButton.Enabled = floatValue > MinValueFloat;
|
||||
}
|
||||
if (MaxValueFloat != null)
|
||||
{
|
||||
floatValue = Math.Min(floatValue, MaxValueFloat.Value);
|
||||
plusButton.Enabled = floatValue < MaxValueFloat;
|
||||
}
|
||||
}
|
||||
|
||||
private void ClampIntValue()
|
||||
{
|
||||
if (MinValueInt != null)
|
||||
{
|
||||
intValue = Math.Max(intValue, MinValueInt.Value);
|
||||
minusButton.Enabled = intValue > MinValueInt;
|
||||
}
|
||||
if (MaxValueInt != null)
|
||||
{
|
||||
intValue = Math.Min(intValue, MaxValueInt.Value);
|
||||
plusButton.Enabled = intValue < MaxValueInt;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateText()
|
||||
{
|
||||
switch (InputType)
|
||||
{
|
||||
case NumberType.Float:
|
||||
TextBox.Text = FloatValue.Format(decimalsToDisplay);
|
||||
break;
|
||||
case NumberType.Int:
|
||||
TextBox.Text = IntValue.ToString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
if (IsPressedTimerRunning)
|
||||
{
|
||||
pressedTimer -= deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
168
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIProgressBar.cs
Normal file
168
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIProgressBar.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class GUIProgressBar : GUIComponent
|
||||
{
|
||||
private bool isHorizontal;
|
||||
private readonly GUIFrame frame, slider;
|
||||
private float barSize;
|
||||
private readonly bool showFrame;
|
||||
|
||||
public delegate float ProgressGetterHandler();
|
||||
public ProgressGetterHandler ProgressGetter;
|
||||
|
||||
public bool IsHorizontal
|
||||
{
|
||||
get { return isHorizontal; }
|
||||
set { isHorizontal = value; }
|
||||
}
|
||||
|
||||
public float BarSize
|
||||
{
|
||||
get { return barSize; }
|
||||
set
|
||||
{
|
||||
if (!MathUtils.IsValid(value))
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"GUIProgressBar.BarSize_setter",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"Attempted to set the BarSize of a GUIProgressBar to an invalid value (" + value + ")\n" + Environment.StackTrace);
|
||||
return;
|
||||
}
|
||||
barSize = MathHelper.Clamp(value, 0.0f, 1.0f);
|
||||
//UpdateRect();
|
||||
}
|
||||
}
|
||||
|
||||
public GUIProgressBar(RectTransform rectT, float barSize, Color? color = null, string style = "", bool showFrame = true) : base(style, rectT)
|
||||
{
|
||||
if (color.HasValue)
|
||||
{
|
||||
this.color = color.Value;
|
||||
}
|
||||
isHorizontal = (Rect.Width > Rect.Height);
|
||||
frame = new GUIFrame(new RectTransform(Vector2.One, rectT));
|
||||
GUI.Style.Apply(frame, "", this);
|
||||
slider = new GUIFrame(new RectTransform(Vector2.One, rectT));
|
||||
GUI.Style.Apply(slider, "Slider", this);
|
||||
this.showFrame = showFrame;
|
||||
this.barSize = barSize;
|
||||
Enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the area the slider should be drawn inside
|
||||
/// </summary>
|
||||
/// <param name="fillAmount">0 = empty, 1 = full</param>
|
||||
public Rectangle GetSliderRect(float fillAmount)
|
||||
{
|
||||
Rectangle sliderArea = new Rectangle(
|
||||
frame.Rect.X + (int)style.Padding.X,
|
||||
frame.Rect.Y + (int)style.Padding.Y,
|
||||
(int)(frame.Rect.Width - style.Padding.X - style.Padding.Z),
|
||||
(int)(frame.Rect.Height - style.Padding.Y - style.Padding.W));
|
||||
|
||||
Vector4 sliceBorderSizes = Vector4.Zero;
|
||||
if (slider.sprites.ContainsKey(slider.State) && (slider.sprites[slider.State].First()?.Slice ?? false))
|
||||
{
|
||||
var slices = slider.sprites[slider.State].First().Slices;
|
||||
sliceBorderSizes = new Vector4(slices[0].Width, slices[0].Height, slices[8].Width, slices[8].Height);
|
||||
sliceBorderSizes *= slider.sprites[slider.State].First().GetSliceBorderScale(sliderArea.Size);
|
||||
}
|
||||
|
||||
Rectangle sliderRect = IsHorizontal ?
|
||||
new Rectangle(
|
||||
sliderArea.X + (int)sliceBorderSizes.X,
|
||||
sliderArea.Y,
|
||||
(int)((sliderArea.Width - sliceBorderSizes.X - sliceBorderSizes.Z) * fillAmount),
|
||||
sliderArea.Height)
|
||||
:
|
||||
new Rectangle(
|
||||
sliderArea.X,
|
||||
(int)(sliderArea.Bottom - (sliderArea.Height - sliceBorderSizes.Y - sliceBorderSizes.W) * fillAmount - sliceBorderSizes.W),
|
||||
sliderArea.Width,
|
||||
(int)((sliderArea.Height - sliceBorderSizes.Y - sliceBorderSizes.W) * fillAmount));
|
||||
|
||||
sliderRect.Width = Math.Max(sliderRect.Width, 1);
|
||||
sliderRect.Height = Math.Max(sliderRect.Height, 1);
|
||||
|
||||
return sliderRect;
|
||||
}
|
||||
|
||||
protected override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!Visible) { return; }
|
||||
|
||||
if (ProgressGetter != null)
|
||||
{
|
||||
float newSize = MathHelper.Clamp(ProgressGetter(), 0.0f, 1.0f);
|
||||
if (!MathUtils.IsValid(newSize))
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"GUIProgressBar.Draw:GetProgress",
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"ProgressGetter of a GUIProgressBar (" + ProgressGetter.Target.ToString() + " - " + ProgressGetter.Method.ToString() + ") returned an invalid value (" + newSize + ")\n" + Environment.StackTrace);
|
||||
}
|
||||
else
|
||||
{
|
||||
BarSize = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
var sliderRect = GetSliderRect(barSize);
|
||||
|
||||
slider.RectTransform.AbsoluteOffset = new Point((int)style.Padding.X, (int)style.Padding.Y);
|
||||
slider.RectTransform.MaxSize = new Point(
|
||||
(int)(Rect.Width - style.Padding.X + style.Padding.Z),
|
||||
(int)(Rect.Height - style.Padding.Y + style.Padding.W));
|
||||
frame.Visible = showFrame;
|
||||
slider.Visible = true;
|
||||
|
||||
if (showFrame)
|
||||
{
|
||||
if (AutoDraw)
|
||||
{
|
||||
frame.DrawAuto(spriteBatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
frame.DrawManually(spriteBatch);
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
if (BarSize <= 1.0f)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = Rectangle.Intersect(prevScissorRect, sliderRect);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
}
|
||||
|
||||
Color currColor = GetColor(State);
|
||||
|
||||
slider.Color = currColor;
|
||||
if (AutoDraw)
|
||||
{
|
||||
slider.DrawAuto(spriteBatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
slider.DrawManually(spriteBatch);
|
||||
}
|
||||
//hide the slider, we've already drawn it manually
|
||||
frame.Visible = false;
|
||||
slider.Visible = false;
|
||||
if (BarSize <= 1.0f)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,8 +54,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private int? selected;
|
||||
public int? Selected
|
||||
// intentional hiding?
|
||||
private new int? selected;
|
||||
public new int? Selected
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -64,7 +65,7 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
OnSelect?.Invoke(this, value);
|
||||
if (selected != null && selected.Equals(value)) return;
|
||||
if (selected != null && selected.Equals(value)) { return; }
|
||||
selected = value;
|
||||
foreach (KeyValuePair<int, GUITickBox> radioButton in radioButtons)
|
||||
{
|
||||
@@ -72,7 +73,10 @@ namespace Barotrauma
|
||||
{
|
||||
radioButton.Value.Selected = true;
|
||||
}
|
||||
else if (radioButton.Value.Selected) radioButton.Value.Selected = false;
|
||||
else if (radioButton.Value.Selected)
|
||||
{
|
||||
radioButton.Value.Selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,10 @@ namespace Barotrauma
|
||||
{
|
||||
public class GUIScrollBar : GUIComponent
|
||||
{
|
||||
public static GUIScrollBar draggingBar;
|
||||
public static GUIScrollBar DraggingBar
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
private bool isHorizontal;
|
||||
|
||||
@@ -19,11 +22,11 @@ namespace Barotrauma
|
||||
private float step;
|
||||
|
||||
private Vector2? dragStartPos;
|
||||
|
||||
|
||||
public delegate bool OnMovedHandler(GUIScrollBar scrollBar, float barScroll);
|
||||
public OnMovedHandler OnMoved;
|
||||
public OnMovedHandler OnReleased;
|
||||
|
||||
|
||||
public bool IsBooleanSwitch;
|
||||
|
||||
public override string ToolTip
|
||||
@@ -77,7 +80,11 @@ namespace Barotrauma
|
||||
{
|
||||
enabled = value;
|
||||
Bar.Enabled = value;
|
||||
if (!enabled) Bar.Selected = false;
|
||||
Children.ForEach(c => c.Enabled = value);
|
||||
if (!enabled)
|
||||
{
|
||||
Bar.Selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,12 +120,12 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ScrollToValue==null) return (BarScroll * (Range.Y - Range.X)) + Range.X;
|
||||
if (ScrollToValue == null) return (BarScroll * (Range.Y - Range.X)) + Range.X;
|
||||
return ScrollToValue(this, BarScroll);
|
||||
}
|
||||
set
|
||||
{
|
||||
if (ValueToScroll==null) BarScroll = (value - Range.X) / (Range.Y - Range.X);
|
||||
if (ValueToScroll == null) BarScroll = (value - Range.X) / (Range.Y - Range.X);
|
||||
else BarScroll = ValueToScroll(this, value);
|
||||
}
|
||||
}
|
||||
@@ -179,22 +186,39 @@ namespace Barotrauma
|
||||
public float BarSize
|
||||
{
|
||||
get { return barSize; }
|
||||
set
|
||||
set
|
||||
{
|
||||
barSize = Math.Min(Math.Max(value, 0.0f), 1.0f);
|
||||
UpdateRect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public GUIScrollBar(RectTransform rectT, float barSize = 1, Color? color = null, string style = "", bool? isHorizontal = null) : base(style, rectT)
|
||||
{
|
||||
CanBeFocused = true;
|
||||
|
||||
this.isHorizontal = isHorizontal ?? (Rect.Width > Rect.Height);
|
||||
Frame = new GUIFrame(new RectTransform(Vector2.One, rectT));
|
||||
GUI.Style.Apply(Frame, IsHorizontal ? "GUIFrameHorizontal" : "GUIFrameVertical", this);
|
||||
this.barSize = barSize;
|
||||
Bar = new GUIButton(new RectTransform(Vector2.One, rectT, IsHorizontal ? Anchor.CenterLeft : Anchor.TopCenter), color: color);
|
||||
|
||||
Bar = new GUIButton(new RectTransform(Vector2.One, rectT, IsHorizontal ? Anchor.CenterLeft : Anchor.TopCenter), color: color, style: null);
|
||||
|
||||
switch (style)
|
||||
{
|
||||
case "":
|
||||
HoverCursor = CursorState.Default;
|
||||
Bar.HoverCursor = CursorState.Default;
|
||||
break;
|
||||
case "GUISlider":
|
||||
HoverCursor = CursorState.Default;
|
||||
Bar.HoverCursor = CursorState.Hand;
|
||||
break;
|
||||
default:
|
||||
HoverCursor = CursorState.Hand;
|
||||
Bar.HoverCursor = CursorState.Hand;
|
||||
break;
|
||||
}
|
||||
|
||||
GUI.Style.Apply(Bar, IsHorizontal ? "GUIButtonHorizontal" : "GUIButtonVertical", this);
|
||||
Bar.OnPressed = SelectBar;
|
||||
enabled = true;
|
||||
@@ -219,12 +243,16 @@ namespace Barotrauma
|
||||
{
|
||||
if (!Visible) { return; }
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (!enabled) { return; }
|
||||
|
||||
Frame.State = GUI.MouseOn == Frame ? ComponentState.Hover : ComponentState.None;
|
||||
if (Frame.State == ComponentState.Hover && PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
Frame.State = ComponentState.Pressed;
|
||||
}
|
||||
|
||||
if (IsBooleanSwitch &&
|
||||
(!PlayerInput.LeftButtonHeld() || (GUI.MouseOn != this && !IsParentOf(GUI.MouseOn))))
|
||||
(!PlayerInput.PrimaryMouseButtonHeld() || (GUI.MouseOn != this && !IsParentOf(GUI.MouseOn))))
|
||||
{
|
||||
int dir = Math.Sign(barScroll - (minValue + maxValue) / 2.0f);
|
||||
if (dir == 0) dir = 1;
|
||||
@@ -235,11 +263,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (draggingBar == this)
|
||||
if (DraggingBar == this)
|
||||
{
|
||||
GUI.ForceMouseOn(this);
|
||||
if (dragStartPos == null) { dragStartPos = PlayerInput.MousePosition; }
|
||||
|
||||
if (!PlayerInput.LeftButtonHeld())
|
||||
if (!PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
if (IsBooleanSwitch && GUI.MouseOn == Bar && Vector2.Distance(dragStartPos.Value, PlayerInput.MousePosition) < 5)
|
||||
{
|
||||
@@ -247,7 +276,7 @@ namespace Barotrauma
|
||||
OnMoved?.Invoke(this, BarScroll);
|
||||
}
|
||||
OnReleased?.Invoke(this, BarScroll);
|
||||
draggingBar = null;
|
||||
DraggingBar = null;
|
||||
dragStartPos = null;
|
||||
|
||||
}
|
||||
@@ -259,9 +288,9 @@ namespace Barotrauma
|
||||
}
|
||||
else if (GUI.MouseOn == Frame)
|
||||
{
|
||||
if (PlayerInput.LeftButtonClicked())
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
draggingBar?.OnReleased?.Invoke(draggingBar, draggingBar.BarScroll);
|
||||
DraggingBar?.OnReleased?.Invoke(DraggingBar, DraggingBar.BarScroll);
|
||||
if (IsBooleanSwitch)
|
||||
{
|
||||
MoveButton(new Vector2(
|
||||
@@ -280,10 +309,10 @@ namespace Barotrauma
|
||||
|
||||
private bool SelectBar()
|
||||
{
|
||||
if (!enabled || !PlayerInput.LeftButtonDown()) { return false; }
|
||||
if (!enabled || !PlayerInput.PrimaryMouseButtonDown()) { return false; }
|
||||
if (barSize >= 1.0f) { return false; }
|
||||
|
||||
draggingBar = this;
|
||||
DraggingBar = this;
|
||||
|
||||
return true;
|
||||
}
|
||||
431
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs
Normal file
431
Barotrauma/BarotraumaClient/ClientSource/GUI/GUIStyle.cs
Normal file
@@ -0,0 +1,431 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class GUIStyle
|
||||
{
|
||||
private Dictionary<string, GUIComponentStyle> componentStyles;
|
||||
|
||||
private XElement configElement;
|
||||
|
||||
private GraphicsDevice graphicsDevice;
|
||||
|
||||
private ScalableFont defaultFont;
|
||||
|
||||
public ScalableFont Font { get; private set; }
|
||||
public ScalableFont GlobalFont { get; private set; }
|
||||
public ScalableFont UnscaledSmallFont { get; private set; }
|
||||
public ScalableFont SmallFont { get; private set; }
|
||||
public ScalableFont LargeFont { get; private set; }
|
||||
public ScalableFont SubHeadingFont { get; private set; }
|
||||
public ScalableFont DigitalFont { get; private set; }
|
||||
public ScalableFont HotkeyFont { get; private set; }
|
||||
|
||||
public Dictionary<ScalableFont, bool> ForceFontUpperCase
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new Dictionary<ScalableFont, bool>();
|
||||
|
||||
public readonly Sprite[] CursorSprite = new Sprite[7];
|
||||
|
||||
public UISprite UIGlow { get; private set; }
|
||||
public UISprite UIGlowCircular { get; private set; }
|
||||
|
||||
public SpriteSheet FocusIndicator { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// General green color used for elements whose colors are set from code
|
||||
/// </summary>
|
||||
public Color Green { get; private set; } = Color.LightGreen;
|
||||
|
||||
/// <summary>
|
||||
/// General red color used for elements whose colors are set from code
|
||||
/// </summary>
|
||||
public Color Orange { get; private set; } = Color.Orange;
|
||||
|
||||
/// <summary>
|
||||
/// General red color used for elements whose colors are set from code
|
||||
/// </summary>
|
||||
public Color Red { get; private set; } = Color.Red;
|
||||
|
||||
/// <summary>
|
||||
/// General blue color used for elements whose colors are set from code
|
||||
/// </summary>
|
||||
public Color Blue { get; private set; } = Color.Blue;
|
||||
|
||||
public Color ColorInventoryEmpty { get; private set; } = Color.Red;
|
||||
public Color ColorInventoryHalf { get; private set; } = Color.Orange;
|
||||
public Color ColorInventoryFull { get; private set; } = Color.LightGreen;
|
||||
public Color ColorInventoryBackground { get; private set; } = Color.Gray;
|
||||
|
||||
public Color TextColor { get; private set; } = Color.White * 0.8f;
|
||||
public Color TextColorBright { get; private set; } = Color.White * 0.9f;
|
||||
public Color TextColorDark { get; private set; } = Color.Black * 0.9f;
|
||||
public Color TextColorDim { get; private set; } = Color.White * 0.6f;
|
||||
|
||||
// Inventory
|
||||
public Color EquipmentSlotIconColor { get; private set; } = new Color(99, 70, 64);
|
||||
|
||||
// Health HUD
|
||||
public Color BuffColorLow { get; private set; } = Color.LightGreen;
|
||||
public Color BuffColorMedium { get; private set; } = Color.Green;
|
||||
public Color BuffColorHigh { get; private set; } = Color.DarkGreen;
|
||||
|
||||
public Color DebuffColorLow { get; private set; } = Color.DarkSalmon;
|
||||
public Color DebuffColorMedium { get; private set; } = Color.Red;
|
||||
public Color DebuffColorHigh { get; private set; } = Color.DarkRed;
|
||||
|
||||
public Color HealthBarColorLow { get; private set; } = Color.Red;
|
||||
public Color HealthBarColorMedium { get; private set; } = Color.Orange;
|
||||
public Color HealthBarColorHigh { get; private set; } = new Color(78, 114, 88);
|
||||
|
||||
public Color EquipmentIndicatorNotEquipped { get; private set; } = Color.Gray;
|
||||
public Color EquipmentIndicatorEquipped { get; private set; } = new Color(105, 202, 125);
|
||||
public Color EquipmentIndicatorRunningOut { get; private set; } = new Color(202, 105, 105);
|
||||
|
||||
public static Point ItemFrameMargin => new Point(50, 56).Multiply(GUI.SlicedSpriteScale);
|
||||
public static Point ItemFrameOffset => new Point(0, 3).Multiply(GUI.SlicedSpriteScale);
|
||||
|
||||
public GUIStyle(XElement element, GraphicsDevice graphicsDevice)
|
||||
{
|
||||
this.graphicsDevice = graphicsDevice;
|
||||
componentStyles = new Dictionary<string, GUIComponentStyle>();
|
||||
configElement = element;
|
||||
foreach (XElement subElement in configElement.Elements())
|
||||
{
|
||||
var name = subElement.Name.ToString().ToLowerInvariant();
|
||||
switch (name)
|
||||
{
|
||||
case "cursor":
|
||||
if (subElement.HasElements)
|
||||
{
|
||||
foreach (var children in subElement.Descendants())
|
||||
{
|
||||
var index = children.GetAttributeInt("state", (int)CursorState.Default);
|
||||
CursorSprite[index] = new Sprite(children);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CursorSprite[(int)CursorState.Default] = new Sprite(subElement);
|
||||
}
|
||||
break;
|
||||
case "green":
|
||||
Green = subElement.GetAttributeColor("color", Green);
|
||||
break;
|
||||
case "orange":
|
||||
Orange = subElement.GetAttributeColor("color", Orange);
|
||||
break;
|
||||
case "red":
|
||||
Red = subElement.GetAttributeColor("color", Red);
|
||||
break;
|
||||
case "blue":
|
||||
Blue = subElement.GetAttributeColor("color", Blue);
|
||||
break;
|
||||
case "colorinventoryempty":
|
||||
ColorInventoryEmpty = subElement.GetAttributeColor("color", ColorInventoryEmpty);
|
||||
break;
|
||||
case "colorinventoryhalf":
|
||||
ColorInventoryHalf = subElement.GetAttributeColor("color", ColorInventoryHalf);
|
||||
break;
|
||||
case "colorinventoryfull":
|
||||
ColorInventoryFull = subElement.GetAttributeColor("color", ColorInventoryFull);
|
||||
break;
|
||||
case "colorinventorybackground":
|
||||
ColorInventoryBackground = subElement.GetAttributeColor("color", ColorInventoryBackground);
|
||||
break;
|
||||
case "textcolordark":
|
||||
TextColorDark = subElement.GetAttributeColor("color", TextColorDark);
|
||||
break;
|
||||
case "textcolorbright":
|
||||
TextColorBright = subElement.GetAttributeColor("color", TextColorBright);
|
||||
break;
|
||||
case "textcolordim":
|
||||
TextColorDim = subElement.GetAttributeColor("color", TextColorDim);
|
||||
break;
|
||||
case "textcolornormal":
|
||||
case "textcolor":
|
||||
TextColor = subElement.GetAttributeColor("color", TextColor);
|
||||
break;
|
||||
case "equipmentsloticoncolor":
|
||||
EquipmentSlotIconColor = subElement.GetAttributeColor("color", EquipmentSlotIconColor);
|
||||
break;
|
||||
case "buffcolorlow":
|
||||
BuffColorLow = subElement.GetAttributeColor("color", BuffColorLow);
|
||||
break;
|
||||
case "buffcolormedium":
|
||||
BuffColorMedium = subElement.GetAttributeColor("color", BuffColorMedium);
|
||||
break;
|
||||
case "buffcolorhigh":
|
||||
BuffColorHigh = subElement.GetAttributeColor("color", BuffColorHigh);
|
||||
break;
|
||||
case "debuffcolorlow":
|
||||
DebuffColorLow = subElement.GetAttributeColor("color", DebuffColorLow);
|
||||
break;
|
||||
case "debuffcolormedium":
|
||||
DebuffColorMedium = subElement.GetAttributeColor("color", DebuffColorMedium);
|
||||
break;
|
||||
case "debuffcolorhigh":
|
||||
DebuffColorHigh = subElement.GetAttributeColor("color", DebuffColorHigh);
|
||||
break;
|
||||
case "healthbarcolorlow":
|
||||
HealthBarColorLow = subElement.GetAttributeColor("color", HealthBarColorLow);
|
||||
break;
|
||||
case "healthbarcolormedium":
|
||||
HealthBarColorMedium = subElement.GetAttributeColor("color", HealthBarColorMedium);
|
||||
break;
|
||||
case "healthbarcolorhigh":
|
||||
HealthBarColorHigh = subElement.GetAttributeColor("color", HealthBarColorHigh);
|
||||
break;
|
||||
case "equipmentindicatornotequipped":
|
||||
EquipmentIndicatorNotEquipped = subElement.GetAttributeColor("color", EquipmentIndicatorNotEquipped);
|
||||
break;
|
||||
case "equipmentindicatorequipped":
|
||||
EquipmentIndicatorEquipped = subElement.GetAttributeColor("color", EquipmentIndicatorEquipped);
|
||||
break;
|
||||
case "equipmentindicatorrunningout":
|
||||
EquipmentIndicatorRunningOut = subElement.GetAttributeColor("color", EquipmentIndicatorRunningOut);
|
||||
break;
|
||||
case "uiglow":
|
||||
UIGlow = new UISprite(subElement);
|
||||
break;
|
||||
case "uiglowcircular":
|
||||
UIGlowCircular = new UISprite(subElement);
|
||||
break;
|
||||
case "focusindicator":
|
||||
FocusIndicator = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "font":
|
||||
Font = LoadFont(subElement, graphicsDevice);
|
||||
ForceFontUpperCase[Font] = subElement.GetAttributeBool("forceuppercase", false);
|
||||
break;
|
||||
case "globalfont":
|
||||
GlobalFont = LoadFont(subElement, graphicsDevice);
|
||||
ForceFontUpperCase[GlobalFont] = subElement.GetAttributeBool("forceuppercase", false);
|
||||
break;
|
||||
case "unscaledsmallfont":
|
||||
UnscaledSmallFont = LoadFont(subElement, graphicsDevice);
|
||||
ForceFontUpperCase[UnscaledSmallFont] = subElement.GetAttributeBool("forceuppercase", false);
|
||||
break;
|
||||
case "smallfont":
|
||||
SmallFont = LoadFont(subElement, graphicsDevice);
|
||||
ForceFontUpperCase[SmallFont] = subElement.GetAttributeBool("forceuppercase", false);
|
||||
break;
|
||||
case "largefont":
|
||||
LargeFont = LoadFont(subElement, graphicsDevice);
|
||||
ForceFontUpperCase[LargeFont] = subElement.GetAttributeBool("forceuppercase", false);
|
||||
break;
|
||||
case "digitalfont":
|
||||
DigitalFont = LoadFont(subElement, graphicsDevice);
|
||||
ForceFontUpperCase[DigitalFont] = subElement.GetAttributeBool("forceuppercase", false);
|
||||
break;
|
||||
case "hotkeyfont":
|
||||
HotkeyFont = LoadFont(subElement, graphicsDevice);
|
||||
ForceFontUpperCase[HotkeyFont] = subElement.GetAttributeBool("forceuppercase", false);
|
||||
break;
|
||||
case "objectivetitle":
|
||||
case "subheading":
|
||||
SubHeadingFont = LoadFont(subElement, graphicsDevice);
|
||||
ForceFontUpperCase[SubHeadingFont] = subElement.GetAttributeBool("forceuppercase", false);
|
||||
break;
|
||||
default:
|
||||
GUIComponentStyle componentStyle = new GUIComponentStyle(subElement, this);
|
||||
componentStyles.Add(subElement.Name.ToString().ToLowerInvariant(), componentStyle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (GlobalFont == null)
|
||||
{
|
||||
GlobalFont = Font;
|
||||
DebugConsole.NewMessage("Global font not defined in the current UI style file. The global font is used to render western symbols when using Chinese/Japanese/Korean localization. Using default font instead...", Color.Orange);
|
||||
}
|
||||
|
||||
GameMain.Instance.OnResolutionChanged += () => { RescaleElements(); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the default font of the currently selected language
|
||||
/// </summary>
|
||||
public ScalableFont LoadCurrentDefaultFont()
|
||||
{
|
||||
defaultFont?.Dispose();
|
||||
defaultFont = null;
|
||||
foreach (XElement subElement in configElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "font":
|
||||
defaultFont = LoadFont(subElement, graphicsDevice);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return defaultFont;
|
||||
}
|
||||
|
||||
|
||||
private void RescaleElements()
|
||||
{
|
||||
if (configElement == null) { return; }
|
||||
if (configElement.Elements() == null) { return; }
|
||||
foreach (XElement subElement in configElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "font":
|
||||
if (Font == null) { continue; }
|
||||
Font.Size = GetFontSize(subElement);
|
||||
break;
|
||||
case "smallfont":
|
||||
if (SmallFont == null) { continue; }
|
||||
SmallFont.Size = GetFontSize(subElement);
|
||||
break;
|
||||
case "largefont":
|
||||
if (LargeFont == null) { continue; }
|
||||
LargeFont.Size = GetFontSize(subElement);
|
||||
break;
|
||||
case "hotkeyfont":
|
||||
if (HotkeyFont == null) { continue; }
|
||||
HotkeyFont.Size = GetFontSize(subElement);
|
||||
break;
|
||||
case "objectivetitle":
|
||||
case "subheading":
|
||||
if (SubHeadingFont == null) { continue; }
|
||||
SubHeadingFont.Size = GetFontSize(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var componentStyle in componentStyles.Values)
|
||||
{
|
||||
componentStyle.GetSize(componentStyle.Element);
|
||||
foreach (var childStyle in componentStyle.ChildStyles.Values)
|
||||
{
|
||||
childStyle.GetSize(childStyle.Element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ScalableFont LoadFont(XElement element, GraphicsDevice graphicsDevice)
|
||||
{
|
||||
string file = GetFontFilePath(element);
|
||||
uint size = GetFontSize(element);
|
||||
bool dynamicLoading = GetFontDynamicLoading(element);
|
||||
bool isCJK = GetIsCJK(element);
|
||||
return new ScalableFont(file, size, graphicsDevice, dynamicLoading, isCJK);
|
||||
}
|
||||
|
||||
private uint GetFontSize(XElement element, uint defaultSize = 14)
|
||||
{
|
||||
//check if any of the language override fonts want to override the font size as well
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
uint overrideFontSize = GetFontSize(subElement, 0);
|
||||
if (overrideFontSize > 0) { return overrideFontSize; }
|
||||
}
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("size", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
|
||||
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
|
||||
{
|
||||
return (uint)subElement.GetAttributeInt("size", 14);
|
||||
}
|
||||
}
|
||||
return defaultSize;
|
||||
}
|
||||
|
||||
private string GetFontFilePath(XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return subElement.GetAttributeString("file", "");
|
||||
}
|
||||
}
|
||||
return element.GetAttributeString("file", "");
|
||||
}
|
||||
|
||||
private bool GetFontDynamicLoading(XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return subElement.GetAttributeBool("dynamicloading", false);
|
||||
}
|
||||
}
|
||||
return element.GetAttributeBool("dynamicloading", false);
|
||||
}
|
||||
|
||||
private bool GetIsCJK(XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return subElement.GetAttributeBool("iscjk", false);
|
||||
}
|
||||
}
|
||||
return element.GetAttributeBool("iscjk", false);
|
||||
}
|
||||
|
||||
public GUIComponentStyle GetComponentStyle(string name)
|
||||
{
|
||||
componentStyles.TryGetValue(name.ToLowerInvariant(), out GUIComponentStyle style);
|
||||
return style;
|
||||
}
|
||||
|
||||
public void Apply(GUIComponent targetComponent, string styleName = "", GUIComponent parent = null)
|
||||
{
|
||||
GUIComponentStyle componentStyle = null;
|
||||
if (parent != null)
|
||||
{
|
||||
GUIComponentStyle parentStyle = parent.Style;
|
||||
|
||||
if (parent.Style == null)
|
||||
{
|
||||
string parentStyleName = parent.GetType().Name.ToLowerInvariant();
|
||||
|
||||
if (!componentStyles.TryGetValue(parentStyleName, out parentStyle))
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't find a GUI style \""+ parentStyleName + "\"");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string childStyleName = string.IsNullOrEmpty(styleName) ? targetComponent.GetType().Name : styleName;
|
||||
parentStyle.ChildStyles.TryGetValue(childStyleName.ToLowerInvariant(), out componentStyle);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(styleName))
|
||||
{
|
||||
styleName = targetComponent.GetType().Name;
|
||||
}
|
||||
if (!componentStyles.TryGetValue(styleName.ToLowerInvariant(), out componentStyle))
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't find a GUI style \""+ styleName+"\"");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
targetComponent.ApplyStyle(componentStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
535
Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBlock.cs
Normal file
535
Barotrauma/BarotraumaClient/ClientSource/GUI/GUITextBlock.cs
Normal file
@@ -0,0 +1,535 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class GUITextBlock : GUIComponent
|
||||
{
|
||||
protected string text;
|
||||
|
||||
protected Alignment textAlignment;
|
||||
|
||||
private float textScale = 1;
|
||||
|
||||
protected Vector2 textPos;
|
||||
protected Vector2 origin;
|
||||
|
||||
protected Color textColor, disabledTextColor, selectedTextColor;
|
||||
|
||||
private string wrappedText;
|
||||
private string censoredText;
|
||||
|
||||
public delegate string TextGetterHandler();
|
||||
public TextGetterHandler TextGetter;
|
||||
|
||||
public bool Wrap;
|
||||
private bool playerInput;
|
||||
|
||||
public bool RoundToNearestPixel = true;
|
||||
|
||||
private bool overflowClipActive;
|
||||
public bool OverflowClip;
|
||||
|
||||
public bool OverflowClipActive
|
||||
{
|
||||
get { return overflowClipActive; }
|
||||
}
|
||||
|
||||
private float textDepth;
|
||||
|
||||
private ScalableFont originalFont;
|
||||
|
||||
public Vector2 TextOffset { get; set; }
|
||||
|
||||
private Vector4 padding;
|
||||
public Vector4 Padding
|
||||
{
|
||||
get { return padding; }
|
||||
set
|
||||
{
|
||||
padding = value;
|
||||
SetTextPos();
|
||||
}
|
||||
}
|
||||
|
||||
public override ScalableFont Font
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Font;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (base.Font == value) { return; }
|
||||
base.Font = originalFont = value;
|
||||
if (text != null && GUI.Style.ForceFontUpperCase.ContainsKey(Font) && GUI.Style.ForceFontUpperCase[Font])
|
||||
{
|
||||
Text = text.ToUpper();
|
||||
}
|
||||
SetTextPos();
|
||||
}
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return text; }
|
||||
set
|
||||
{
|
||||
string newText = forceUpperCase || (GUI.Style.ForceFontUpperCase.ContainsKey(Font) && GUI.Style.ForceFontUpperCase[Font]) || (style != null && style.ForceUpperCase) ?
|
||||
value?.ToUpper() :
|
||||
value;
|
||||
|
||||
if (Text == newText) { return; }
|
||||
|
||||
//reset scale, it gets recalculated in SetTextPos
|
||||
if (autoScaleHorizontal || autoScaleVertical) { textScale = 1.0f; }
|
||||
|
||||
text = newText;
|
||||
wrappedText = newText;
|
||||
if (TextManager.IsCJK(text))
|
||||
{
|
||||
//switch to fallback CJK font
|
||||
if (!Font.IsCJK) { base.Font = GUI.CJKFont; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Font == GUI.CJKFont) { base.Font = originalFont; }
|
||||
}
|
||||
SetTextPos();
|
||||
}
|
||||
}
|
||||
|
||||
public string WrappedText
|
||||
{
|
||||
get { return wrappedText; }
|
||||
}
|
||||
|
||||
public float TextDepth
|
||||
{
|
||||
get { return textDepth; }
|
||||
set { textDepth = MathHelper.Clamp(value, 0.0f, 1.0f); }
|
||||
}
|
||||
|
||||
public Vector2 TextPos
|
||||
{
|
||||
get { return textPos; }
|
||||
set { textPos = value; }
|
||||
}
|
||||
|
||||
public float TextScale
|
||||
{
|
||||
get { return textScale; }
|
||||
set
|
||||
{
|
||||
if (value != textScale)
|
||||
{
|
||||
textScale = value;
|
||||
SetTextPos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool autoScaleHorizontal, autoScaleVertical;
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, the text is automatically scaled down to fit the textblock horizontally.
|
||||
/// </summary>
|
||||
public bool AutoScaleHorizontal
|
||||
{
|
||||
get { return autoScaleHorizontal; }
|
||||
set
|
||||
{
|
||||
if (autoScaleHorizontal == value) { return; }
|
||||
autoScaleHorizontal = value;
|
||||
if (autoScaleHorizontal)
|
||||
{
|
||||
SetTextPos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, the text is automatically scaled down to fit the textblock vertically.
|
||||
/// </summary>
|
||||
public bool AutoScaleVertical
|
||||
{
|
||||
get { return autoScaleVertical; }
|
||||
set
|
||||
{
|
||||
if (autoScaleVertical == value) { return; }
|
||||
autoScaleVertical = value;
|
||||
if (autoScaleVertical)
|
||||
{
|
||||
SetTextPos();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool forceUpperCase;
|
||||
public bool ForceUpperCase
|
||||
{
|
||||
get { return forceUpperCase; }
|
||||
set
|
||||
{
|
||||
if (forceUpperCase == value) { return; }
|
||||
|
||||
forceUpperCase = value;
|
||||
if (forceUpperCase ||
|
||||
(style != null && style.ForceUpperCase) ||
|
||||
(GUI.Style.ForceFontUpperCase.ContainsKey(Font) && GUI.Style.ForceFontUpperCase[Font]))
|
||||
{
|
||||
Text = text?.ToUpper();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 Origin
|
||||
{
|
||||
get { return origin; }
|
||||
}
|
||||
|
||||
public Vector2 TextSize
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Color TextColor
|
||||
{
|
||||
get { return textColor; }
|
||||
set { textColor = value; }
|
||||
}
|
||||
|
||||
private Color? hoverTextColor;
|
||||
public Color HoverTextColor
|
||||
{
|
||||
get { return hoverTextColor ?? textColor; }
|
||||
set { hoverTextColor = value; }
|
||||
}
|
||||
|
||||
public Color SelectedTextColor
|
||||
{
|
||||
get { return selectedTextColor; }
|
||||
set { selectedTextColor = value; }
|
||||
}
|
||||
|
||||
public Alignment TextAlignment
|
||||
{
|
||||
get { return textAlignment; }
|
||||
set
|
||||
{
|
||||
if (textAlignment == value) return;
|
||||
textAlignment = value;
|
||||
SetTextPos();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Censor
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string CensoredText
|
||||
{
|
||||
get { return censoredText; }
|
||||
}
|
||||
|
||||
private List<ColorData> colorData = null;
|
||||
private bool hasColorHighlight = false;
|
||||
|
||||
/// <summary>
|
||||
/// This is the new constructor.
|
||||
/// If the rectT height is set 0, the height is calculated from the text.
|
||||
/// </summary>
|
||||
public GUITextBlock(RectTransform rectT, string text, Color? textColor = null, ScalableFont font = null,
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false)
|
||||
: base(style, rectT)
|
||||
{
|
||||
if (color.HasValue)
|
||||
{
|
||||
this.color = color.Value;
|
||||
}
|
||||
if (textColor.HasValue)
|
||||
{
|
||||
OverrideTextColor(textColor.Value);
|
||||
}
|
||||
|
||||
//if the text is in chinese/korean/japanese and we're not using a CJK-compatible font,
|
||||
//use the default CJK font as a fallback
|
||||
var selectedFont = originalFont = font ?? GUI.Font;
|
||||
if (TextManager.IsCJK(text) && !selectedFont.IsCJK)
|
||||
{
|
||||
selectedFont = GUI.CJKFont;
|
||||
}
|
||||
this.Font = selectedFont;
|
||||
this.textAlignment = textAlignment;
|
||||
this.Wrap = wrap;
|
||||
this.Text = text ?? "";
|
||||
this.playerInput = playerInput;
|
||||
if (rectT.Rect.Height == 0 && !string.IsNullOrEmpty(text))
|
||||
{
|
||||
CalculateHeightFromText();
|
||||
}
|
||||
SetTextPos();
|
||||
|
||||
RectTransform.ScaleChanged += SetTextPos;
|
||||
RectTransform.SizeChanged += SetTextPos;
|
||||
|
||||
Enabled = true;
|
||||
Censor = false;
|
||||
}
|
||||
public GUITextBlock(RectTransform rectT, List<ColorData> colorData, string text, Color? textColor = null, ScalableFont font = null, Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool playerInput = false)
|
||||
: this(rectT, text, textColor, font, textAlignment, wrap, style, color, playerInput)
|
||||
{
|
||||
this.colorData = colorData;
|
||||
hasColorHighlight = colorData != null;
|
||||
}
|
||||
|
||||
public void CalculateHeightFromText(int padding = 0)
|
||||
{
|
||||
if (wrappedText == null) { return; }
|
||||
RectTransform.Resize(new Point(RectTransform.Rect.Width, (int)Font.MeasureString(wrappedText).Y + padding));
|
||||
}
|
||||
|
||||
public override void ApplyStyle(GUIComponentStyle componentStyle)
|
||||
{
|
||||
if (componentStyle == null) { return; }
|
||||
base.ApplyStyle(componentStyle);
|
||||
padding = componentStyle.Padding;
|
||||
|
||||
textColor = componentStyle.TextColor;
|
||||
hoverTextColor = componentStyle.HoverTextColor;
|
||||
disabledTextColor = componentStyle.DisabledTextColor;
|
||||
selectedTextColor = componentStyle.SelectedTextColor;
|
||||
|
||||
switch (componentStyle.Font)
|
||||
{
|
||||
case "font":
|
||||
Font = componentStyle.Style.Font;
|
||||
break;
|
||||
case "smallfont":
|
||||
Font = componentStyle.Style.SmallFont;
|
||||
break;
|
||||
case "largefont":
|
||||
Font = componentStyle.Style.LargeFont;
|
||||
break;
|
||||
case "objectivetitle":
|
||||
case "subheading":
|
||||
Font = componentStyle.Style.SubHeadingFont;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTextPos()
|
||||
{
|
||||
if (text == null) { return; }
|
||||
|
||||
censoredText = string.IsNullOrEmpty(text) ? "" : new string('\u2022', text.Length);
|
||||
|
||||
var rect = Rect;
|
||||
|
||||
overflowClipActive = false;
|
||||
wrappedText = text;
|
||||
|
||||
TextSize = MeasureText(text);
|
||||
|
||||
if (Wrap && rect.Width > 0)
|
||||
{
|
||||
wrappedText = ToolBox.WrapText(text, rect.Width - padding.X - padding.Z, Font, textScale, playerInput);
|
||||
TextSize = MeasureText(wrappedText);
|
||||
}
|
||||
else if (OverflowClip)
|
||||
{
|
||||
overflowClipActive = TextSize.X > rect.Width - padding.X - padding.Z;
|
||||
}
|
||||
|
||||
Vector2 minSize = new Vector2(
|
||||
Math.Max(rect.Width - padding.X - padding.Z, 5.0f),
|
||||
Math.Max(rect.Height - padding.Y - padding.W, 5.0f));
|
||||
if (!autoScaleHorizontal) { minSize.X = float.MaxValue; }
|
||||
if (!Wrap && !autoScaleVertical) { minSize.Y = float.MaxValue; }
|
||||
|
||||
if ((autoScaleHorizontal || autoScaleVertical) && textScale > 0.1f &&
|
||||
(TextSize.X * textScale > minSize.X || TextSize.Y * textScale > minSize.Y))
|
||||
{
|
||||
TextScale = Math.Max(0.1f, Math.Min(minSize.X / TextSize.X, minSize.Y / TextSize.Y)) - 0.01f;
|
||||
return;
|
||||
}
|
||||
|
||||
textPos = new Vector2(padding.X + (rect.Width - padding.Z - padding.X) / 2.0f, padding.Y + (rect.Height - padding.Y - padding.W) / 2.0f);
|
||||
origin = TextSize * 0.5f;
|
||||
|
||||
if (textAlignment.HasFlag(Alignment.Left) && !overflowClipActive)
|
||||
{
|
||||
textPos.X = padding.X;
|
||||
origin.X = 0;
|
||||
}
|
||||
if (textAlignment.HasFlag(Alignment.Right) || overflowClipActive)
|
||||
{
|
||||
textPos.X = rect.Width - padding.Z;
|
||||
origin.X = TextSize.X;
|
||||
}
|
||||
if (textAlignment.HasFlag(Alignment.Top))
|
||||
{
|
||||
textPos.Y = padding.Y;
|
||||
origin.Y = 0;
|
||||
}
|
||||
if (textAlignment.HasFlag(Alignment.Bottom))
|
||||
{
|
||||
textPos.Y = rect.Height - padding.W;
|
||||
origin.Y = TextSize.Y;
|
||||
}
|
||||
|
||||
origin.X = (int)(origin.X);
|
||||
origin.Y = (int)(origin.Y);
|
||||
|
||||
textPos.X = (int)textPos.X;
|
||||
textPos.Y = (int)textPos.Y;
|
||||
}
|
||||
|
||||
private Vector2 MeasureText(string text)
|
||||
{
|
||||
if (Font == null) return Vector2.Zero;
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return Font.MeasureString(" ");
|
||||
}
|
||||
|
||||
Vector2 size = Vector2.Zero;
|
||||
while (size == Vector2.Zero)
|
||||
{
|
||||
try { size = Font.MeasureString(string.IsNullOrEmpty(text) ? " " : text); }
|
||||
catch { text = text.Substring(0, text.Length - 1); }
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
protected override void SetAlpha(float a)
|
||||
{
|
||||
base.SetAlpha(a);
|
||||
textColor = new Color(textColor.R, textColor.G, textColor.B, a);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the color for all the states.
|
||||
/// </summary>
|
||||
public void OverrideTextColor(Color color)
|
||||
{
|
||||
textColor = color;
|
||||
hoverTextColor = color;
|
||||
selectedTextColor = color;
|
||||
disabledTextColor = color;
|
||||
}
|
||||
|
||||
protected override void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (!Visible) { return; }
|
||||
|
||||
Color currColor = GetColor(State);
|
||||
|
||||
var rect = Rect;
|
||||
|
||||
base.Draw(spriteBatch);
|
||||
|
||||
if (TextGetter != null) { Text = TextGetter(); }
|
||||
|
||||
Rectangle prevScissorRect = spriteBatch.GraphicsDevice.ScissorRectangle;
|
||||
if (overflowClipActive)
|
||||
{
|
||||
spriteBatch.End();
|
||||
Rectangle scissorRect = new Rectangle(rect.X + (int)padding.X, rect.Y, rect.Width - (int)padding.X - (int)padding.Z, rect.Height);
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = scissorRect;
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
Vector2 pos = rect.Location.ToVector2() + textPos + TextOffset;
|
||||
if (RoundToNearestPixel)
|
||||
{
|
||||
pos.X = (int)pos.X;
|
||||
pos.Y = (int)pos.Y;
|
||||
}
|
||||
|
||||
Color currentTextColor = State == ComponentState.Hover || State == ComponentState.HoverSelected ? HoverTextColor : TextColor;
|
||||
if (!enabled)
|
||||
{
|
||||
currentTextColor = disabledTextColor;
|
||||
}
|
||||
else if (State == ComponentState.Selected)
|
||||
{
|
||||
currentTextColor = selectedTextColor;
|
||||
}
|
||||
|
||||
if (!hasColorHighlight)
|
||||
{
|
||||
Font.DrawString(spriteBatch,
|
||||
Censor ? censoredText : (Wrap ? wrappedText : text),
|
||||
pos,
|
||||
currentTextColor * (currentTextColor.A / 255.0f),
|
||||
0.0f, origin, TextScale,
|
||||
SpriteEffects.None, textDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
Font.DrawStringWithColors(spriteBatch, Censor ? censoredText : (Wrap ? wrappedText : text), pos,
|
||||
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, colorData);
|
||||
}
|
||||
}
|
||||
|
||||
if (overflowClipActive)
|
||||
{
|
||||
spriteBatch.End();
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = prevScissorRect;
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, samplerState: GUI.SamplerState, rasterizerState: GameMain.ScissorTestEnable);
|
||||
}
|
||||
|
||||
if (OutlineColor.A * currColor.A > 0.0f) GUI.DrawRectangle(spriteBatch, rect, OutlineColor * (currColor.A / 255.0f), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the text scale of the GUITextBlocks so that they all use the same scale and can fit the text within the block.
|
||||
/// </summary>
|
||||
public static void AutoScaleAndNormalize(params GUITextBlock[] textBlocks)
|
||||
{
|
||||
AutoScaleAndNormalize(textBlocks.AsEnumerable<GUITextBlock>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the text scale of the GUITextBlocks so that they all use the same scale and can fit the text within the block.
|
||||
/// </summary>
|
||||
public static void AutoScaleAndNormalize(bool scaleHorizontal = true, bool scaleVertical = false, params GUITextBlock[] textBlocks)
|
||||
{
|
||||
AutoScaleAndNormalize(textBlocks.AsEnumerable<GUITextBlock>(), scaleHorizontal, scaleVertical);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the text scale of the GUITextBlocks so that they all use the same scale and can fit the text within the block.
|
||||
/// </summary>
|
||||
public static void AutoScaleAndNormalize(IEnumerable<GUITextBlock> textBlocks, bool scaleHorizontal = true, bool scaleVertical = false, float? defaultScale = null)
|
||||
{
|
||||
if (!textBlocks.Any()) { return; }
|
||||
float minScale = Math.Max(textBlocks.First().TextScale, 1.0f);
|
||||
foreach (GUITextBlock textBlock in textBlocks)
|
||||
{
|
||||
if (defaultScale.HasValue) { textBlock.TextScale = defaultScale.Value; }
|
||||
textBlock.AutoScaleHorizontal = scaleHorizontal;
|
||||
textBlock.AutoScaleVertical = scaleVertical;
|
||||
minScale = Math.Min(textBlock.TextScale, minScale);
|
||||
}
|
||||
|
||||
foreach (GUITextBlock textBlock in textBlocks)
|
||||
{
|
||||
textBlock.AutoScaleHorizontal = false;
|
||||
textBlock.AutoScaleVertical = false;
|
||||
textBlock.TextScale = minScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,9 @@ namespace Barotrauma
|
||||
bool caretVisible;
|
||||
float caretTimer;
|
||||
|
||||
private GUIFrame frame;
|
||||
private GUITextBlock textBlock;
|
||||
private readonly GUIFrame frame;
|
||||
private readonly GUITextBlock textBlock;
|
||||
private readonly GUIImage icon;
|
||||
|
||||
public Func<string, string> textFilterFunction;
|
||||
|
||||
@@ -48,7 +49,6 @@ namespace Barotrauma
|
||||
get { return _caretIndex; }
|
||||
set
|
||||
{
|
||||
previousCaretIndex = _caretIndex;
|
||||
_caretIndex = value;
|
||||
caretPosDirty = true;
|
||||
}
|
||||
@@ -63,7 +63,6 @@ namespace Barotrauma
|
||||
private int selectionStartIndex;
|
||||
private int selectionEndIndex;
|
||||
private bool IsLeftToRight => selectionStartIndex <= selectionEndIndex;
|
||||
private int previousCaretIndex;
|
||||
private Vector2 selectionStartPos;
|
||||
private Vector2 selectionEndPos;
|
||||
private Vector2 selectionRectSize;
|
||||
@@ -76,8 +75,8 @@ namespace Barotrauma
|
||||
set { textBlock.TextGetter = value; }
|
||||
}
|
||||
|
||||
private bool selected;
|
||||
public bool Selected
|
||||
private new bool selected;
|
||||
public new bool Selected
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -105,6 +104,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public GUITextBlock TextBlock
|
||||
{
|
||||
get { return textBlock; }
|
||||
}
|
||||
|
||||
//should the text be limited to the size of the box
|
||||
//ignored when MaxTextLength is set or text wrapping is enabled
|
||||
public bool ClampText
|
||||
@@ -134,7 +138,8 @@ namespace Barotrauma
|
||||
get { return enabled; }
|
||||
set
|
||||
{
|
||||
enabled = value;
|
||||
enabled = frame.Enabled = textBlock.Enabled = value;
|
||||
if (icon != null) { icon.Enabled = value; }
|
||||
if (!enabled && Selected)
|
||||
{
|
||||
Deselect();
|
||||
@@ -228,26 +233,62 @@ namespace Barotrauma
|
||||
{
|
||||
get { return textBlock.WrappedText; }
|
||||
}
|
||||
|
||||
|
||||
public bool Readonly { get; set; }
|
||||
|
||||
public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, ScalableFont font = null,
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null)
|
||||
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false)
|
||||
: base(style, rectT)
|
||||
{
|
||||
HoverCursor = CursorState.IBeam;
|
||||
CanBeFocused = true;
|
||||
|
||||
Enabled = true;
|
||||
this.color = color ?? Color.White;
|
||||
frame = new GUIFrame(new RectTransform(Vector2.One, rectT, Anchor.Center), style, color);
|
||||
GUI.Style.Apply(frame, style == "" ? "GUITextBox" : style);
|
||||
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.Center), text, textColor, font, textAlignment, wrap, playerInput: true);
|
||||
textBlock = new GUITextBlock(new RectTransform(Vector2.One, frame.RectTransform, Anchor.CenterLeft), text, textColor, font, textAlignment, wrap, playerInput: true);
|
||||
GUI.Style.Apply(textBlock, "", this);
|
||||
CaretEnabled = true;
|
||||
caretPosDirty = true;
|
||||
|
||||
new GUICustomComponent(new RectTransform(Vector2.One, frame.RectTransform), onDraw: DrawCaretAndSelection);
|
||||
|
||||
int clearButtonWidth = 0;
|
||||
if (createClearButton)
|
||||
{
|
||||
var clearButton = new GUIButton(new RectTransform(new Vector2(0.6f, 0.6f), frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5, 0) }, style: "GUICancelButton")
|
||||
{
|
||||
OnClicked = (bt, userdata) =>
|
||||
{
|
||||
Text = "";
|
||||
frame.Flash(Color.White);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
textBlock.RectTransform.MaxSize = new Point(frame.Rect.Width - clearButton.Rect.Height - clearButton.RectTransform.AbsoluteOffset.X * 2, int.MaxValue);
|
||||
clearButtonWidth = (int)(clearButton.Rect.Width * 1.2f);
|
||||
}
|
||||
|
||||
if (this.style != null && this.style.ChildStyles.ContainsKey("textboxicon"))
|
||||
{
|
||||
icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5 + clearButtonWidth, 0) }, null, scaleToFit: true);
|
||||
icon.ApplyStyle(this.style.ChildStyles["textboxicon"]);
|
||||
textBlock.RectTransform.MaxSize = new Point(frame.Rect.Width - icon.Rect.Height - clearButtonWidth - icon.RectTransform.AbsoluteOffset.X * 2, int.MaxValue);
|
||||
}
|
||||
Font = textBlock.Font;
|
||||
|
||||
rectT.SizeChanged += () => { caretPosDirty = true; };
|
||||
rectT.ScaleChanged += () => { caretPosDirty = true; };
|
||||
Enabled = true;
|
||||
|
||||
rectT.SizeChanged += () =>
|
||||
{
|
||||
if (icon != null) { textBlock.RectTransform.MaxSize = new Point(frame.Rect.Width - icon.Rect.Height - icon.RectTransform.AbsoluteOffset.X * 2, int.MaxValue); }
|
||||
caretPosDirty = true;
|
||||
};
|
||||
rectT.ScaleChanged += () =>
|
||||
{
|
||||
if (icon != null) { textBlock.RectTransform.MaxSize = new Point(frame.Rect.Width - icon.Rect.Height - icon.RectTransform.AbsoluteOffset.X * 2, int.MaxValue); }
|
||||
caretPosDirty = true;
|
||||
};
|
||||
}
|
||||
|
||||
private bool SetText(string text, bool store = true)
|
||||
@@ -417,13 +458,13 @@ namespace Barotrauma
|
||||
return currPosition != null ? currPosition.Item2 : textBlock.Text.Length;
|
||||
}
|
||||
|
||||
public void Select()
|
||||
public void Select(int forcedCaretIndex = -1)
|
||||
{
|
||||
if (memento.Current == null)
|
||||
{
|
||||
memento.Store(Text);
|
||||
}
|
||||
CaretIndex = GetCaretIndexFromScreenPos(PlayerInput.MousePosition);
|
||||
CaretIndex = forcedCaretIndex == - 1 ? GetCaretIndexFromScreenPos(PlayerInput.MousePosition) : forcedCaretIndex;
|
||||
ClearSelection();
|
||||
selected = true;
|
||||
GUI.KeyboardDispatcher.Subscriber = this;
|
||||
@@ -442,9 +483,9 @@ namespace Barotrauma
|
||||
OnDeselected?.Invoke(this, Keys.None);
|
||||
}
|
||||
|
||||
public override void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, Vector2? flashRectOffset = null)
|
||||
public override void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectOffset = null)
|
||||
{
|
||||
textBlock.Flash(color, flashDuration, useRectangleFlash, flashRectOffset);
|
||||
frame.Flash(color, flashDuration, useRectangleFlash, useCircularFlash, flashRectOffset);
|
||||
}
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
@@ -452,17 +493,17 @@ namespace Barotrauma
|
||||
if (!Visible) return;
|
||||
|
||||
if (flashTimer > 0.0f) flashTimer -= deltaTime;
|
||||
if (!Enabled) return;
|
||||
if (MouseRect.Contains(PlayerInput.MousePosition) && (GUI.MouseOn == null || GUI.IsMouseOn(this)))
|
||||
if (!Enabled) { return; }
|
||||
if (MouseRect.Contains(PlayerInput.MousePosition) && (GUI.MouseOn == null || (!(GUI.MouseOn is GUIButton) && GUI.IsMouseOn(this))))
|
||||
{
|
||||
state = ComponentState.Hover;
|
||||
if (PlayerInput.LeftButtonDown())
|
||||
State = ComponentState.Hover;
|
||||
if (PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
Select();
|
||||
}
|
||||
else
|
||||
{
|
||||
isSelecting = PlayerInput.LeftButtonHeld();
|
||||
isSelecting = PlayerInput.PrimaryMouseButtonHeld();
|
||||
}
|
||||
if (PlayerInput.DoubleClicked())
|
||||
{
|
||||
@@ -482,7 +523,7 @@ namespace Barotrauma
|
||||
{
|
||||
if ((PlayerInput.LeftButtonClicked() || PlayerInput.RightButtonClicked()) && selected) Deselect();
|
||||
isSelecting = false;
|
||||
state = ComponentState.None;
|
||||
State = ComponentState.None;
|
||||
}
|
||||
if (!isSelecting)
|
||||
{
|
||||
@@ -493,14 +534,14 @@ namespace Barotrauma
|
||||
{
|
||||
if (textBlock.OverflowClipActive)
|
||||
{
|
||||
if (CaretScreenPos.X < Rect.X + textBlock.Padding.X)
|
||||
if (CaretScreenPos.X < textBlock.Rect.X + textBlock.Padding.X)
|
||||
{
|
||||
textBlock.TextPos = new Vector2(textBlock.TextPos.X + ((Rect.X + textBlock.Padding.X) - CaretScreenPos.X), textBlock.TextPos.Y);
|
||||
textBlock.TextPos = new Vector2(textBlock.TextPos.X + ((textBlock.Rect.X + textBlock.Padding.X) - CaretScreenPos.X), textBlock.TextPos.Y);
|
||||
CalculateCaretPos();
|
||||
}
|
||||
else if (CaretScreenPos.X > Rect.Right - textBlock.Padding.Z)
|
||||
else if (CaretScreenPos.X > textBlock.Rect.Right - textBlock.Padding.Z)
|
||||
{
|
||||
textBlock.TextPos = new Vector2(textBlock.TextPos.X - (CaretScreenPos.X - (Rect.Right - textBlock.Padding.Z)), textBlock.TextPos.Y);
|
||||
textBlock.TextPos = new Vector2(textBlock.TextPos.X - (CaretScreenPos.X - (textBlock.Rect.Right - textBlock.Padding.Z)), textBlock.TextPos.Y);
|
||||
CalculateCaretPos();
|
||||
}
|
||||
}
|
||||
@@ -514,7 +555,7 @@ namespace Barotrauma
|
||||
|
||||
if (GUI.KeyboardDispatcher.Subscriber == this)
|
||||
{
|
||||
state = ComponentState.Selected;
|
||||
State = ComponentState.Selected;
|
||||
Character.DisableControls = true;
|
||||
if (OnEnterPressed != null && PlayerInput.KeyHit(Keys.Enter))
|
||||
{
|
||||
@@ -526,16 +567,12 @@ namespace Barotrauma
|
||||
Deselect();
|
||||
}
|
||||
|
||||
textBlock.State = state;
|
||||
textBlock.State = State;
|
||||
}
|
||||
|
||||
protected override void Draw(SpriteBatch spriteBatch)
|
||||
private void DrawCaretAndSelection(SpriteBatch spriteBatch, GUICustomComponent customComponent)
|
||||
{
|
||||
if (!Visible) return;
|
||||
base.Draw(spriteBatch);
|
||||
// Frame is not used in the old system.
|
||||
frame?.DrawManually(spriteBatch);
|
||||
textBlock.DrawManually(spriteBatch);
|
||||
if (!Visible) { return; }
|
||||
if (Selected)
|
||||
{
|
||||
if (caretVisible )
|
||||
@@ -553,9 +590,9 @@ namespace Barotrauma
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 20), selectionStartIndex.ToString(), Color.White, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(140, 20), selectionEndIndex.ToString(), Color.White, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 40), selectedText.ToString(), Color.Yellow, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 60), $"caret index: {CaretIndex.ToString()}", Color.Red, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 80), $"caret pos: {caretPos.ToString()}", Color.Red, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 100), $"caret screen pos: {CaretScreenPos.ToString()}", Color.Red, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 60), $"caret index: {CaretIndex.ToString()}", GUI.Style.Red, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 80), $"caret pos: {caretPos.ToString()}", GUI.Style.Red, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 100), $"caret screen pos: {CaretScreenPos.ToString()}", GUI.Style.Red, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 120), $"text start pos: {(textBlock.TextPos - textBlock.Origin).ToString()}", Color.White, Color.Black);
|
||||
//GUI.DrawString(spriteBatch, new Vector2(100, 140), $"cursor pos: {PlayerInput.MousePosition.ToString()}", Color.White, Color.Black);
|
||||
}
|
||||
@@ -635,6 +672,7 @@ namespace Barotrauma
|
||||
|
||||
public void ReceiveTextInput(string input)
|
||||
{
|
||||
if (Readonly) { return; }
|
||||
if (selectedCharacters > 0)
|
||||
{
|
||||
RemoveSelectedText();
|
||||
@@ -658,7 +696,7 @@ namespace Barotrauma
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case '\b': //backspace
|
||||
case '\b' when !Readonly: //backspace
|
||||
if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl))
|
||||
{
|
||||
SetText(string.Empty, false);
|
||||
@@ -680,7 +718,7 @@ namespace Barotrauma
|
||||
case (char)0x3: // ctrl-c
|
||||
CopySelectedText();
|
||||
break;
|
||||
case (char)0x16: // ctrl-v
|
||||
case (char)0x16 when !Readonly: // ctrl-v
|
||||
string text = GetCopiedText();
|
||||
RemoveSelectedText();
|
||||
if (SetText(Text.Insert(CaretIndex, text)))
|
||||
@@ -691,12 +729,15 @@ namespace Barotrauma
|
||||
break;
|
||||
case (char)0x18: // ctrl-x
|
||||
CopySelectedText();
|
||||
RemoveSelectedText();
|
||||
if (!Readonly)
|
||||
{
|
||||
RemoveSelectedText();
|
||||
}
|
||||
break;
|
||||
case (char)0x1: // ctrl-a
|
||||
SelectAll();
|
||||
break;
|
||||
case (char)0x1A: // ctrl-z
|
||||
case (char)0x1A when !Readonly: // ctrl-z
|
||||
text = memento.Undo();
|
||||
if (text != Text)
|
||||
{
|
||||
@@ -706,7 +747,7 @@ namespace Barotrauma
|
||||
OnTextChanged?.Invoke(this, Text);
|
||||
}
|
||||
break;
|
||||
case (char)0x12: // ctrl-r
|
||||
case (char)0x12 when !Readonly: // ctrl-r
|
||||
text = memento.Redo();
|
||||
if (text != Text)
|
||||
{
|
||||
@@ -763,7 +804,7 @@ namespace Barotrauma
|
||||
caretTimer = 0;
|
||||
HandleSelection();
|
||||
break;
|
||||
case Keys.Delete:
|
||||
case Keys.Delete when !Readonly:
|
||||
if (selectedCharacters > 0)
|
||||
{
|
||||
RemoveSelectedText();
|
||||
@@ -806,7 +847,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
IEnumerable<GUITextBox> GetAndSortTextBoxes(GUIComponent parent) => parent.GetAllChildren().Select(c => c as GUITextBox).Where(t => t != null).OrderBy(t => t.Rect.Y).ThenBy(t => t.Rect.X);
|
||||
IEnumerable<GUITextBox> GetAndSortTextBoxes(GUIComponent parent) => parent.GetAllChildren<GUITextBox>().OrderBy(t => t.Rect.Y).ThenBy(t => t.Rect.X);
|
||||
GUITextBox SelectNextTextBox(GUIListBox listBox)
|
||||
{
|
||||
var textBoxes = GetAndSortTextBoxes(listBox.SelectedComponent);
|
||||
224
Barotrauma/BarotraumaClient/ClientSource/GUI/GUITickBox.cs
Normal file
224
Barotrauma/BarotraumaClient/ClientSource/GUI/GUITickBox.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class GUITickBox : GUIComponent
|
||||
{
|
||||
private GUILayoutGroup layoutGroup;
|
||||
private GUIFrame box;
|
||||
private GUITextBlock text;
|
||||
|
||||
public delegate bool OnSelectedHandler(GUITickBox obj);
|
||||
public OnSelectedHandler OnSelected;
|
||||
|
||||
public static int size = 20;
|
||||
|
||||
private GUIRadioButtonGroup radioButtonGroup;
|
||||
|
||||
public override bool Selected
|
||||
{
|
||||
get { return selected; }
|
||||
set
|
||||
{
|
||||
if (value == selected) { return; }
|
||||
if (radioButtonGroup != null && radioButtonGroup.SelectedRadioButton == this)
|
||||
{
|
||||
selected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
selected = value;
|
||||
State = selected ? ComponentState.Selected : ComponentState.None;
|
||||
if (value && radioButtonGroup != null)
|
||||
{
|
||||
radioButtonGroup.SelectRadioButton(this);
|
||||
}
|
||||
|
||||
OnSelected?.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState State
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.State;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.State = value;
|
||||
box.State = TextBlock.State = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (value == enabled) { return; }
|
||||
enabled = box.Enabled = TextBlock.Enabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Color TextColor
|
||||
{
|
||||
get { return text.TextColor; }
|
||||
set { text.TextColor = value; }
|
||||
}
|
||||
|
||||
/*public override Rectangle MouseRect
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!CanBeFocused) return Rectangle.Empty;
|
||||
Rectangle union = Rectangle.Union(box.Rect, TextBlock.Rect);
|
||||
Vector2 textPos = TextBlock.Rect.Location.ToVector2() + TextBlock.TextPos + TextBlock.TextOffset;
|
||||
Vector2 textSize = TextBlock.Font.MeasureString(TextBlock.Text);
|
||||
union = Rectangle.Union(union, new Rectangle(textPos.ToPoint(), textSize.ToPoint()));
|
||||
union = Rectangle.Union(union, Rect);
|
||||
return ClampMouseRectToParent ? ClampRect(union) : union;
|
||||
}
|
||||
}*/
|
||||
|
||||
public override ScalableFont Font
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.Font;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
base.Font = value;
|
||||
if (text != null) text.Font = value;
|
||||
}
|
||||
}
|
||||
|
||||
public GUIFrame Box
|
||||
{
|
||||
get { return box; }
|
||||
}
|
||||
|
||||
public GUITextBlock TextBlock
|
||||
{
|
||||
get { return text; }
|
||||
}
|
||||
|
||||
public override string ToolTip
|
||||
{
|
||||
get { return base.ToolTip; }
|
||||
set
|
||||
{
|
||||
base.ToolTip = value;
|
||||
box.ToolTip = value;
|
||||
text.ToolTip = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return text.Text; }
|
||||
set { text.Text = value; }
|
||||
}
|
||||
|
||||
public GUITickBox(RectTransform rectT, string label, ScalableFont font = null, string style = "") : base(null, rectT)
|
||||
{
|
||||
CanBeFocused = true;
|
||||
HoverCursor = CursorState.Hand;
|
||||
|
||||
layoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, rectT), true);
|
||||
|
||||
box = new GUIFrame(new RectTransform(Vector2.One, layoutGroup.RectTransform, Anchor.CenterLeft, scaleBasis: ScaleBasis.BothHeight)
|
||||
{
|
||||
IsFixedSize = true
|
||||
}, string.Empty, Color.DarkGray)
|
||||
{
|
||||
HoverColor = Color.Gray,
|
||||
SelectedColor = Color.DarkGray,
|
||||
CanBeFocused = false
|
||||
};
|
||||
GUI.Style.Apply(box, style == "" ? "GUITickBox" : style);
|
||||
if (box.RectTransform.MinSize.Y > 0)
|
||||
{
|
||||
RectTransform.MinSize = box.RectTransform.MinSize;
|
||||
RectTransform.MaxSize = box.RectTransform.MaxSize;
|
||||
RectTransform.Resize(new Point(RectTransform.NonScaledSize.X, RectTransform.MinSize.Y));
|
||||
box.RectTransform.MinSize = new Point(box.RectTransform.MinSize.Y);
|
||||
box.RectTransform.Resize(box.RectTransform.MinSize);
|
||||
}
|
||||
Vector2 textBlockScale = new Vector2((float)(Rect.Width - Rect.Height) / (float)Math.Max(Rect.Width, 1.0), 1.0f);
|
||||
text = new GUITextBlock(new RectTransform(textBlockScale, layoutGroup.RectTransform, Anchor.CenterLeft), label, font: font, textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
GUI.Style.Apply(text, "GUITextBlock", this);
|
||||
Enabled = true;
|
||||
|
||||
ResizeBox();
|
||||
|
||||
rectT.ScaleChanged += ResizeBox;
|
||||
rectT.SizeChanged += ResizeBox;
|
||||
}
|
||||
|
||||
public void SetRadioButtonGroup(GUIRadioButtonGroup rbg)
|
||||
{
|
||||
radioButtonGroup = rbg;
|
||||
}
|
||||
|
||||
private void ResizeBox()
|
||||
{
|
||||
Vector2 textBlockScale = new Vector2(Math.Max(Rect.Width - box.Rect.Width, 0.0f) / Math.Max(Rect.Width, 1.0f), 1.0f);
|
||||
text.RectTransform.RelativeSize = textBlockScale;
|
||||
box.RectTransform.MinSize = new Point(Rect.Height);
|
||||
box.RectTransform.Resize(box.RectTransform.MinSize);
|
||||
text.SetTextPos();
|
||||
}
|
||||
|
||||
protected override void Update(float deltaTime)
|
||||
{
|
||||
if (!Visible) { return; }
|
||||
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (GUI.MouseOn == this && Enabled)
|
||||
{
|
||||
State = Selected ?
|
||||
ComponentState.HoverSelected :
|
||||
ComponentState.Hover;
|
||||
|
||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
State = ComponentState.Selected;
|
||||
}
|
||||
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
if (radioButtonGroup == null)
|
||||
{
|
||||
Selected = !Selected;
|
||||
}
|
||||
else if (!selected)
|
||||
{
|
||||
Selected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (selected)
|
||||
{
|
||||
State = ComponentState.Selected;
|
||||
}
|
||||
else
|
||||
{
|
||||
State = ComponentState.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
static class HUDLayoutSettings
|
||||
{
|
||||
public static bool DebugDraw;
|
||||
|
||||
private static int inventoryTopY;
|
||||
public static int InventoryTopY
|
||||
{
|
||||
get { return inventoryTopY; }
|
||||
set
|
||||
{
|
||||
if (value == inventoryTopY) return;
|
||||
inventoryTopY = value;
|
||||
CreateAreas();
|
||||
}
|
||||
}
|
||||
|
||||
public static Rectangle ButtonAreaTop
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle MessageAreaTop
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle CrewArea
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle ChatBoxArea
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle ObjectiveAnchor
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle InventoryAreaLower
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/*public static Rectangle HealthBarAreaRight
|
||||
{
|
||||
get; private set;
|
||||
}*/
|
||||
public static Rectangle HealthBarArea
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle BottomRightInfoArea
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle AfflictionAreaLeft
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle HealthWindowAreaLeft
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static Rectangle PortraitArea
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static int Padding
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
static HUDLayoutSettings()
|
||||
{
|
||||
if (GameMain.Instance != null)
|
||||
{
|
||||
GameMain.Instance.OnResolutionChanged += CreateAreas;
|
||||
GameMain.Config.OnHUDScaleChanged += CreateAreas;
|
||||
CreateAreas();
|
||||
CharacterInfo.Init();
|
||||
}
|
||||
}
|
||||
|
||||
public static RectTransform ToRectTransform(Rectangle rect, RectTransform parent)
|
||||
{
|
||||
return new RectTransform(new Vector2(rect.Width / (float)GameMain.GraphicsWidth, rect.Height / (float)GameMain.GraphicsHeight), parent)
|
||||
{
|
||||
RelativeOffset = new Vector2(rect.X / (float)GameMain.GraphicsWidth, rect.Y / (float)GameMain.GraphicsHeight)
|
||||
};
|
||||
}
|
||||
|
||||
public static void CreateAreas()
|
||||
{
|
||||
Padding = (int)(11 * GUI.Scale);
|
||||
|
||||
if (inventoryTopY == 0) { inventoryTopY = GameMain.GraphicsHeight - 30; }
|
||||
|
||||
//slice from the top of the screen for misc buttons (info, end round, server controls)
|
||||
ButtonAreaTop = new Rectangle(Padding, Padding, GameMain.GraphicsWidth - Padding * 2, (int)(50 * GUI.Scale));
|
||||
|
||||
int infoAreaWidth = (int)(142 * GUI.Scale);
|
||||
int infoAreaHeight = (int)(98 * GUI.Scale);
|
||||
int portraitSize = (int)(infoAreaHeight * 0.95f);
|
||||
BottomRightInfoArea = new Rectangle(GameMain.GraphicsWidth - Padding * 2 - infoAreaWidth, GameMain.GraphicsHeight - Padding * 2 - infoAreaHeight, infoAreaWidth, infoAreaHeight);
|
||||
PortraitArea = new Rectangle(GameMain.GraphicsWidth - portraitSize, BottomRightInfoArea.Bottom - portraitSize + Padding / 2, portraitSize, portraitSize);
|
||||
|
||||
//horizontal slices at the corners of the screen for health bar and affliction icons
|
||||
int afflictionAreaHeight = (int)(50 * GUI.Scale);
|
||||
int healthBarWidth = BottomRightInfoArea.Width + CharacterInventory.SlotSize.X + CharacterInventory.Spacing * 2 + CharacterInventory.HideButtonWidth;
|
||||
int healthBarHeight = (int)(50f * GUI.Scale);
|
||||
HealthBarArea = new Rectangle(BottomRightInfoArea.X - (healthBarWidth - BottomRightInfoArea.Width) + (int)(2 * GUI.Scale), BottomRightInfoArea.Y - healthBarHeight + (int)(10 * GUI.Scale), healthBarWidth, healthBarHeight);
|
||||
AfflictionAreaLeft = new Rectangle(HealthBarArea.X, HealthBarArea.Y - Padding - afflictionAreaHeight, HealthBarArea.Width, afflictionAreaHeight);
|
||||
|
||||
//HealthBarAreaRight = new Rectangle(Padding, GameMain.GraphicsHeight - healthBarHeight - Padding, healthBarWidth, healthBarHeight);
|
||||
/*if (HealthBarAreaRight.Y + healthBarHeight * 0.75f < PortraitArea.Y)
|
||||
{
|
||||
HealthBarAreaRight = new Rectangle(GameMain.GraphicsWidth - Padding - healthBarWidth, HealthBarAreaRight.Y, HealthBarAreaRight.Width, HealthBarAreaRight.Height);
|
||||
}*/
|
||||
//AfflictionAreaRight = new Rectangle(HealthBarAreaRight.X, HealthBarAreaRight.Y + healthBarHeight + Padding, healthBarWidth, afflictionAreaHeight);
|
||||
|
||||
int messageAreaWidth = GameMain.GraphicsWidth / 3;
|
||||
MessageAreaTop = new Rectangle((GameMain.GraphicsWidth - messageAreaWidth) / 2, ButtonAreaTop.Bottom, messageAreaWidth, ButtonAreaTop.Height);
|
||||
|
||||
bool isFourByThree = GUI.IsFourByThree();
|
||||
int chatBoxWidth = !isFourByThree ? (int)(475 * GUI.Scale) : (int)(375 * GUI.Scale);
|
||||
int chatBoxHeight = (int)Math.Max(GameMain.GraphicsHeight * 0.25f, 150);
|
||||
ChatBoxArea = new Rectangle(Padding, GameMain.GraphicsHeight - Padding - chatBoxHeight, chatBoxWidth, chatBoxHeight);
|
||||
|
||||
int objectiveAnchorWidth = (int)(250 * GUI.Scale);
|
||||
int objectiveAnchorOffsetY = (int)(150 * GUI.Scale);
|
||||
ObjectiveAnchor = new Rectangle(Padding, ChatBoxArea.Y - objectiveAnchorOffsetY, objectiveAnchorWidth, 0);
|
||||
|
||||
CrewArea = new Rectangle(Padding, Padding, (int)Math.Max(400 * GUI.Scale, 220), ObjectiveAnchor.Top - Padding * 2);
|
||||
|
||||
InventoryAreaLower = new Rectangle(Padding, inventoryTopY, GameMain.GraphicsWidth - Padding * 2, GameMain.GraphicsHeight - inventoryTopY);
|
||||
|
||||
int healthWindowWidth = (int)(GameMain.GraphicsWidth * 0.5f);
|
||||
int healthWindowHeight = (int)(GameMain.GraphicsWidth * 0.5f * 0.65f);
|
||||
int healthWindowX = GameMain.GraphicsWidth / 2 - healthWindowWidth / 2;
|
||||
int healthWindowY = GameMain.GraphicsHeight / 2 - healthWindowHeight / 2;
|
||||
|
||||
HealthWindowAreaLeft = new Rectangle(healthWindowX, healthWindowY, healthWindowWidth, healthWindowHeight);
|
||||
}
|
||||
|
||||
public static void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, ButtonAreaTop, Color.White * 0.5f);
|
||||
GUI.DrawRectangle(spriteBatch, MessageAreaTop, GUI.Style.Orange * 0.5f);
|
||||
GUI.DrawRectangle(spriteBatch, CrewArea, Color.Blue * 0.5f);
|
||||
GUI.DrawRectangle(spriteBatch, ChatBoxArea, Color.Cyan * 0.5f);
|
||||
GUI.DrawRectangle(spriteBatch, HealthBarArea, Color.Red * 0.5f);
|
||||
GUI.DrawRectangle(spriteBatch, AfflictionAreaLeft, Color.Red * 0.5f);
|
||||
GUI.DrawRectangle(spriteBatch, InventoryAreaLower, Color.Yellow * 0.5f);
|
||||
GUI.DrawRectangle(spriteBatch, HealthWindowAreaLeft, Color.Red * 0.5f);
|
||||
GUI.DrawRectangle(spriteBatch, BottomRightInfoArea, Color.Green * 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
public static class HUD
|
||||
{
|
||||
public static bool CloseHUD(Rectangle rect)
|
||||
{
|
||||
// Always close when hitting escape
|
||||
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) { return true; }
|
||||
|
||||
//don't close when the cursor is on a UI element
|
||||
if (GUI.MouseOn != null) { return false; }
|
||||
|
||||
//don't close when hovering over an inventory element
|
||||
if (Inventory.IsMouseOnInventory()) { return false; }
|
||||
|
||||
bool input = PlayerInput.PrimaryMouseButtonDown() || PlayerInput.SecondaryMouseButtonClicked();
|
||||
return input && !rect.Contains(PlayerInput.MousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
387
Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs
Normal file
387
Barotrauma/BarotraumaClient/ClientSource/GUI/LoadingScreen.cs
Normal file
@@ -0,0 +1,387 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Media;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class LoadingScreen
|
||||
{
|
||||
private readonly Texture2D defaultBackgroundTexture, overlay;
|
||||
private readonly SpriteSheet decorativeGraph, decorativeMap;
|
||||
private Texture2D currentBackgroundTexture;
|
||||
private Sprite noiseSprite;
|
||||
|
||||
private string randText = "";
|
||||
|
||||
private Sprite languageSelectionCursor;
|
||||
private ScalableFont languageSelectionFont, languageSelectionFontCJK;
|
||||
|
||||
private Video currSplashScreen;
|
||||
private DateTime videoStartTime;
|
||||
|
||||
public struct PendingSplashScreen
|
||||
{
|
||||
public string Filename;
|
||||
public float Gain;
|
||||
public PendingSplashScreen(string filename, float gain)
|
||||
{
|
||||
Filename = filename;
|
||||
Gain = gain;
|
||||
}
|
||||
}
|
||||
|
||||
private Queue<PendingSplashScreen> pendingSplashScreens = new Queue<PendingSplashScreen>();
|
||||
/// <summary>
|
||||
/// Triplet.first = filepath, Triplet.second = resolution, Triplet.third = audio gain
|
||||
/// </summary>
|
||||
public Queue<PendingSplashScreen> PendingSplashScreens
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
return pendingSplashScreens;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
pendingSplashScreens = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool PlayingSplashScreen
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
return currSplashScreen != null || pendingSplashScreens.Count > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string selectedTip;
|
||||
|
||||
private readonly object loadMutex = new object();
|
||||
private float? loadState;
|
||||
|
||||
public float? LoadState
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
return loadState;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
lock (loadMutex)
|
||||
{
|
||||
loadState = value;
|
||||
DrawLoadingText = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool DrawLoadingText
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool WaitForLanguageSelection
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public LoadingScreen(GraphicsDevice graphics)
|
||||
{
|
||||
defaultBackgroundTexture = TextureLoader.FromFile("Content/Map/LocationPortraits/AlienRuins.png");
|
||||
|
||||
decorativeMap = new SpriteSheet("Content/Map/MapHUD.png", 6, 5, Vector2.Zero, sourceRect: new Rectangle(0, 0, 2048, 640));
|
||||
decorativeGraph = new SpriteSheet("Content/Map/MapHUD.png", 4, 10, Vector2.Zero, sourceRect: new Rectangle(1025, 1259, 1024, 732));
|
||||
|
||||
overlay = TextureLoader.FromFile("Content/UI/LoadingScreenOverlay.png");
|
||||
noiseSprite = new Sprite("Content/UI/noise.png", Vector2.Zero);
|
||||
DrawLoadingText = true;
|
||||
selectedTip = TextManager.Get("LoadingScreenTip", true);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, GraphicsDevice graphics, float deltaTime)
|
||||
{
|
||||
if (GameMain.Config.EnableSplashScreen)
|
||||
{
|
||||
try
|
||||
{
|
||||
DrawSplashScreen(spriteBatch, graphics);
|
||||
if (currSplashScreen != null || PendingSplashScreens.Count > 0) { return; }
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Playing splash screen video failed", e);
|
||||
GameMain.Config.EnableSplashScreen = false;
|
||||
}
|
||||
}
|
||||
|
||||
var titleStyle = GUI.Style?.GetComponentStyle("TitleText");
|
||||
Sprite titleSprite = null;
|
||||
if (!WaitForLanguageSelection && titleStyle != null && titleStyle.Sprites.ContainsKey(GUIComponent.ComponentState.None))
|
||||
{
|
||||
titleSprite = titleStyle.Sprites[GUIComponent.ComponentState.None].First()?.Sprite;
|
||||
}
|
||||
|
||||
drawn = true;
|
||||
|
||||
currentBackgroundTexture ??= defaultBackgroundTexture;
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, samplerState: GUI.SamplerState);
|
||||
|
||||
float scale = (GameMain.GraphicsWidth / (float)currentBackgroundTexture.Width) * 1.2f;
|
||||
float paddingX = currentBackgroundTexture.Width * scale - GameMain.GraphicsWidth;
|
||||
float paddingY = currentBackgroundTexture.Height * scale - GameMain.GraphicsHeight;
|
||||
|
||||
double noiseT = (Timing.TotalTime * 0.02f);
|
||||
Vector2 pos = new Vector2((float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0.5f) - 0.5f);
|
||||
pos = new Vector2(pos.X * paddingX, pos.Y * paddingY);
|
||||
|
||||
spriteBatch.Draw(currentBackgroundTexture,
|
||||
new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight) / 2 + pos,
|
||||
null, Color.White, 0.0f, new Vector2(currentBackgroundTexture.Width / 2, currentBackgroundTexture.Height / 2),
|
||||
scale, SpriteEffects.None, 0.0f);
|
||||
|
||||
spriteBatch.Draw(overlay, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), null, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 0.0f);
|
||||
|
||||
float noiseStrength = (float)PerlinNoise.CalculatePerlin(noiseT, noiseT, 0);
|
||||
float noiseScale = (float)PerlinNoise.CalculatePerlin(noiseT * 5.0f, noiseT * 2.0f, 0) * 4.0f;
|
||||
noiseSprite.DrawTiled(spriteBatch, Vector2.Zero, new Vector2(GameMain.GraphicsWidth, GameMain.GraphicsHeight),
|
||||
startOffset: new Point(Rand.Range(0, noiseSprite.SourceRect.Width), Rand.Range(0, noiseSprite.SourceRect.Height)),
|
||||
color: Color.White * noiseStrength * 0.1f,
|
||||
textureScale: Vector2.One * noiseScale);
|
||||
|
||||
titleSprite?.Draw(spriteBatch, new Vector2(GameMain.GraphicsWidth * 0.05f, GameMain.GraphicsHeight * 0.125f),
|
||||
Color.White, origin: new Vector2(0.0f, titleSprite.SourceRect.Height / 2.0f),
|
||||
scale: GameMain.GraphicsHeight / 2000.0f);
|
||||
|
||||
if (WaitForLanguageSelection)
|
||||
{
|
||||
DrawLanguageSelectionPrompt(spriteBatch, graphics);
|
||||
}
|
||||
else if (DrawLoadingText)
|
||||
{
|
||||
if (TextManager.Initialized)
|
||||
{
|
||||
string loadText;
|
||||
if (LoadState == 100.0f)
|
||||
{
|
||||
#if DEBUG
|
||||
if (GameMain.Config.AutomaticQuickStartEnabled && GameMain.FirstLoad)
|
||||
{
|
||||
loadText = "QUICKSTARTING ...";
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
loadText = TextManager.Get("PressAnyKey");
|
||||
#if DEBUG
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
loadText = TextManager.Get("Loading");
|
||||
if (LoadState != null)
|
||||
{
|
||||
loadText += " " + (int)LoadState + " %";
|
||||
}
|
||||
}
|
||||
if (GUI.LargeFont != null)
|
||||
{
|
||||
GUI.LargeFont.DrawString(spriteBatch, loadText.ToUpper(),
|
||||
new Vector2(GameMain.GraphicsWidth / 2.0f - GUI.LargeFont.MeasureString(loadText.ToUpper()).X / 2.0f, GameMain.GraphicsHeight * 0.75f),
|
||||
Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
if (GUI.Font != null && selectedTip != null)
|
||||
{
|
||||
string wrappedTip = ToolBox.WrapText(selectedTip, GameMain.GraphicsWidth * 0.5f, GUI.Font);
|
||||
string[] lines = wrappedTip.Split('\n');
|
||||
float lineHeight = GUI.Font.MeasureString(selectedTip).Y;
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
GUI.Font.DrawString(spriteBatch, lines[i],
|
||||
new Vector2((int)(GameMain.GraphicsWidth / 2.0f - GUI.Font.MeasureString(lines[i]).X / 2.0f), (int)(GameMain.GraphicsHeight * 0.8f + i * lineHeight)), Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
spriteBatch.End();
|
||||
|
||||
spriteBatch.Begin(blendState: BlendState.Additive);
|
||||
|
||||
Vector2 decorativeScale = new Vector2(GameMain.GraphicsHeight / 1080.0f);
|
||||
|
||||
float noiseVal = (float)PerlinNoise.CalculatePerlin(Timing.TotalTime * 0.25f, Timing.TotalTime * 0.5f, 0);
|
||||
decorativeGraph.Draw(spriteBatch, (int)(decorativeGraph.FrameCount * noiseVal),
|
||||
new Vector2(GameMain.GraphicsWidth * 0.001f, GameMain.GraphicsHeight * 0.24f),
|
||||
Color.White, Vector2.Zero, 0.0f, decorativeScale, SpriteEffects.FlipVertically);
|
||||
|
||||
decorativeMap.Draw(spriteBatch, (int)(decorativeMap.FrameCount * noiseVal),
|
||||
new Vector2(GameMain.GraphicsWidth * 0.99f, GameMain.GraphicsHeight * 0.66f),
|
||||
Color.White, decorativeMap.FrameSize.ToVector2(), 0.0f, decorativeScale);
|
||||
|
||||
if (noiseVal < 0.2f)
|
||||
{
|
||||
//SCP-CB reference
|
||||
randText = (new string[] { "NIL", "black white gray", "Sometimes we would have had time to scream", "e8m106]af", "NO" }).GetRandom();
|
||||
}
|
||||
else if (noiseVal < 0.3f)
|
||||
{
|
||||
randText = ToolBox.RandomSeed(9);
|
||||
}
|
||||
else if (noiseVal < 0.5f)
|
||||
{
|
||||
randText =
|
||||
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
|
||||
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
|
||||
Rand.Int(100).ToString().PadLeft(2, '0') + " " +
|
||||
Rand.Int(100).ToString().PadLeft(2, '0');
|
||||
}
|
||||
|
||||
GUI.LargeFont?.DrawString(spriteBatch, randText,
|
||||
new Vector2(GameMain.GraphicsWidth - decorativeMap.FrameSize.X * decorativeScale.X * 0.8f, GameMain.GraphicsHeight * 0.57f),
|
||||
Color.White * (1.0f - noiseVal));
|
||||
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
private void DrawLanguageSelectionPrompt(SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
|
||||
{
|
||||
if (languageSelectionFont == null)
|
||||
{
|
||||
languageSelectionFont = new ScalableFont("Content/Fonts/NotoSans/NotoSans-Bold.ttf",
|
||||
(uint)(30 * (GameMain.GraphicsHeight / 1080.0f)), graphicsDevice);
|
||||
}
|
||||
if (languageSelectionFontCJK == null)
|
||||
{
|
||||
languageSelectionFontCJK = new ScalableFont("Content/Fonts/NotoSans/NotoSansCJKsc-Bold.otf",
|
||||
(uint)(30 * (GameMain.GraphicsHeight / 1080.0f)), graphicsDevice, dynamicLoading: true);
|
||||
}
|
||||
if (languageSelectionCursor == null)
|
||||
{
|
||||
languageSelectionCursor = new Sprite("Content/UI/cursor.png", Vector2.Zero);
|
||||
}
|
||||
|
||||
Vector2 textPos = new Vector2(GameMain.GraphicsWidth / 2, GameMain.GraphicsHeight * 0.3f);
|
||||
Vector2 textSpacing = new Vector2(0.0f, (GameMain.GraphicsHeight * 0.5f) / TextManager.AvailableLanguages.Count());
|
||||
foreach (string language in TextManager.AvailableLanguages)
|
||||
{
|
||||
string localizedLanguageName = TextManager.GetTranslatedLanguageName(language);
|
||||
var font = TextManager.IsCJK(localizedLanguageName) ? languageSelectionFontCJK : languageSelectionFont;
|
||||
|
||||
Vector2 textSize = font.MeasureString(localizedLanguageName);
|
||||
bool hover =
|
||||
Math.Abs(PlayerInput.MousePosition.X - textPos.X) < textSize.X / 2 &&
|
||||
Math.Abs(PlayerInput.MousePosition.Y - textPos.Y) < textSpacing.Y / 2;
|
||||
|
||||
font.DrawString(spriteBatch, localizedLanguageName, textPos - textSize / 2,
|
||||
hover ? Color.White : Color.White * 0.6f);
|
||||
if (hover && PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
GameMain.Config.Language = language;
|
||||
//reload tip in the selected language
|
||||
selectedTip = TextManager.Get("LoadingScreenTip", true);
|
||||
GameMain.Config.SetDefaultBindings(legacy: false);
|
||||
GameMain.Config.CheckBindings(useDefaults: true);
|
||||
WaitForLanguageSelection = false;
|
||||
languageSelectionFont?.Dispose(); languageSelectionFont = null;
|
||||
languageSelectionFontCJK?.Dispose(); languageSelectionFontCJK = null;
|
||||
break;
|
||||
}
|
||||
|
||||
textPos += textSpacing;
|
||||
}
|
||||
|
||||
languageSelectionCursor.Draw(spriteBatch, PlayerInput.LatestMousePosition, scale: 0.5f);
|
||||
}
|
||||
|
||||
private void DrawSplashScreen(SpriteBatch spriteBatch, GraphicsDevice graphics)
|
||||
{
|
||||
if (currSplashScreen == null && PendingSplashScreens.Count == 0) { return; }
|
||||
|
||||
if (currSplashScreen == null)
|
||||
{
|
||||
var newSplashScreen = PendingSplashScreens.Dequeue();
|
||||
string fileName = newSplashScreen.Filename;
|
||||
try
|
||||
{
|
||||
currSplashScreen = Video.Load(graphics, GameMain.SoundManager, fileName);
|
||||
currSplashScreen.AudioGain = newSplashScreen.Gain;
|
||||
videoStartTime = DateTime.Now;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
GameMain.Config.EnableSplashScreen = false;
|
||||
DebugConsole.ThrowError("Playing the splash screen \"" + fileName + "\" failed.", e);
|
||||
PendingSplashScreens.Clear();
|
||||
currSplashScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (currSplashScreen.IsPlaying)
|
||||
{
|
||||
spriteBatch.Begin();
|
||||
spriteBatch.Draw(currSplashScreen.GetTexture(), new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);
|
||||
spriteBatch.End();
|
||||
|
||||
if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 500) && GameMain.WindowActive && (PlayerInput.KeyHit(Keys.Space) || PlayerInput.KeyHit(Keys.Enter) || PlayerInput.PrimaryMouseButtonDown()))
|
||||
{
|
||||
currSplashScreen.Dispose(); currSplashScreen = null;
|
||||
}
|
||||
}
|
||||
else if (DateTime.Now > videoStartTime + new TimeSpan(0, 0, 0, 0, milliseconds: 1500))
|
||||
{
|
||||
currSplashScreen.Dispose(); currSplashScreen = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool drawn;
|
||||
public IEnumerable<object> DoLoading(IEnumerable<object> loader)
|
||||
{
|
||||
drawn = false;
|
||||
LoadState = null;
|
||||
selectedTip = TextManager.Get("LoadingScreenTip", true);
|
||||
currentBackgroundTexture = LocationType.List.GetRandom()?.GetPortrait(Rand.Int(int.MaxValue))?.Texture;
|
||||
|
||||
while (!drawn)
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
CoroutineManager.StartCoroutine(loader);
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
|
||||
while (CoroutineManager.IsCoroutineRunning(loader.ToString()))
|
||||
{
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
LoadState = 100.0f;
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,9 @@ namespace Barotrauma
|
||||
Parent = new GUIFrame(rectT, null, Color);
|
||||
EditorBox = new GUIListBox(new RectTransform(Vector2.One * 0.98f, rectT, Anchor.Center), color: Color.Black, style: null)
|
||||
{
|
||||
Spacing = 10
|
||||
Spacing = 10,
|
||||
AutoHideScrollBar = true,
|
||||
KeepSpaceForScrollBar = true
|
||||
};
|
||||
return EditorBox;
|
||||
}
|
||||
@@ -23,7 +23,9 @@ namespace Barotrauma
|
||||
|
||||
public enum ScaleBasis
|
||||
{
|
||||
Normal, BothWidth, BothHeight
|
||||
Normal,
|
||||
BothWidth, BothHeight,
|
||||
Smallest, Largest
|
||||
}
|
||||
|
||||
public class RectTransform
|
||||
@@ -50,13 +52,13 @@ namespace Barotrauma
|
||||
{
|
||||
parent.children.Add(this);
|
||||
RecalculateAll(false, true, true);
|
||||
ParentChanged?.Invoke(parent);
|
||||
Parent.ChildrenChanged?.Invoke(this);
|
||||
}
|
||||
ParentChanged?.Invoke(parent);
|
||||
}
|
||||
}
|
||||
|
||||
private List<RectTransform> children = new List<RectTransform>();
|
||||
private readonly List<RectTransform> children = new List<RectTransform>();
|
||||
public IEnumerable<RectTransform> Children => children;
|
||||
|
||||
public int CountChildren => children.Count;
|
||||
@@ -291,7 +293,16 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private ScaleBasis scaleBasis;
|
||||
private ScaleBasis _scaleBasis;
|
||||
public ScaleBasis ScaleBasis
|
||||
{
|
||||
get { return _scaleBasis; }
|
||||
set
|
||||
{
|
||||
_scaleBasis = value;
|
||||
RecalculateAbsoluteSize();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLastChild
|
||||
{
|
||||
@@ -330,7 +341,7 @@ namespace Barotrauma
|
||||
public RectTransform(Vector2 relativeSize, RectTransform parent, Anchor anchor = Anchor.TopLeft, Pivot? pivot = null, Point? minSize = null, Point? maxSize = null, ScaleBasis scaleBasis = ScaleBasis.Normal)
|
||||
{
|
||||
Init(parent, anchor, pivot);
|
||||
this.scaleBasis = scaleBasis;
|
||||
_scaleBasis = scaleBasis;
|
||||
this.relativeSize = relativeSize;
|
||||
this.minSize = minSize;
|
||||
this.maxSize = maxSize;
|
||||
@@ -342,19 +353,23 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// By default, elements defined with an absolute size (in pixels), will be treated as fixed sized.
|
||||
/// This can be changed by setting IsFixedSize to false.
|
||||
/// By default, elements defined with an absolute size (in pixels) will scale with the parent.
|
||||
/// This can be changed by setting IsFixedSize to true.
|
||||
/// </summary>
|
||||
public RectTransform(Point absoluteSize, RectTransform parent = null, Anchor anchor = Anchor.TopLeft, Pivot? pivot = null)
|
||||
public RectTransform(Point absoluteSize, RectTransform parent = null, Anchor anchor = Anchor.TopLeft, Pivot? pivot = null, ScaleBasis scaleBasis = ScaleBasis.Normal, bool isFixedSize = false)
|
||||
{
|
||||
Init(parent, anchor, pivot);
|
||||
this.scaleBasis = ScaleBasis.Normal;
|
||||
_scaleBasis = scaleBasis;
|
||||
this.nonScaledSize = absoluteSize;
|
||||
RecalculateScale();
|
||||
RecalculateRelativeSize();
|
||||
RecalculateRelativeSize();
|
||||
if (scaleBasis != ScaleBasis.Normal)
|
||||
{
|
||||
RecalculateAbsoluteSize();
|
||||
}
|
||||
RecalculateAnchorPoint();
|
||||
RecalculatePivotOffset();
|
||||
IsFixedSize = true;
|
||||
IsFixedSize = isFixedSize;
|
||||
parent?.ChildrenChanged?.Invoke(this);
|
||||
}
|
||||
|
||||
@@ -364,13 +379,24 @@ namespace Barotrauma
|
||||
Enum.TryParse(element.GetAttributeString("pivot", anchor.ToString()), out Pivot pivot);
|
||||
|
||||
Point? minSize = null, maxSize = null;
|
||||
if (element.Attribute("minsize") != null) minSize = element.GetAttributePoint("minsize", Point.Zero);
|
||||
//if (element.Attribute("maxsize") != null) maxSize = element.GetAttributePoint("maxsize", new Point(1000, 1000));
|
||||
|
||||
ScaleBasis scaleBasis = ScaleBasis.Normal;
|
||||
if (element.Attribute("minsize") != null)
|
||||
{
|
||||
minSize = element.GetAttributePoint("minsize", Point.Zero);
|
||||
}
|
||||
if (element.Attribute("maxsize") != null)
|
||||
{
|
||||
maxSize = element.GetAttributePoint("maxsize", new Point(1000, 1000));
|
||||
}
|
||||
string sb = element.GetAttributeString("scalebasis", null);
|
||||
if (sb != null)
|
||||
{
|
||||
Enum.TryParse(sb, ignoreCase: true, out scaleBasis);
|
||||
}
|
||||
RectTransform rectTransform;
|
||||
if (element.Attribute("absolutesize") != null)
|
||||
{
|
||||
rectTransform = new RectTransform(element.GetAttributePoint("absolutesize", new Point(1000, 1000)), parent, anchor, pivot)
|
||||
rectTransform = new RectTransform(element.GetAttributePoint("absolutesize", new Point(1000, 1000)), parent, anchor, pivot, scaleBasis)
|
||||
{
|
||||
minSize = minSize,
|
||||
maxSize = maxSize
|
||||
@@ -378,7 +404,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
rectTransform = new RectTransform(element.GetAttributeVector2("relativesize", Vector2.One), parent, anchor, pivot, minSize, maxSize);
|
||||
rectTransform = new RectTransform(element.GetAttributeVector2("relativesize", Vector2.One), parent, anchor, pivot, minSize, maxSize, scaleBasis);
|
||||
}
|
||||
rectTransform.RelativeOffset = element.GetAttributeVector2("relativeoffset", Vector2.Zero);
|
||||
rectTransform.AbsoluteOffset = element.GetAttributePoint("absoluteoffset", Point.Zero);
|
||||
@@ -426,15 +452,37 @@ namespace Barotrauma
|
||||
protected void RecalculateAbsoluteSize()
|
||||
{
|
||||
Point size = NonScaledParentRect.Size;
|
||||
if (scaleBasis == ScaleBasis.BothWidth)
|
||||
switch (ScaleBasis)
|
||||
{
|
||||
size.Y = size.X;
|
||||
case ScaleBasis.BothWidth:
|
||||
size.Y = size.X;
|
||||
break;
|
||||
case ScaleBasis.BothHeight:
|
||||
size.X = size.Y;
|
||||
break;
|
||||
case ScaleBasis.Smallest:
|
||||
if (size.X < size.Y)
|
||||
{
|
||||
size.Y = size.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
size.X = size.Y;
|
||||
}
|
||||
break;
|
||||
case ScaleBasis.Largest:
|
||||
if (size.X > size.Y)
|
||||
{
|
||||
size.Y = size.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
size.X = size.Y;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (scaleBasis == ScaleBasis.BothHeight)
|
||||
{
|
||||
size.X = size.Y;
|
||||
}
|
||||
nonScaledSize = size.Multiply(RelativeSize).Clamp(MinSize, MaxSize);
|
||||
size = size.Multiply(RelativeSize);
|
||||
nonScaledSize = size.Clamp(MinSize, MaxSize);
|
||||
recalculateRect = true;
|
||||
SizeChanged?.Invoke();
|
||||
}
|
||||
@@ -464,7 +512,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool RemoveFromHierarchy(bool displayErrors = true, bool recalculate = true)
|
||||
private bool RemoveFromHierarchy(bool displayErrors = true)
|
||||
{
|
||||
if (Parent == null)
|
||||
{
|
||||
@@ -506,7 +554,7 @@ namespace Barotrauma
|
||||
|
||||
public void Resize(Point absoluteSize, bool resizeChildren = true)
|
||||
{
|
||||
nonScaledSize = absoluteSize;
|
||||
nonScaledSize = absoluteSize.Clamp(MinSize, MaxSize);
|
||||
RecalculateRelativeSize();
|
||||
RecalculateAll(resize: false, scale: false, withChildren: false);
|
||||
RecalculateChildren(resizeChildren, false);
|
||||
@@ -573,7 +621,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public IEnumerable<RectTransform> GetAllChildren()
|
||||
{
|
||||
return children.SelectManyRecursive(c => c.children);
|
||||
return children.Concat(children.SelectManyRecursive(c => c.children));
|
||||
}
|
||||
|
||||
public int GetChildIndex(RectTransform rectT)
|
||||
@@ -599,7 +647,7 @@ namespace Barotrauma
|
||||
public void SortChildren(Comparison<RectTransform> comparison)
|
||||
{
|
||||
children.Sort(comparison);
|
||||
RecalculateAll(false, true, true);
|
||||
RecalculateAll(false, false, true);
|
||||
Parent.ChildrenChanged?.Invoke(this);
|
||||
}
|
||||
|
||||
@@ -650,6 +698,53 @@ namespace Barotrauma
|
||||
children[i].GUIComponent.AddToGUIUpdateList(ignoreChildren, order);
|
||||
}
|
||||
}
|
||||
|
||||
public void MatchPivotToAnchor() => MatchPivotToAnchor(Anchor);
|
||||
|
||||
|
||||
private Point? animTargetPos;
|
||||
public Point AnimTargetPos
|
||||
{
|
||||
get { return animTargetPos ?? AbsoluteOffset; }
|
||||
}
|
||||
|
||||
public void MoveOverTime(Point targetPos, float duration)
|
||||
{
|
||||
animTargetPos = targetPos;
|
||||
CoroutineManager.StartCoroutine(DoMoveAnimation(targetPos, duration));
|
||||
}
|
||||
public void ScaleOverTime(Point targetSize, float duration)
|
||||
{
|
||||
CoroutineManager.StartCoroutine(DoScaleAnimation(targetSize, duration));
|
||||
}
|
||||
|
||||
private IEnumerable<object> DoMoveAnimation(Point targetPos, float duration)
|
||||
{
|
||||
Vector2 startPos = AbsoluteOffset.ToVector2();
|
||||
float t = 0.0f;
|
||||
while (t < duration && duration > 0.0f)
|
||||
{
|
||||
t += CoroutineManager.DeltaTime;
|
||||
AbsoluteOffset = Vector2.SmoothStep(startPos, targetPos.ToVector2(), t / duration).ToPoint();
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
AbsoluteOffset = targetPos;
|
||||
animTargetPos = null;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
private IEnumerable<object> DoScaleAnimation(Point targetSize, float duration)
|
||||
{
|
||||
Vector2 startSize = NonScaledSize.ToVector2();
|
||||
float t = 0.0f;
|
||||
while (t < duration && duration > 0.0f)
|
||||
{
|
||||
t += CoroutineManager.DeltaTime;
|
||||
NonScaledSize = Vector2.SmoothStep(startSize, targetSize.ToVector2(), t / duration).ToPoint();
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
NonScaledSize = targetSize;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static methods
|
||||
173
Barotrauma/BarotraumaClient/ClientSource/GUI/UISprite.cs
Normal file
173
Barotrauma/BarotraumaClient/ClientSource/GUI/UISprite.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class UISprite
|
||||
{
|
||||
public Sprite Sprite
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool Tile
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool Slice
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Rectangle[] Slices
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public bool MaintainAspectRatio
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How much the borders of a sliced sprite are allowed to scale
|
||||
/// You may for example want to prevent a 1-pixel border from scaling down (and disappearing) on small resolutions
|
||||
/// </summary>
|
||||
private readonly float minBorderScale = 0.1f, maxBorderScale = 10.0f;
|
||||
|
||||
public bool CrossFadeIn { get; private set; } = true;
|
||||
public bool CrossFadeOut { get; private set; } = true;
|
||||
|
||||
public TransitionMode TransitionMode { get; private set; }
|
||||
|
||||
public UISprite(XElement element)
|
||||
{
|
||||
Sprite = new Sprite(element);
|
||||
MaintainAspectRatio = element.GetAttributeBool("maintainaspectratio", false);
|
||||
Tile = element.GetAttributeBool("tile", true);
|
||||
CrossFadeIn = element.GetAttributeBool("crossfadein", CrossFadeIn);
|
||||
CrossFadeOut = element.GetAttributeBool("crossfadeout", CrossFadeOut);
|
||||
string transitionMode = element.GetAttributeString("transition", string.Empty);
|
||||
if (Enum.TryParse(transitionMode, ignoreCase: true, out TransitionMode transition))
|
||||
{
|
||||
TransitionMode = transition;
|
||||
}
|
||||
|
||||
Vector4 sliceVec = element.GetAttributeVector4("slice", Vector4.Zero);
|
||||
if (sliceVec != Vector4.Zero)
|
||||
{
|
||||
minBorderScale = element.GetAttributeFloat("minborderscale", 0.1f);
|
||||
maxBorderScale = element.GetAttributeFloat("minborderscale", 10.0f);
|
||||
|
||||
Rectangle slice = new Rectangle((int)sliceVec.X, (int)sliceVec.Y, (int)(sliceVec.Z - sliceVec.X), (int)(sliceVec.W - sliceVec.Y));
|
||||
|
||||
Slice = true;
|
||||
Slices = new Rectangle[9];
|
||||
|
||||
//top-left
|
||||
Slices[0] = new Rectangle(Sprite.SourceRect.Location, slice.Location - Sprite.SourceRect.Location);
|
||||
//top-mid
|
||||
Slices[1] = new Rectangle(slice.Location.X, Slices[0].Y, slice.Width, Slices[0].Height);
|
||||
//top-right
|
||||
Slices[2] = new Rectangle(slice.Right, Slices[0].Y, Sprite.SourceRect.Right - slice.Right, Slices[0].Height);
|
||||
|
||||
//mid-left
|
||||
Slices[3] = new Rectangle(Slices[0].X, slice.Y, Slices[0].Width, slice.Height);
|
||||
//center
|
||||
Slices[4] = slice;
|
||||
//mid-right
|
||||
Slices[5] = new Rectangle(Slices[2].X, slice.Y, Slices[2].Width, slice.Height);
|
||||
|
||||
//bottom-left
|
||||
Slices[6] = new Rectangle(Slices[0].X, slice.Bottom, Slices[0].Width, Sprite.SourceRect.Bottom - slice.Bottom);
|
||||
//bottom-mid
|
||||
Slices[7] = new Rectangle(Slices[1].X, slice.Bottom, Slices[1].Width, Sprite.SourceRect.Bottom - slice.Bottom);
|
||||
//bottom-right
|
||||
Slices[8] = new Rectangle(Slices[2].X, slice.Bottom, Slices[2].Width, Sprite.SourceRect.Bottom - slice.Bottom);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the scale of the sliced sprite's borders when it's draw inside an area of a specific size
|
||||
/// </summary>
|
||||
public float GetSliceBorderScale(Point drawSize)
|
||||
{
|
||||
if (!Slice) { return 1.0f; }
|
||||
|
||||
Vector2 scale = new Vector2(
|
||||
MathHelper.Clamp((float)drawSize.X / (Slices[0].Height + Slices[6].Height), 0, 1),
|
||||
MathHelper.Clamp((float)drawSize.Y / (Slices[0].Width + Slices[2].Width), 0, 1));
|
||||
return MathHelper.Clamp(Math.Min(Math.Min(scale.X, scale.Y), GUI.SlicedSpriteScale), minBorderScale, maxBorderScale);
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Rectangle rect, Color color, SpriteEffects spriteEffects = SpriteEffects.None)
|
||||
{
|
||||
if (Sprite.Texture == null)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, rect, Color.Magenta);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Slice)
|
||||
{
|
||||
Vector2 pos = new Vector2(rect.X, rect.Y);
|
||||
|
||||
float scale = GetSliceBorderScale(rect.Size);
|
||||
int centerHeight = rect.Height - (int)((Slices[0].Height + Slices[6].Height) * scale);
|
||||
int centerWidth = rect.Width - (int)((Slices[0].Width + Slices[2].Width) * scale);
|
||||
|
||||
for (int x = 0; x < 3; x++)
|
||||
{
|
||||
int width = (int)(x == 1 ? centerWidth : Slices[x].Width * scale);
|
||||
if (width <= 0) { continue; }
|
||||
for (int y = 0; y < 3; y++)
|
||||
{
|
||||
int height = (int)(y == 1 ? centerHeight : Slices[x + y * 3].Height * scale);
|
||||
if (height <= 0) { continue; }
|
||||
|
||||
spriteBatch.Draw(Sprite.Texture,
|
||||
new Rectangle((int)pos.X, (int)pos.Y, width, height),
|
||||
Slices[x + y * 3],
|
||||
color);
|
||||
|
||||
pos.Y += height;
|
||||
}
|
||||
pos.X += width;
|
||||
pos.Y = rect.Y;
|
||||
}
|
||||
}
|
||||
else if (Tile)
|
||||
{
|
||||
Vector2 startPos = new Vector2(rect.X, rect.Y);
|
||||
Sprite.DrawTiled(spriteBatch, startPos, new Vector2(rect.Width, rect.Height), null, color);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MaintainAspectRatio)
|
||||
{
|
||||
float scale = Math.Min((float)rect.Width / Sprite.SourceRect.Width, (float)rect.Height / Sprite.SourceRect.Height);
|
||||
|
||||
spriteBatch.Draw(Sprite.Texture, rect.Center.ToVector2(),
|
||||
Sprite.SourceRect,
|
||||
color,
|
||||
rotation: 0.0f,
|
||||
origin: Sprite.size / 2.0f,
|
||||
scale: scale,
|
||||
effects: spriteEffects, layerDepth: 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteBatch.Draw(Sprite.Texture, rect, Sprite.SourceRect, color, 0, Vector2.Zero, spriteEffects, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma
|
||||
private GUICustomComponent videoView;
|
||||
private GUIButton okButton;
|
||||
|
||||
private Color backgroundColor = new Color(0f, 0f, 0f, 1f);
|
||||
private Color backgroundColor = new Color(0f, 0f, 0f, 0.8f);
|
||||
private Action callbackOnStop;
|
||||
|
||||
private Point scaledVideoResolution;
|
||||
@@ -62,8 +62,8 @@ namespace Barotrauma
|
||||
int width = scaledVideoResolution.X;
|
||||
int height = scaledVideoResolution.Y;
|
||||
|
||||
background = new GUIFrame(new RectTransform(Point.Zero, GUI.Canvas, Anchor.Center), "InnerFrame", backgroundColor);
|
||||
videoFrame = new GUIFrame(new RectTransform(Point.Zero, background.RectTransform, Anchor.Center, Pivot.Center), "SonarFrame");
|
||||
background = new GUIFrame(new RectTransform(Point.Zero, GUI.Canvas, Anchor.Center), style: null, color: backgroundColor);
|
||||
videoFrame = new GUIFrame(new RectTransform(Point.Zero, background.RectTransform, Anchor.Center, Pivot.Center), style: "InnerFrame");
|
||||
|
||||
if (useTextOnRightSide)
|
||||
{
|
||||
@@ -75,13 +75,13 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
videoView = new GUICustomComponent(new RectTransform(Point.Zero, videoFrame.RectTransform, Anchor.Center), (spriteBatch, guiCustomComponent) => { DrawVideo(spriteBatch, guiCustomComponent.Rect); });
|
||||
title = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft), string.Empty, font: GUI.VideoTitleFont, textColor: new Color(253, 174, 0), textAlignment: Alignment.Left);
|
||||
title = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft), string.Empty, font: GUI.LargeFont, textColor: new Color(253, 174, 0), textAlignment: Alignment.Left);
|
||||
|
||||
textContent = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft), string.Empty, font: GUI.Font, textAlignment: Alignment.TopLeft);
|
||||
|
||||
objectiveTitle = new GUITextBlock(new RectTransform(new Vector2(1f, 0f), textFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter), string.Empty, font: GUI.ObjectiveTitleFont, textAlignment: Alignment.CenterRight, textColor: Color.White);
|
||||
objectiveTitle = new GUITextBlock(new RectTransform(new Vector2(1f, 0f), textFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter), string.Empty, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterRight, textColor: Color.White);
|
||||
objectiveTitle.Text = TextManager.Get("Tutorial.NewObjective");
|
||||
objectiveText = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter), string.Empty, font: GUI.ObjectiveNameFont, textColor: new Color(4, 180, 108), textAlignment: Alignment.CenterRight);
|
||||
objectiveText = new GUITextBlock(new RectTransform(Point.Zero, textFrame.RectTransform, Anchor.TopCenter, Pivot.TopCenter), string.Empty, font: GUI.SubHeadingFont, textColor: new Color(4, 180, 108), textAlignment: Alignment.CenterRight);
|
||||
|
||||
objectiveTitle.Visible = objectiveText.Visible = false;
|
||||
}
|
||||
@@ -111,9 +111,7 @@ namespace Barotrauma
|
||||
if (currentVideo == null) return;
|
||||
if (currentVideo.IsPlaying) return;
|
||||
|
||||
currentVideo.Dispose();
|
||||
currentVideo = null;
|
||||
currentVideo = CreateVideo(scaledVideoResolution);
|
||||
currentVideo.Play();
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList(bool ignoreChildren = false, int order = 0)
|
||||
@@ -140,7 +138,7 @@ namespace Barotrauma
|
||||
currentVideo = null;
|
||||
}
|
||||
|
||||
currentVideo = CreateVideo(scaledVideoResolution);
|
||||
currentVideo = CreateVideo();
|
||||
title.Text = textSettings != null ? TextManager.Get(contentId) : string.Empty;
|
||||
textContent.Text = textSettings != null ? textSettings.Text : string.Empty;
|
||||
objectiveText.Text = objective;
|
||||
@@ -178,10 +176,10 @@ namespace Barotrauma
|
||||
|
||||
background.RectTransform.NonScaledSize = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
|
||||
videoFrame.RectTransform.NonScaledSize += scaledVideoResolution + new Point(scaledBorderSize, scaledBorderSize);
|
||||
videoView.RectTransform.NonScaledSize += scaledVideoResolution;
|
||||
videoFrame.RectTransform.NonScaledSize = scaledVideoResolution + new Point(scaledBorderSize, scaledBorderSize);
|
||||
videoView.RectTransform.NonScaledSize = scaledVideoResolution;
|
||||
|
||||
title.RectTransform.NonScaledSize += new Point(scaledTextWidth, scaledTitleHeight);
|
||||
title.RectTransform.NonScaledSize = new Point(scaledTextWidth, scaledTitleHeight);
|
||||
title.RectTransform.AbsoluteOffset = new Point((int)(5 * GUI.Scale), (int)(10 * GUI.Scale));
|
||||
|
||||
if (textSettings != null && !string.IsNullOrEmpty(textSettings.Text))
|
||||
@@ -189,7 +187,7 @@ namespace Barotrauma
|
||||
textSettings.Text = ToolBox.WrapText(textSettings.Text, scaledTextWidth, GUI.Font);
|
||||
int wrappedHeight = textSettings.Text.Split('\n').Length * scaledTextHeight;
|
||||
|
||||
textFrame.RectTransform.NonScaledSize += new Point(scaledTextWidth + scaledBorderSize, wrappedHeight + scaledBorderSize + scaledButtonSize.Y + scaledTitleHeight);
|
||||
textFrame.RectTransform.NonScaledSize = new Point(scaledTextWidth + scaledBorderSize, wrappedHeight + scaledBorderSize + scaledButtonSize.Y + scaledTitleHeight);
|
||||
|
||||
if (useTextOnRightSide)
|
||||
{
|
||||
@@ -200,7 +198,7 @@ namespace Barotrauma
|
||||
textFrame.RectTransform.AbsoluteOffset = new Point(0, scaledVideoResolution.Y + scaledBorderSize * 2);
|
||||
}
|
||||
|
||||
textContent.RectTransform.NonScaledSize += new Point(scaledTextWidth, wrappedHeight);
|
||||
textContent.RectTransform.NonScaledSize = new Point(scaledTextWidth, wrappedHeight);
|
||||
textContent.RectTransform.AbsoluteOffset = new Point(0, scaledBorderSize + scaledTitleHeight);
|
||||
}
|
||||
|
||||
@@ -212,7 +210,7 @@ namespace Barotrauma
|
||||
objectiveText.RectTransform.AbsoluteOffset = new Point(scaledXOffset, textContent.RectTransform.Rect.Height + objectiveTitle.Rect.Height + (int)(scaledTextHeight * 2.25f));
|
||||
|
||||
textFrame.RectTransform.NonScaledSize += new Point(0, scaledObjectiveFrameHeight);
|
||||
objectiveText.RectTransform.NonScaledSize += new Point(textFrame.Rect.Width, scaledTextHeight);
|
||||
objectiveText.RectTransform.NonScaledSize = new Point(textFrame.Rect.Width, scaledTextHeight);
|
||||
objectiveTitle.Visible = objectiveText.Visible = true;
|
||||
}
|
||||
else
|
||||
@@ -258,13 +256,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private Video CreateVideo(Point resolution)
|
||||
private Video CreateVideo()
|
||||
{
|
||||
Video video = null;
|
||||
|
||||
try
|
||||
{
|
||||
video = new Video(GameMain.Instance.GraphicsDevice, GameMain.SoundManager, filePath, (uint)resolution.X, (uint)resolution.Y);
|
||||
video = Video.Load(GameMain.Instance.GraphicsDevice, GameMain.SoundManager, filePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -41,7 +41,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public bool isFilled;
|
||||
public int inputAreaMargin;
|
||||
public Color color = Color.Red;
|
||||
public Color color = GUI.Style.Red;
|
||||
public Color? secondaryColor;
|
||||
public Color textColor = Color.White;
|
||||
public Color textBackgroundColor = Color.Black * 0.5f;
|
||||
@@ -65,7 +65,7 @@ namespace Barotrauma
|
||||
public object data;
|
||||
|
||||
public bool IsSelected => enabled && selectedWidgets.Contains(this);
|
||||
public bool IsControlled => IsSelected && PlayerInput.LeftButtonHeld();
|
||||
public bool IsControlled => IsSelected && PlayerInput.PrimaryMouseButtonHeld();
|
||||
public bool IsMouseOver => GUI.MouseOn == null && InputRect.Contains(PlayerInput.MousePosition);
|
||||
private bool enabled = true;
|
||||
public bool Enabled
|
||||
@@ -111,10 +111,10 @@ namespace Barotrauma
|
||||
{
|
||||
PreUpdate?.Invoke(deltaTime);
|
||||
if (!enabled) { return; }
|
||||
if (IsMouseOver || (!RequireMouseOn && selectedWidgets.Contains(this) && PlayerInput.LeftButtonHeld()))
|
||||
if (IsMouseOver || (!RequireMouseOn && selectedWidgets.Contains(this) && PlayerInput.PrimaryMouseButtonHeld()))
|
||||
{
|
||||
Hovered?.Invoke();
|
||||
if (RequireMouseOn || PlayerInput.LeftButtonDown())
|
||||
if (RequireMouseOn || PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
if ((multiselect && !selectedWidgets.Contains(this)) || selectedWidgets.None())
|
||||
{
|
||||
@@ -130,15 +130,15 @@ namespace Barotrauma
|
||||
}
|
||||
if (IsSelected)
|
||||
{
|
||||
if (PlayerInput.LeftButtonDown())
|
||||
if (PlayerInput.PrimaryMouseButtonDown())
|
||||
{
|
||||
MouseDown?.Invoke();
|
||||
}
|
||||
if (PlayerInput.LeftButtonHeld())
|
||||
if (PlayerInput.PrimaryMouseButtonHeld())
|
||||
{
|
||||
MouseHeld?.Invoke(deltaTime);
|
||||
}
|
||||
if (PlayerInput.LeftButtonClicked())
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
MouseUp?.Invoke();
|
||||
}
|
||||
1176
Barotrauma/BarotraumaClient/ClientSource/GameMain.cs
Normal file
1176
Barotrauma/BarotraumaClient/ClientSource/GameMain.cs
Normal file
File diff suppressed because it is too large
Load Diff
2530
Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs
Normal file
2530
Barotrauma/BarotraumaClient/ClientSource/GameSession/CrewManager.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,248 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class MultiPlayerCampaign : CampaignMode
|
||||
{
|
||||
public bool SuppressStateSending = false;
|
||||
|
||||
private UInt16 startWatchmanID, endWatchmanID;
|
||||
|
||||
public static void StartCampaignSetup(IEnumerable<string> saveFiles)
|
||||
{
|
||||
var parent = GameMain.NetLobbyScreen.CampaignSetupFrame;
|
||||
parent.ClearChildren();
|
||||
parent.Visible = true;
|
||||
GameMain.NetLobbyScreen.HighlightMode(2);
|
||||
|
||||
var layout = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true
|
||||
};
|
||||
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.07f), layout.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.1f) }, isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.02f
|
||||
};
|
||||
|
||||
var campaignContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.9f), layout.RectTransform, Anchor.BottomLeft), style: "InnerFrame")
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
var newCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
|
||||
var loadCampaignContainer = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.95f), campaignContainer.RectTransform, Anchor.Center), style: null);
|
||||
|
||||
var campaignSetupUI = new CampaignSetupUI(true, newCampaignContainer, loadCampaignContainer, null, saveFiles);
|
||||
|
||||
var newCampaignButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("NewCampaign"), style: "GUITabButton")
|
||||
{
|
||||
Selected = true
|
||||
};
|
||||
|
||||
var loadCampaignButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1.00f), buttonContainer.RectTransform),
|
||||
TextManager.Get("LoadCampaign"), style: "GUITabButton");
|
||||
|
||||
newCampaignButton.OnClicked = (btn, obj) =>
|
||||
{
|
||||
newCampaignButton.Selected = true;
|
||||
loadCampaignButton.Selected = false;
|
||||
newCampaignContainer.Visible = true;
|
||||
loadCampaignContainer.Visible = false;
|
||||
return true;
|
||||
};
|
||||
loadCampaignButton.OnClicked = (btn, obj) =>
|
||||
{
|
||||
newCampaignButton.Selected = false;
|
||||
loadCampaignButton.Selected = true;
|
||||
newCampaignContainer.Visible = false;
|
||||
loadCampaignContainer.Visible = true;
|
||||
return true;
|
||||
};
|
||||
loadCampaignContainer.Visible = false;
|
||||
|
||||
GUITextBlock.AutoScaleAndNormalize(newCampaignButton.TextBlock, loadCampaignButton.TextBlock);
|
||||
|
||||
campaignSetupUI.StartNewGame = GameMain.Client.SetupNewCampaign;
|
||||
campaignSetupUI.LoadGame = GameMain.Client.SetupLoadCampaign;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (startWatchmanID > 0 && startWatchman == null)
|
||||
{
|
||||
startWatchman = Entity.FindEntityByID(startWatchmanID) as Character;
|
||||
if (startWatchman != null) { InitializeWatchman(startWatchman); }
|
||||
}
|
||||
if (endWatchmanID > 0 && endWatchman == null)
|
||||
{
|
||||
endWatchman = Entity.FindEntityByID(endWatchmanID) as Character;
|
||||
if (endWatchman != null) { InitializeWatchman(endWatchman); }
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WatchmanInteract(Character watchman, Character interactor)
|
||||
{
|
||||
if ((watchman.Submarine == Level.Loaded.StartOutpost && !Submarine.MainSub.AtStartPosition) ||
|
||||
(watchman.Submarine == Level.Loaded.EndOutpost && !Submarine.MainSub.AtEndPosition))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GUIMessageBox.MessageBoxes.Any(mbox => mbox.UserData as string == "watchmanprompt"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.Client != null && interactor == Character.Controlled)
|
||||
{
|
||||
var msgBox = new GUIMessageBox("", TextManager.GetWithVariable("CampaignEnterOutpostPrompt", "[locationname]",
|
||||
Submarine.MainSub.AtStartPosition ? Map.CurrentLocation.Name : Map.SelectedLocation.Name),
|
||||
new string[] { TextManager.Get("Yes"), TextManager.Get("No") })
|
||||
{
|
||||
UserData = "watchmanprompt"
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked = (btn, userdata) =>
|
||||
{
|
||||
GameMain.Client.RequestRoundEnd();
|
||||
return true;
|
||||
};
|
||||
msgBox.Buttons[0].OnClicked += msgBox.Close;
|
||||
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(map.Locations.Count < UInt16.MaxValue);
|
||||
|
||||
msg.Write(map.SelectedLocationIndex == -1 ? UInt16.MaxValue : (UInt16)map.SelectedLocationIndex);
|
||||
msg.Write(map.SelectedMissionIndex == -1 ? byte.MaxValue : (byte)map.SelectedMissionIndex);
|
||||
msg.Write(PurchasedHullRepairs);
|
||||
msg.Write(PurchasedItemRepairs);
|
||||
msg.Write(PurchasedLostShuttles);
|
||||
|
||||
msg.Write((UInt16)CargoManager.PurchasedItems.Count);
|
||||
foreach (PurchasedItem pi in CargoManager.PurchasedItems)
|
||||
{
|
||||
msg.Write(pi.ItemPrefab.Identifier);
|
||||
msg.Write((UInt16)pi.Quantity);
|
||||
}
|
||||
}
|
||||
|
||||
//static because we may need to instantiate the campaign if it hasn't been done yet
|
||||
public static void ClientRead(IReadMessage msg)
|
||||
{
|
||||
byte campaignID = msg.ReadByte();
|
||||
UInt16 updateID = msg.ReadUInt16();
|
||||
UInt16 saveID = msg.ReadUInt16();
|
||||
string mapSeed = msg.ReadString();
|
||||
UInt16 currentLocIndex = msg.ReadUInt16();
|
||||
UInt16 selectedLocIndex = msg.ReadUInt16();
|
||||
byte selectedMissionIndex = msg.ReadByte();
|
||||
|
||||
UInt16 startWatchmanID = msg.ReadUInt16();
|
||||
UInt16 endWatchmanID = msg.ReadUInt16();
|
||||
|
||||
int money = msg.ReadInt32();
|
||||
bool purchasedHullRepairs = msg.ReadBoolean();
|
||||
bool purchasedItemRepairs = msg.ReadBoolean();
|
||||
bool purchasedLostShuttles = msg.ReadBoolean();
|
||||
|
||||
UInt16 purchasedItemCount = msg.ReadUInt16();
|
||||
List<PurchasedItem> purchasedItems = new List<PurchasedItem>();
|
||||
for (int i = 0; i < purchasedItemCount; i++)
|
||||
{
|
||||
string itemPrefabIdentifier = msg.ReadString();
|
||||
UInt16 itemQuantity = msg.ReadUInt16();
|
||||
purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity));
|
||||
}
|
||||
|
||||
bool hasCharacterData = msg.ReadBoolean();
|
||||
CharacterInfo myCharacterInfo = null;
|
||||
if (hasCharacterData)
|
||||
{
|
||||
myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg);
|
||||
}
|
||||
|
||||
MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
|
||||
if (campaign == null || campaignID != campaign.CampaignID)
|
||||
{
|
||||
string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer);
|
||||
|
||||
GameMain.GameSession = new GameSession(null, savePath,
|
||||
GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign"));
|
||||
|
||||
campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode);
|
||||
campaign.CampaignID = campaignID;
|
||||
campaign.GenerateMap(mapSeed);
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
}
|
||||
|
||||
|
||||
//server has a newer save file
|
||||
if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID))
|
||||
{
|
||||
/*//stop any active campaign save transfers, they're outdated now
|
||||
List<FileReceiver.FileTransferIn> saveTransfers =
|
||||
GameMain.Client.FileReceiver.ActiveTransfers.FindAll(t => t.FileType == FileTransferType.CampaignSave);
|
||||
|
||||
foreach (var transfer in saveTransfers)
|
||||
{
|
||||
GameMain.Client.FileReceiver.StopTransfer(transfer);
|
||||
}
|
||||
|
||||
GameMain.Client.RequestFile(FileTransferType.CampaignSave, null, null);*/
|
||||
campaign.PendingSaveID = saveID;
|
||||
}
|
||||
|
||||
if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID))
|
||||
{
|
||||
campaign.SuppressStateSending = true;
|
||||
|
||||
//we need to have the latest save file to display location/mission/store
|
||||
if (campaign.LastSaveID == saveID)
|
||||
{
|
||||
campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex);
|
||||
campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex);
|
||||
campaign.Map.SelectMission(selectedMissionIndex);
|
||||
campaign.CargoManager.SetPurchasedItems(purchasedItems);
|
||||
}
|
||||
|
||||
campaign.startWatchmanID = startWatchmanID;
|
||||
campaign.endWatchmanID = endWatchmanID;
|
||||
|
||||
campaign.Money = money;
|
||||
campaign.PurchasedHullRepairs = purchasedHullRepairs;
|
||||
campaign.PurchasedItemRepairs = purchasedItemRepairs;
|
||||
campaign.PurchasedLostShuttles = purchasedLostShuttles;
|
||||
|
||||
if (myCharacterInfo != null)
|
||||
{
|
||||
GameMain.Client.CharacterInfo = myCharacterInfo;
|
||||
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null);
|
||||
}
|
||||
|
||||
campaign.lastUpdateID = updateID;
|
||||
campaign.SuppressStateSending = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
{
|
||||
//do nothing, the clients get the save files from the server
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,11 +32,12 @@ namespace Barotrauma
|
||||
OnClicked = (btn, userdata) => { TryEndRound(GetLeavingSub()); return true; }
|
||||
};
|
||||
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.List.Values)
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
|
||||
{
|
||||
for (int i = 0; i < jobPrefab.InitialCount; i++)
|
||||
{
|
||||
CrewManager.AddCharacterInfo(new CharacterInfo(Character.HumanSpeciesName, "", jobPrefab));
|
||||
var variant = Rand.Range(0, jobPrefab.Variants);
|
||||
CrewManager.AddCharacterInfo(new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: jobPrefab, variant: variant));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +91,7 @@ namespace Barotrauma
|
||||
if (Character.Controlled.Submarine != outpost) { return null; }
|
||||
|
||||
//if there's a sub docked to the outpost, we can leave the level
|
||||
if (outpost.DockedTo.Count > 0)
|
||||
if (outpost.DockedTo.Any())
|
||||
{
|
||||
var dockedSub = outpost.DockedTo.FirstOrDefault();
|
||||
return dockedSub.DockedTo.Contains(Submarine.MainSub) ? Submarine.MainSub : dockedSub;
|
||||
@@ -292,7 +293,7 @@ namespace Barotrauma
|
||||
|
||||
summaryScreen.RemoveChild(summaryScreen.Children.FirstOrDefault(c => c is GUIButton));
|
||||
|
||||
var okButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonArea.RectTransform),
|
||||
var okButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonArea.RectTransform),
|
||||
TextManager.Get("LoadGameButton"))
|
||||
{
|
||||
OnClicked = (GUIButton button, object obj) =>
|
||||
@@ -304,7 +305,7 @@ namespace Barotrauma
|
||||
}
|
||||
};
|
||||
|
||||
var quitButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonArea.RectTransform),
|
||||
var quitButton = new GUIButton(new RectTransform(new Vector2(0.25f, 1.0f), buttonArea.RectTransform),
|
||||
TextManager.Get("QuitButton"));
|
||||
quitButton.OnClicked += GameMain.LobbyScreen.QuitToMainMenu;
|
||||
quitButton.OnClicked += (GUIButton button, object obj) =>
|
||||
@@ -401,14 +402,17 @@ namespace Barotrauma
|
||||
|
||||
campaign.Money = element.GetAttributeInt("money", 0);
|
||||
campaign.CheatsEnabled = element.GetAttributeBool("cheatsenabled", false);
|
||||
campaign.InitialSuppliesSpawned = element.GetAttributeBool("initialsuppliesspawned", false);
|
||||
if (campaign.CheatsEnabled)
|
||||
{
|
||||
DebugConsole.CheatsEnabled = true;
|
||||
if (Steam.SteamManager.USE_STEAM && !SteamAchievementManager.CheatsEnabled)
|
||||
#if USE_STEAM
|
||||
if (!SteamAchievementManager.CheatsEnabled)
|
||||
{
|
||||
SteamAchievementManager.CheatsEnabled = true;
|
||||
new GUIMessageBox("Cheats enabled", "Cheat commands have been enabled on the campaign. You will not receive Steam Achievements until you restart the game.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//backwards compatibility with older save files
|
||||
@@ -429,7 +433,8 @@ namespace Barotrauma
|
||||
XElement modeElement = new XElement("SinglePlayerCampaign",
|
||||
// Refunds the money when save & quitting from the map if there are items selected in the store
|
||||
new XAttribute("money", Money + (CargoManager != null ? CargoManager.GetTotalItemCost() : 0)),
|
||||
new XAttribute("cheatsenabled", CheatsEnabled));
|
||||
new XAttribute("cheatsenabled", CheatsEnabled),
|
||||
new XAttribute("initialsuppliesspawned", InitialSuppliesSpawned));
|
||||
CrewManager.Save(modeElement);
|
||||
Map.Save(modeElement);
|
||||
element.Add(modeElement);
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
@@ -56,17 +57,30 @@ namespace Barotrauma.Tutorials
|
||||
radioSpeakerName = TextManager.Get("Tutorial.Radio.Watchman");
|
||||
GameMain.GameSession.CrewManager.AllowCharacterSwitch = false;
|
||||
|
||||
var revolver = captain.Inventory.FindItemByIdentifier("revolver");
|
||||
var revolver = FindOrGiveItem(captain, "revolver");
|
||||
revolver.Unequip(captain);
|
||||
captain.Inventory.RemoveItem(revolver);
|
||||
|
||||
var captainscap = captain.Inventory.FindItemByIdentifier("captainscap");
|
||||
captainscap.Unequip(captain);
|
||||
captain.Inventory.RemoveItem(captainscap);
|
||||
var captainscap =
|
||||
captain.Inventory.FindItemByIdentifier("captainscap1") ??
|
||||
captain.Inventory.FindItemByIdentifier("captainscap2") ??
|
||||
captain.Inventory.FindItemByIdentifier("captainscap3");
|
||||
|
||||
var captainsuniform = captain.Inventory.FindItemByIdentifier("captainsuniform");
|
||||
captainsuniform.Unequip(captain);
|
||||
captain.Inventory.RemoveItem(captainsuniform);
|
||||
if (captainscap != null)
|
||||
{
|
||||
captainscap.Unequip(captain);
|
||||
captain.Inventory.RemoveItem(captainscap);
|
||||
}
|
||||
|
||||
var captainsuniform =
|
||||
captain.Inventory.FindItemByIdentifier("captainsuniform1") ??
|
||||
captain.Inventory.FindItemByIdentifier("captainsuniform2") ??
|
||||
captain.Inventory.FindItemByIdentifier("captainsuniform3");
|
||||
if (captainsuniform != null)
|
||||
{
|
||||
captainsuniform.Unequip(captain);
|
||||
captain.Inventory.RemoveItem(captainsuniform);
|
||||
}
|
||||
|
||||
var steerOrder = Order.GetPrefab("steer");
|
||||
captain_steerIcon = steerOrder.SymbolSprite;
|
||||
@@ -85,7 +99,7 @@ namespace Barotrauma.Tutorials
|
||||
captain_medicSpawnPos = Item.ItemList.Find(i => i.HasTag("captain_medicspawnpos")).WorldPosition;
|
||||
tutorial_submarineDoor = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoor")).GetComponent<Door>();
|
||||
tutorial_submarineDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_submarinedoorlight")).GetComponent<LightComponent>();
|
||||
var medicInfo = new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("medicaldoctor"));
|
||||
var medicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("medicaldoctor"));
|
||||
captain_medic = Character.Create(medicInfo, captain_medicSpawnPos, "medicaldoctor");
|
||||
captain_medic.GiveJobItems(null);
|
||||
captain_medic.CanSpeak = captain_medic.AIController.Enabled = false;
|
||||
@@ -107,15 +121,15 @@ namespace Barotrauma.Tutorials
|
||||
SetDoorAccess(tutorial_lockedDoor_1, null, false);
|
||||
SetDoorAccess(tutorial_lockedDoor_2, null, false);
|
||||
|
||||
var mechanicInfo = new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("mechanic"));
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("mechanic"));
|
||||
captain_mechanic = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "mechanic");
|
||||
captain_mechanic.GiveJobItems();
|
||||
|
||||
var securityInfo = new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
|
||||
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
|
||||
captain_security = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "securityofficer");
|
||||
captain_security.GiveJobItems();
|
||||
|
||||
var engineerInfo = new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
captain_engineer = Character.Create(engineerInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "engineer");
|
||||
captain_engineer.GiveJobItems();
|
||||
|
||||
@@ -143,13 +157,14 @@ namespace Barotrauma.Tutorials
|
||||
do { yield return null; } while (!captain_medicObjectiveSensor.MotionDetected);
|
||||
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(captain_medic.Info.DisplayName, TextManager.Get("Captain.Radio.Medic"), ChatMessageType.Radio, null);
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
GameMain.GameSession.CrewManager.ToggleCrewAreaOpen = true;
|
||||
GameMain.GameSession.CrewManager.ToggleCrewListOpen = true;
|
||||
GameMain.GameSession.CrewManager.AddCharacter(captain_medic);
|
||||
TriggerTutorialSegment(0);
|
||||
TriggerTutorialSegment(0, GameMain.Config.KeyBindText(InputType.Command));
|
||||
do
|
||||
{
|
||||
yield return null;
|
||||
GameMain.GameSession.CrewManager.HighlightOrderButton(captain_medic, "follow", highlightColor, new Vector2(5, 5));
|
||||
// TODO: Rework order highlighting for new command UI
|
||||
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_medic, "follow", highlightColor, new Vector2(5, 5));
|
||||
}
|
||||
while (!HasOrder(captain_medic, "follow"));
|
||||
SetDoorAccess(tutorial_submarineDoor, tutorial_submarineDoorLight, true);
|
||||
@@ -159,34 +174,37 @@ namespace Barotrauma.Tutorials
|
||||
do { yield return null; } while (!captain_enteredSubmarineSensor.MotionDetected);
|
||||
yield return new WaitForSeconds(3f, false);
|
||||
captain_mechanic.AIController.Enabled = captain_security.AIController.Enabled = captain_engineer.AIController.Enabled = true;
|
||||
TriggerTutorialSegment(1);
|
||||
TriggerTutorialSegment(1, GameMain.Config.KeyBindText(InputType.Command));
|
||||
GameMain.GameSession.CrewManager.AddCharacter(captain_mechanic);
|
||||
do
|
||||
{
|
||||
yield return null;
|
||||
GameMain.GameSession.CrewManager.HighlightOrderButton(captain_mechanic, "repairsystems", highlightColor, new Vector2(5, 5));
|
||||
// TODO: Rework order highlighting for new command UI
|
||||
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_mechanic, "repairsystems", highlightColor, new Vector2(5, 5));
|
||||
//HighlightOrderOption("jobspecific");
|
||||
}
|
||||
while (!HasOrder(captain_mechanic, "repairsystems"));
|
||||
RemoveCompletedObjective(segments[1]);
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
TriggerTutorialSegment(2);
|
||||
TriggerTutorialSegment(2, GameMain.Config.KeyBindText(InputType.Command));
|
||||
GameMain.GameSession.CrewManager.AddCharacter(captain_security);
|
||||
do
|
||||
{
|
||||
yield return null;
|
||||
GameMain.GameSession.CrewManager.HighlightOrderButton(captain_security, "operateweapons", highlightColor, new Vector2(5, 5));
|
||||
// TODO: Rework order highlighting for new command UI
|
||||
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_security, "operateweapons", highlightColor, new Vector2(5, 5));
|
||||
HighlightOrderOption("fireatwill");
|
||||
}
|
||||
while (!HasOrder(captain_security, "operateweapons", "fireatwill"));
|
||||
RemoveCompletedObjective(segments[2]);
|
||||
yield return new WaitForSeconds(4f, false);
|
||||
TriggerTutorialSegment(3);
|
||||
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command));
|
||||
GameMain.GameSession.CrewManager.AddCharacter(captain_engineer);
|
||||
do
|
||||
{
|
||||
yield return null;
|
||||
GameMain.GameSession.CrewManager.HighlightOrderButton(captain_engineer, "operatereactor", highlightColor, new Vector2(5, 5));
|
||||
// TODO: Rework order highlighting for new command UI
|
||||
// GameMain.GameSession.CrewManager.HighlightOrderButton(captain_engineer, "operatereactor", highlightColor, new Vector2(5, 5));
|
||||
HighlightOrderOption("powerup");
|
||||
}
|
||||
while (!HasOrder(captain_engineer, "operatereactor", "powerup"));
|
||||
@@ -204,7 +222,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
//captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f);
|
||||
yield return new WaitForSeconds(1.0f, false);
|
||||
} while (Submarine.MainSub.DockedTo.Count > 0);
|
||||
} while (Submarine.MainSub.DockedTo.Any());
|
||||
RemoveCompletedObjective(segments[4]);
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
TriggerTutorialSegment(5); // Navigate to destination
|
||||
@@ -212,10 +230,9 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
if (IsSelectedItem(captain_navConsole.Item))
|
||||
{
|
||||
if (captain_sonar.ActiveTickBox.Box.FlashTimer <= 0)
|
||||
if (captain_sonar.SonarModeSwitch.Frame.FlashTimer <= 0)
|
||||
{
|
||||
captain_sonar.ActiveTickBox.Box.Flash(highlightColor, 1.5f, false, new Vector2(2.5f, 2.5f));
|
||||
//captain_sonar.ActiveTickBox.Box.Pulsate(Vector2.One, Vector2.One * 1.5f, 1.5f);
|
||||
captain_sonar.SonarModeSwitch.Frame.Flash(highlightColor, 1.5f, false, false, new Vector2(2.5f, 2.5f));
|
||||
}
|
||||
}
|
||||
yield return null;
|
||||
@@ -229,7 +246,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
//captain_navConsoleCustomInterface.HighlightElement(0, uiHighlightColor, duration: 1.0f, pulsateAmount: 0.0f);
|
||||
yield return new WaitForSeconds(1.0f, false);
|
||||
} while (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0);
|
||||
} while (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Any());
|
||||
RemoveCompletedObjective(segments[6]);
|
||||
yield return new WaitForSeconds(3f, false);
|
||||
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.GetWithVariable("Captain.Radio.Complete", "[OUTPOSTNAME]", GameMain.GameSession.EndLocation.Name), ChatMessageType.Radio, null);
|
||||
@@ -22,7 +22,6 @@ namespace Barotrauma.Tutorials
|
||||
private ItemContainer doctor_medBayCabinet;
|
||||
private Character patient1, patient2;
|
||||
private List<Character> subPatients;
|
||||
private Hull startRoom;
|
||||
private Hull medBay;
|
||||
|
||||
private Door doctor_firstDoor;
|
||||
@@ -56,6 +55,22 @@ namespace Barotrauma.Tutorials
|
||||
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
|
||||
doctor = Character.Controlled;
|
||||
|
||||
var bandages = FindOrGiveItem(doctor, "antibleeding1");
|
||||
bandages.Unequip(doctor);
|
||||
doctor.Inventory.RemoveItem(bandages);
|
||||
|
||||
var syringegun = FindOrGiveItem(doctor, "syringegun");
|
||||
syringegun.Unequip(doctor);
|
||||
doctor.Inventory.RemoveItem(syringegun);
|
||||
|
||||
var antibiotics = FindOrGiveItem(doctor, "antibiotics");
|
||||
antibiotics.Unequip(doctor);
|
||||
doctor.Inventory.RemoveItem(antibiotics);
|
||||
|
||||
var morphine = FindOrGiveItem(doctor, "antidama1");
|
||||
morphine.Unequip(doctor);
|
||||
doctor.Inventory.RemoveItem(morphine);
|
||||
|
||||
doctor_suppliesCabinet = Item.ItemList.Find(i => i.HasTag("doctor_suppliescabinet"))?.GetComponent<ItemContainer>();
|
||||
doctor_medBayCabinet = Item.ItemList.Find(i => i.HasTag("doctor_medbaycabinet"))?.GetComponent<ItemContainer>();
|
||||
|
||||
@@ -63,30 +78,30 @@ namespace Barotrauma.Tutorials
|
||||
var patientHull2 = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "airlock").CurrentHull;
|
||||
medBay = WayPoint.WayPointList.Find(wp => wp.IdCardDesc == "medbay").CurrentHull;
|
||||
|
||||
var assistantInfo = new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("assistant"));
|
||||
var assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
|
||||
patient1 = Character.Create(assistantInfo, patientHull1.WorldPosition, "1");
|
||||
patient1.GiveJobItems(null);
|
||||
patient1.CanSpeak = false;
|
||||
patient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 45.0f) }, stun: 0, playSound: false);
|
||||
patient1.AIController.Enabled = false;
|
||||
|
||||
assistantInfo = new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("assistant"));
|
||||
assistantInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("assistant"));
|
||||
patient2 = Character.Create(assistantInfo, patientHull2.WorldPosition, "2");
|
||||
patient2.GiveJobItems(null);
|
||||
patient2.CanSpeak = false;
|
||||
patient2.AIController.Enabled = false;
|
||||
|
||||
var mechanicInfo = new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var mechanicInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var subPatient1 = Character.Create(mechanicInfo, WayPoint.GetRandom(SpawnType.Human, mechanicInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient1.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 40.0f) }, stun: 0, playSound: false);
|
||||
subPatients.Add(subPatient1);
|
||||
|
||||
var securityInfo = new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
|
||||
var securityInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("securityofficer"));
|
||||
var subPatient2 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, securityInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient2.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.InternalDamage, 40.0f) }, stun: 0, playSound: false);
|
||||
subPatients.Add(subPatient2);
|
||||
|
||||
var engineerInfo = new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var engineerInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer"));
|
||||
var subPatient3 = Character.Create(securityInfo, WayPoint.GetRandom(SpawnType.Human, engineerInfo.Job, Submarine.MainSub).WorldPosition, "3");
|
||||
subPatient3.AddDamage(patient1.WorldPosition, new List<Affliction>() { new Affliction(AfflictionPrefab.Burn, 20.0f) }, stun: 0, playSound: false);
|
||||
subPatients.Add(subPatient3);
|
||||
@@ -176,7 +191,7 @@ namespace Barotrauma.Tutorials
|
||||
yield return new WaitForSeconds(2.0f);
|
||||
}*/
|
||||
|
||||
TriggerTutorialSegment(0, GameMain.Config.KeyBind(InputType.Select), GameMain.Config.KeyBind(InputType.Deselect)); // Medical supplies objective
|
||||
TriggerTutorialSegment(0, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Deselect), GameMain.Config.KeyBindText(InputType.ToggleInventory)); // Medical supplies objective
|
||||
|
||||
do
|
||||
{
|
||||
@@ -205,7 +220,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
// 2nd tutorial segment, treat self -------------------------------------------------------------------------
|
||||
|
||||
TriggerTutorialSegment(1, GameMain.Config.KeyBind(InputType.Health)); // Open health interface
|
||||
TriggerTutorialSegment(1, GameMain.Config.KeyBindText(InputType.Health)); // Open health interface
|
||||
while (CharacterHealth.OpenHealthWindow == null)
|
||||
{
|
||||
doctor.CharacterHealth.HealthBarPulsateTimer = 1.0f;
|
||||
@@ -255,17 +270,18 @@ namespace Barotrauma.Tutorials
|
||||
GameMain.GameSession.CrewManager.AllowCharacterSwitch = false;
|
||||
GameMain.GameSession.CrewManager.AddCharacter(doctor);
|
||||
GameMain.GameSession.CrewManager.AddCharacter(patient1);
|
||||
GameMain.GameSession.CrewManager.ToggleCrewAreaOpen = true;
|
||||
GameMain.GameSession.CrewManager.ToggleCrewListOpen = true;
|
||||
patient1.CharacterHealth.UseHealthWindow = false;
|
||||
|
||||
yield return new WaitForSeconds(3.0f, false);
|
||||
patient1.AIController.Enabled = true;
|
||||
doctor.RemoveActiveObjectiveEntity(patient1);
|
||||
TriggerTutorialSegment(3); // Get the patient to medbay
|
||||
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Command)); // Get the patient to medbay
|
||||
|
||||
while (patient1.CurrentOrder == null || patient1.CurrentOrder.Identifier != "follow")
|
||||
{
|
||||
GameMain.GameSession.CrewManager.HighlightOrderButton(patient1, "follow", highlightColor, new Vector2(5, 5));
|
||||
// TODO: Rework order highlighting for new command UI
|
||||
// GameMain.GameSession.CrewManager.HighlightOrderButton(patient1, "follow", highlightColor, new Vector2(5, 5));
|
||||
yield return null;
|
||||
}
|
||||
|
||||
@@ -282,7 +298,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
yield return new WaitForSeconds(2.0f, false);
|
||||
|
||||
TriggerTutorialSegment(4, GameMain.Config.KeyBind(InputType.Health)); // treat burns
|
||||
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Health)); // treat burns
|
||||
|
||||
do
|
||||
{
|
||||
@@ -344,14 +360,18 @@ namespace Barotrauma.Tutorials
|
||||
do { yield return null; } while (!tutorial_upperFinalDoor.IsOpen);
|
||||
yield return new WaitForSeconds(2.0f, false);
|
||||
|
||||
TriggerTutorialSegment(5, GameMain.Config.KeyBind(InputType.Health)); // perform CPR
|
||||
TriggerTutorialSegment(5, GameMain.Config.KeyBindText(InputType.Health)); // perform CPR
|
||||
SetHighlight(patient2, true);
|
||||
while (patient2.IsUnconscious)
|
||||
{
|
||||
if (CharacterHealth.OpenHealthWindow != null && doctor.AnimController.Anim != AnimController.Animation.CPR)
|
||||
{
|
||||
CharacterHealth.OpenHealthWindow.CPRButton.Pulsate(Vector2.One, Vector2.One * 1.5f, 1.0f);
|
||||
CharacterHealth.OpenHealthWindow.CPRButton.Flash();
|
||||
//Disabled pulse until it's replaced by a better effect
|
||||
//CharacterHealth.OpenHealthWindow.CPRButton.Pulsate(Vector2.One, Vector2.One * 1.5f, 1.0f);
|
||||
if (CharacterHealth.OpenHealthWindow.CPRButton.FlashTimer <= 0.0f)
|
||||
{
|
||||
CharacterHealth.OpenHealthWindow.CPRButton.Flash(highlightColor);
|
||||
}
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
@@ -374,7 +394,7 @@ namespace Barotrauma.Tutorials
|
||||
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Doctor.Radio.EnteredSub"), ChatMessageType.Radio, null);
|
||||
|
||||
yield return new WaitForSeconds(3.0f, false);
|
||||
TriggerTutorialSegment(6, GameMain.Config.KeyBind(InputType.Health)); // give treatment to anyone in need
|
||||
TriggerTutorialSegment(6, GameMain.Config.KeyBindText(InputType.Health)); // give treatment to anyone in need
|
||||
|
||||
foreach (var patient in subPatients)
|
||||
{
|
||||
@@ -87,7 +87,7 @@ namespace Barotrauma.Tutorials
|
||||
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
|
||||
engineer = Character.Controlled;
|
||||
|
||||
var toolbox = engineer.Inventory.FindItemByIdentifier("toolbox");
|
||||
var toolbox = FindOrGiveItem(engineer, "toolbox");
|
||||
toolbox.Unequip(engineer);
|
||||
engineer.Inventory.RemoveItem(toolbox);
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Barotrauma.Tutorials
|
||||
engineer_reactor = Item.ItemList.Find(i => i.HasTag("engineer_reactor")).GetComponent<Reactor>();
|
||||
engineer_reactor.FireDelay = engineer_reactor.MeltdownDelay = float.PositiveInfinity;
|
||||
engineer_reactor.FuelConsumptionRate = 0.0f;
|
||||
engineer_reactor.OnOffSwitch.BarScroll = 1f;
|
||||
engineer_reactor.PowerOn = true;
|
||||
reactorOperatedProperly = false;
|
||||
|
||||
engineer_secondDoor = Item.ItemList.Find(i => i.HasTag("engineer_seconddoor")).GetComponent<Door>(); ;
|
||||
@@ -195,7 +195,7 @@ namespace Barotrauma.Tutorials
|
||||
engineer_submarineJunctionBox_2 = Item.ItemList.Find(i => i.HasTag("engineer_submarinejunctionbox_2"));
|
||||
engineer_submarineJunctionBox_3 = Item.ItemList.Find(i => i.HasTag("engineer_submarinejunctionbox_3"));
|
||||
engineer_submarineReactor = Item.ItemList.Find(i => i.HasTag("engineer_submarinereactor")).GetComponent<Reactor>();
|
||||
engineer_submarineReactor.OnOffSwitch.BarScrollValue = .25f;
|
||||
engineer_submarineReactor.PowerOn = true;
|
||||
engineer_submarineReactor.IsActive = engineer_submarineReactor.AutoTemp = false;
|
||||
|
||||
engineer_submarineJunctionBox_1.Indestructible = false;
|
||||
@@ -235,7 +235,7 @@ namespace Barotrauma.Tutorials
|
||||
do { yield return null; } while (!engineer_equipmentObjectiveSensor.MotionDetected);
|
||||
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.Equipment"), ChatMessageType.Radio, null);
|
||||
yield return new WaitForSeconds(0.5f, false);
|
||||
TriggerTutorialSegment(0, GameMain.Config.KeyBind(InputType.Select), GameMain.Config.KeyBind(InputType.Deselect)); // Retrieve equipment
|
||||
TriggerTutorialSegment(0, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Deselect), GameMain.Config.KeyBindText(InputType.ToggleInventory)); // Retrieve equipment
|
||||
bool firstSlotRemoved = false;
|
||||
bool secondSlotRemoved = false;
|
||||
bool thirdSlotRemoved = false;
|
||||
@@ -289,19 +289,19 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
if (IsSelectedItem(engineer_reactor.Item))
|
||||
{
|
||||
engineer_reactor.AutoTempSlider.BarScrollValue = 1.0f;
|
||||
if (engineer_reactor.OnOffSwitch.FlashTimer <= 0)
|
||||
engineer_reactor.AutoTemp = false;
|
||||
if (engineer_reactor.PowerButton.FlashTimer <= 0)
|
||||
{
|
||||
engineer_reactor.OnOffSwitch.Flash(highlightColor, 1.5f, false);
|
||||
engineer_reactor.PowerButton.Flash(highlightColor, 1.5f, false);
|
||||
}
|
||||
}
|
||||
yield return null;
|
||||
} while (engineer_reactor.OnOffSwitch.BarScroll > 0.45f);
|
||||
} while (!engineer_reactor.PowerOn);
|
||||
do
|
||||
{
|
||||
if (IsSelectedItem(engineer_reactor.Item) && engineer_reactor.Item.OwnInventory.slots != null)
|
||||
{
|
||||
engineer_reactor.AutoTempSlider.BarScrollValue = 1.0f;
|
||||
engineer_reactor.AutoTemp = false;
|
||||
HighlightInventorySlot(engineer.Inventory, "fuelrod", highlightColor, 0.5f, 0.5f, 0f);
|
||||
|
||||
for (int i = 0; i < engineer_reactor.Item.OwnInventory.slots.Length; i++)
|
||||
@@ -316,7 +316,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
if (IsSelectedItem(engineer_reactor.Item))
|
||||
{
|
||||
engineer_reactor.AutoTempSlider.BarScrollValue = 1.0f;
|
||||
engineer_reactor.AutoTemp = false;
|
||||
if (engineer_reactor.FissionRateScrollBar.FlashTimer <= 0)
|
||||
{
|
||||
engineer_reactor.FissionRateScrollBar.Flash(highlightColor, 1.5f);
|
||||
@@ -335,9 +335,9 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
if (IsSelectedItem(engineer_reactor.Item))
|
||||
{
|
||||
if (engineer_reactor.AutoTempSlider.FlashTimer <= 0)
|
||||
if (engineer_reactor.AutoTempSwitch.FlashTimer <= 0)
|
||||
{
|
||||
engineer_reactor.AutoTempSlider.Flash(highlightColor, 1.5f, false, new Vector2(10, 10));
|
||||
engineer_reactor.AutoTempSwitch.Flash(highlightColor, 1.5f, false, false, new Vector2(10, 10));
|
||||
}
|
||||
}
|
||||
yield return null;
|
||||
@@ -348,7 +348,7 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
yield return new WaitForSeconds(0.1f, false);
|
||||
wait -= 0.1f;
|
||||
engineer_reactor.AutoTempSlider.BarScrollValue = 0.0f;
|
||||
engineer_reactor.AutoTemp = true;
|
||||
} while (wait > 0.0f);
|
||||
engineer.SelectedConstruction = null;
|
||||
engineer_reactor.CanBeSelected = false;
|
||||
@@ -361,7 +361,7 @@ namespace Barotrauma.Tutorials
|
||||
do { yield return null; } while (!engineer_secondDoor.IsOpen);
|
||||
yield return new WaitForSeconds(1f, false);
|
||||
Repairable repairableJunctionBoxComponent = engineer_brokenJunctionBox.GetComponent<Repairable>();
|
||||
TriggerTutorialSegment(2, GameMain.Config.KeyBind(InputType.Select)); // Repair the junction box
|
||||
TriggerTutorialSegment(2, GameMain.Config.KeyBindText(InputType.Select)); // Repair the junction box
|
||||
do
|
||||
{
|
||||
if (!engineer.HasEquippedItem("screwdriver"))
|
||||
@@ -389,7 +389,7 @@ namespace Barotrauma.Tutorials
|
||||
do { yield return null; } while (!engineer_thirdDoor.IsOpen);
|
||||
GameMain.GameSession.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Engineer.Radio.FaultyWiring"), ChatMessageType.Radio, null);
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
TriggerTutorialSegment(3, GameMain.Config.KeyBind(InputType.Use), GameMain.Config.KeyBind(InputType.Deselect)); // Connect the junction boxes
|
||||
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Use), GameMain.Config.KeyBindText(InputType.Deselect)); // Connect the junction boxes
|
||||
do { CheckGhostWires(); HandleJunctionBoxWiringHighlights(); yield return null; } while (engineer_workingPump.Voltage < engineer_workingPump.MinVoltage); // Wait until connected all the way to the pump
|
||||
CheckGhostWires();
|
||||
for (int i = 0; i < engineer_disconnectedJunctionBoxes.Length; i++)
|
||||
@@ -87,18 +87,18 @@ namespace Barotrauma.Tutorials
|
||||
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
|
||||
mechanic = Character.Controlled;
|
||||
|
||||
var toolbox = mechanic.Inventory.FindItemByIdentifier("toolbox");
|
||||
var toolbox = FindOrGiveItem(mechanic, "toolbox");
|
||||
toolbox.Unequip(mechanic);
|
||||
mechanic.Inventory.RemoveItem(toolbox);
|
||||
|
||||
var crowbar = mechanic.Inventory.FindItemByIdentifier("crowbar");
|
||||
var crowbar = FindOrGiveItem(mechanic, "crowbar");
|
||||
crowbar.Unequip(mechanic);
|
||||
mechanic.Inventory.RemoveItem(crowbar);
|
||||
|
||||
var repairOrder = Order.GetPrefab("repairsystems");
|
||||
mechanic_repairIcon = repairOrder.SymbolSprite;
|
||||
mechanic_repairIconColor = repairOrder.Color;
|
||||
mechanic_weldIcon = new Sprite("Content/UI/IconAtlas.png", new Rectangle(1, 256, 127, 127), new Vector2(0.5f, 0.5f));
|
||||
mechanic_weldIcon = new Sprite("Content/UI/MainIconsAtlas.png", new Rectangle(1, 256, 127, 127), new Vector2(0.5f, 0.5f));
|
||||
|
||||
// Other tutorial items
|
||||
tutorial_securityFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_securityfinaldoorlight")).GetComponent<LightComponent>();
|
||||
@@ -241,7 +241,7 @@ namespace Barotrauma.Tutorials
|
||||
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.WakeUp"), ChatMessageType.Radio, null);
|
||||
|
||||
yield return new WaitForSeconds(2.5f, false);
|
||||
TriggerTutorialSegment(0, GameMain.Config.KeyBind(InputType.Up), GameMain.Config.KeyBind(InputType.Left), GameMain.Config.KeyBind(InputType.Down), GameMain.Config.KeyBind(InputType.Right), GameMain.Config.KeyBind(InputType.Select), GameMain.Config.KeyBind(InputType.Select)); // Open door objective
|
||||
TriggerTutorialSegment(0, GameMain.Config.KeyBindText(InputType.Up), GameMain.Config.KeyBindText(InputType.Left), GameMain.Config.KeyBindText(InputType.Down), GameMain.Config.KeyBindText(InputType.Right), GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Select)); // Open door objective
|
||||
yield return new WaitForSeconds(0.0f, false);
|
||||
SetDoorAccess(mechanic_firstDoor, mechanic_firstDoorLight, true);
|
||||
SetHighlight(mechanic_firstDoor.Item, true);
|
||||
@@ -254,7 +254,7 @@ namespace Barotrauma.Tutorials
|
||||
yield return new WaitForSeconds(0.0f, false);
|
||||
GameMain.GameSession?.CrewManager.AddSinglePlayerChatMessage(radioSpeakerName, TextManager.Get("Mechanic.Radio.Equipment"), ChatMessageType.Radio, null);
|
||||
do { yield return null; } while (!mechanic_equipmentObjectiveSensor.MotionDetected);
|
||||
TriggerTutorialSegment(1, GameMain.Config.KeyBind(InputType.Select), GameMain.Config.KeyBind(InputType.Deselect)); // Equipment & inventory objective
|
||||
TriggerTutorialSegment(1, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Deselect), GameMain.Config.KeyBindText(InputType.ToggleInventory)); // Equipment & inventory objective
|
||||
SetHighlight(mechanic_equipmentCabinet.Item, true);
|
||||
bool firstSlotRemoved = false;
|
||||
bool secondSlotRemoved = false;
|
||||
@@ -296,7 +296,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
// Room 3
|
||||
do { yield return null; } while (!mechanic_weldingObjectiveSensor.MotionDetected);
|
||||
TriggerTutorialSegment(2, GameMain.Config.KeyBind(InputType.Aim), GameMain.Config.KeyBind(InputType.Shoot)); // Welding objective
|
||||
TriggerTutorialSegment(2, GameMain.Config.KeyBindText(InputType.Aim), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.ToggleInventory)); // Welding objective
|
||||
do
|
||||
{
|
||||
if (!mechanic.HasEquippedItem("divingmask"))
|
||||
@@ -316,16 +316,16 @@ namespace Barotrauma.Tutorials
|
||||
mechanic.RemoveActiveObjectiveEntity(mechanic_brokenWall_1);
|
||||
RemoveCompletedObjective(segments[2]);
|
||||
yield return new WaitForSeconds(1f, false);
|
||||
TriggerTutorialSegment(3, GameMain.Config.KeyBind(InputType.Select)); // Pump objective
|
||||
TriggerTutorialSegment(3, GameMain.Config.KeyBindText(InputType.Select)); // Pump objective
|
||||
SetHighlight(mechanic_workingPump.Item, true);
|
||||
do
|
||||
{
|
||||
yield return null;
|
||||
if (IsSelectedItem(mechanic_workingPump.Item))
|
||||
{
|
||||
if (mechanic_workingPump.IsActiveSlider.FlashTimer <= 0)
|
||||
if (mechanic_workingPump.PowerButton.FlashTimer <= 0)
|
||||
{
|
||||
mechanic_workingPump.IsActiveSlider.Flash(uiHighlightColor, 1.5f, true);
|
||||
mechanic_workingPump.PowerButton.Flash(uiHighlightColor, 1.5f, true);
|
||||
}
|
||||
}
|
||||
} while (mechanic_workingPump.FlowPercentage >= 0 || !mechanic_workingPump.IsActive); // Highlight until draining
|
||||
@@ -348,6 +348,9 @@ namespace Barotrauma.Tutorials
|
||||
TriggerTutorialSegment(4); // Deconstruct
|
||||
|
||||
SetHighlight(mechanic_craftingCabinet.Item, true);
|
||||
|
||||
bool gotOxygenTank = false;
|
||||
bool gotSodium = false;
|
||||
do
|
||||
{
|
||||
if (mechanic.SelectedConstruction == mechanic_craftingCabinet.Item)
|
||||
@@ -381,8 +384,18 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!gotOxygenTank && mechanic.Inventory.FindItemByIdentifier("oxygentank") != null)
|
||||
{
|
||||
gotOxygenTank = true;
|
||||
}
|
||||
if (!gotSodium && mechanic.Inventory.FindItemByIdentifier("sodium") != null)
|
||||
{
|
||||
gotSodium = true;
|
||||
}
|
||||
yield return null;
|
||||
} while ((mechanic.Inventory.FindItemByIdentifier("oxygentank") == null && mechanic.Inventory.FindItemByIdentifier("aluminium") == null) || mechanic.Inventory.FindItemByIdentifier("sodium") == null); // Wait until looted
|
||||
} while (!gotOxygenTank || !gotSodium); // Wait until looted
|
||||
|
||||
yield return new WaitForSeconds(1.0f, false);
|
||||
SetHighlight(mechanic_craftingCabinet.Item, false);
|
||||
SetHighlight(mechanic_deconstructor.Item, true);
|
||||
@@ -424,7 +437,9 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
}
|
||||
yield return null;
|
||||
} while (mechanic.Inventory.FindItemByIdentifier("aluminium") == null); // Wait until deconstructed
|
||||
} while (
|
||||
mechanic.Inventory.FindItemByIdentifier("aluminium") == null &&
|
||||
mechanic_fabricator.InputContainer.Inventory.FindItemByIdentifier("aluminium") == null); // Wait until aluminium obtained
|
||||
|
||||
SetHighlight(mechanic_deconstructor.Item, false);
|
||||
RemoveCompletedObjective(segments[4]);
|
||||
@@ -482,7 +497,7 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
// Room 5
|
||||
do { yield return null; } while (!mechanic_fireSensor.MotionDetected);
|
||||
TriggerTutorialSegment(6, GameMain.Config.KeyBind(InputType.Aim), GameMain.Config.KeyBind(InputType.Shoot)); // Using the extinguisher
|
||||
TriggerTutorialSegment(6, GameMain.Config.KeyBindText(InputType.Aim), GameMain.Config.KeyBindText(InputType.Shoot)); // Using the extinguisher
|
||||
do { yield return null; } while (!mechanic_fire.Removed); // Wait until extinguished
|
||||
yield return new WaitForSeconds(3f, false);
|
||||
RemoveCompletedObjective(segments[6]);
|
||||
@@ -528,7 +543,7 @@ namespace Barotrauma.Tutorials
|
||||
mechanic.RemoveActiveObjectiveEntity(mechanic_brokenWall_2);
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
|
||||
TriggerTutorialSegment(9, GameMain.Config.KeyBind(InputType.Use)); // Repairing machinery (pump)
|
||||
TriggerTutorialSegment(9, GameMain.Config.KeyBindText(InputType.Use)); // Repairing machinery (pump)
|
||||
SetHighlight(mechanic_brokenPump.Item, true);
|
||||
mechanic_brokenPump.CanBeSelected = true;
|
||||
Repairable repairablePumpComponent = mechanic_brokenPump.Item.GetComponent<Repairable>();
|
||||
@@ -554,9 +569,9 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
if (IsSelectedItem(mechanic_brokenPump.Item))
|
||||
{
|
||||
if (mechanic_brokenPump.IsActiveSlider.FlashTimer <= 0)
|
||||
if (mechanic_brokenPump.PowerButton.FlashTimer <= 0)
|
||||
{
|
||||
mechanic_brokenPump.IsActiveSlider.Flash(uiHighlightColor, 1.5f, true);
|
||||
mechanic_brokenPump.PowerButton.Flash(uiHighlightColor, 1.5f, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,19 +89,34 @@ namespace Barotrauma.Tutorials
|
||||
radioSpeakerName = TextManager.Get("Tutorial.Radio.Speaker");
|
||||
officer = Character.Controlled;
|
||||
|
||||
var handcuffs = officer.Inventory.FindItemByIdentifier("handcuffs");
|
||||
var handcuffs = FindOrGiveItem(officer, "handcuffs");
|
||||
handcuffs.Unequip(officer);
|
||||
officer.Inventory.RemoveItem(handcuffs);
|
||||
|
||||
var stunbaton = officer.Inventory.FindItemByIdentifier("stunbaton");
|
||||
var stunbaton = FindOrGiveItem(officer, "stunbaton");
|
||||
stunbaton.Unequip(officer);
|
||||
officer.Inventory.RemoveItem(stunbaton);
|
||||
|
||||
var ballistichelmet = officer.Inventory.FindItemByIdentifier("ballistichelmet");
|
||||
var smg = FindOrGiveItem(officer, "smg");
|
||||
smg.Unequip(officer);
|
||||
officer.Inventory.RemoveItem(smg);
|
||||
|
||||
var divingknife = FindOrGiveItem(officer, "divingknife");
|
||||
divingknife.Unequip(officer);
|
||||
officer.Inventory.RemoveItem(divingknife);
|
||||
|
||||
var steroids = FindOrGiveItem(officer, "steroids");
|
||||
steroids.Unequip(officer);
|
||||
officer.Inventory.RemoveItem(steroids);
|
||||
|
||||
var ballistichelmet =
|
||||
officer.Inventory.FindItemByIdentifier("ballistichelmet1") ??
|
||||
officer.Inventory.FindItemByIdentifier("ballistichelmet2") ??
|
||||
FindOrGiveItem(officer, "ballistichelmet3");
|
||||
ballistichelmet.Unequip(officer);
|
||||
officer.Inventory.RemoveItem(ballistichelmet);
|
||||
|
||||
var bodyarmor = officer.Inventory.FindItemByIdentifier("bodyarmor");
|
||||
var bodyarmor = FindOrGiveItem(officer, "bodyarmor");
|
||||
bodyarmor.Unequip(officer);
|
||||
officer.Inventory.RemoveItem(bodyarmor);
|
||||
|
||||
@@ -109,6 +124,11 @@ namespace Barotrauma.Tutorials
|
||||
officer_gunIcon = gunOrder.SymbolSprite;
|
||||
officer_gunIconColor = gunOrder.Color;
|
||||
|
||||
var bandage = FindOrGiveItem(officer, "antibleeding1");
|
||||
bandage.Unequip(officer);
|
||||
officer.Inventory.RemoveItem(bandage);
|
||||
FindOrGiveItem(officer, "antibleeding1");
|
||||
|
||||
// Other tutorial items
|
||||
tutorial_mechanicFinalDoorLight = Item.ItemList.Find(i => i.HasTag("tutorial_mechanicfinaldoorlight")).GetComponent<LightComponent>();
|
||||
tutorial_submarineSteering = Item.ItemList.Find(i => i.HasTag("command")).GetComponent<Steering>();
|
||||
@@ -185,6 +205,8 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
while (GameMain.Instance.LoadingScreenOpen) yield return null;
|
||||
|
||||
yield return new WaitForSeconds(0.01f);
|
||||
|
||||
// Room 1
|
||||
SoundPlayer.PlayDamageSound("StructureBlunt", 10, Character.Controlled.WorldPosition);
|
||||
while (shakeTimer > 0.0f) // Wake up, shake
|
||||
@@ -238,7 +260,7 @@ namespace Barotrauma.Tutorials
|
||||
//RemoveCompletedObjective(segments[0]);
|
||||
SetHighlight(officer_equipmentCabinet.Item, false);
|
||||
do { yield return null; } while (IsSelectedItem(officer_equipmentCabinet.Item));
|
||||
TriggerTutorialSegment(1, GameMain.Config.KeyBind(InputType.Aim), GameMain.Config.KeyBind(InputType.Shoot)); // Equip melee weapon & armor
|
||||
TriggerTutorialSegment(1, GameMain.Config.KeyBindText(InputType.Aim), GameMain.Config.KeyBindText(InputType.Shoot)); // Equip melee weapon & armor
|
||||
do
|
||||
{
|
||||
if (!officer.HasEquippedItem("stunbaton"))
|
||||
@@ -249,12 +271,12 @@ namespace Barotrauma.Tutorials
|
||||
{
|
||||
HighlightInventorySlot(officer.Inventory, "bodyarmor", highlightColor, .5f, .5f, 0f);
|
||||
}
|
||||
if (!officer.HasEquippedItem("ballistichelmet"))
|
||||
if (!officer.HasEquippedItem("ballistichelmet1"))
|
||||
{
|
||||
HighlightInventorySlot(officer.Inventory, "ballistichelmet", highlightColor, .5f, .5f, 0f);
|
||||
HighlightInventorySlot(officer.Inventory, "ballistichelmet1", highlightColor, .5f, .5f, 0f);
|
||||
}
|
||||
yield return new WaitForSeconds(1f, false);
|
||||
} while (!officer.HasEquippedItem("stunbaton") || !officer.HasEquippedItem("bodyarmor") || !officer.HasEquippedItem("ballistichelmet"));
|
||||
} while (!officer.HasEquippedItem("stunbaton") || !officer.HasEquippedItem("bodyarmor") || !officer.HasEquippedItem("ballistichelmet1"));
|
||||
RemoveCompletedObjective(segments[1]);
|
||||
SetDoorAccess(officer_firstDoor, officer_firstDoorLight, true);
|
||||
|
||||
@@ -291,7 +313,7 @@ namespace Barotrauma.Tutorials
|
||||
SetHighlight(officer_ammoShelf_2.Item, false);
|
||||
RemoveCompletedObjective(segments[3]);
|
||||
yield return new WaitForSeconds(2f, false);
|
||||
TriggerTutorialSegment(4, GameMain.Config.KeyBind(InputType.Select), GameMain.Config.KeyBind(InputType.Shoot), GameMain.Config.KeyBind(InputType.Deselect)); // Kill hammerhead
|
||||
TriggerTutorialSegment(4, GameMain.Config.KeyBindText(InputType.Select), GameMain.Config.KeyBindText(InputType.Shoot), GameMain.Config.KeyBindText(InputType.Deselect)); // Kill hammerhead
|
||||
officer_hammerhead = SpawnMonster("hammerhead", officer_hammerheadSpawnPos);
|
||||
officer_hammerhead.AIController.SelectTarget(officer.AiTarget);
|
||||
SetHighlight(officer_coilgunPeriscope, true);
|
||||
@@ -325,7 +347,7 @@ namespace Barotrauma.Tutorials
|
||||
//do { yield return null; } while (!officer_rangedWeaponSensor.MotionDetected);
|
||||
do { yield return null; } while (!officer_thirdDoor.IsOpen);
|
||||
yield return new WaitForSeconds(3f, false);
|
||||
TriggerTutorialSegment(5, GameMain.Config.KeyBind(InputType.Aim), GameMain.Config.KeyBind(InputType.Shoot)); // Ranged weapons
|
||||
TriggerTutorialSegment(5, GameMain.Config.KeyBindText(InputType.Aim), GameMain.Config.KeyBindText(InputType.Shoot)); // Ranged weapons
|
||||
SetHighlight(officer_rangedWeaponHolder.Item, true);
|
||||
do { yield return null; } while (!officer_rangedWeaponHolder.Inventory.IsEmpty()); // Wait until looted
|
||||
SetHighlight(officer_rangedWeaponHolder.Item, false);
|
||||
@@ -31,8 +31,8 @@ namespace Barotrauma.Tutorials
|
||||
protected Color highlightColor = Color.OrangeRed;
|
||||
protected Color uiHighlightColor = new Color(150, 50, 0);
|
||||
protected Color buttonHighlightColor = new Color(255, 100, 0);
|
||||
protected Color inaccessibleColor = Color.Red;
|
||||
protected Color accessibleColor = Color.Green;
|
||||
protected Color inaccessibleColor = GUI.Style.Red;
|
||||
protected Color accessibleColor = GUI.Style.Green;
|
||||
|
||||
public ScenarioTutorial(XElement element) : base(element)
|
||||
{
|
||||
@@ -88,7 +88,7 @@ namespace Barotrauma.Tutorials
|
||||
GameMain.GameSession.StartRound(levelSeed);
|
||||
}
|
||||
|
||||
GameMain.GameSession.EventManager.Events.Clear();
|
||||
GameMain.GameSession.EventManager.ActiveEvents.Clear();
|
||||
GameMain.GameSession.EventManager.Enabled = false;
|
||||
GameMain.GameScreen.Select();
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Barotrauma.Tutorials
|
||||
Submarine.MainSub.GodMode = true;
|
||||
|
||||
CharacterInfo charInfo = configElement.Element("Character") == null ?
|
||||
new CharacterInfo(Character.HumanSpeciesName, "", JobPrefab.Get("engineer")) :
|
||||
new CharacterInfo(CharacterPrefab.HumanSpeciesName, "", JobPrefab.Get("engineer")) :
|
||||
new CharacterInfo(configElement.Element("Character"));
|
||||
|
||||
WayPoint wayPoint = GetSpawnPoint(charInfo);
|
||||
@@ -298,5 +298,16 @@ namespace Barotrauma.Tutorials
|
||||
character.Bloodloss = 0.0f;
|
||||
character.SetStun(0.0f, true);
|
||||
}
|
||||
|
||||
protected Item FindOrGiveItem(Character character, string identifier)
|
||||
{
|
||||
var item = character.Inventory.FindItemByIdentifier(identifier);
|
||||
if (item != null && !item.Removed) { return item; }
|
||||
|
||||
ItemPrefab itemPrefab = MapEntityPrefab.Find(name: null, identifier: identifier) as ItemPrefab;
|
||||
item = new Item(itemPrefab, Vector2.Zero, submarine: null);
|
||||
character.Inventory.TryPutItem(item, character, item.AllowedSlots);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,9 +99,9 @@ namespace Barotrauma.Tutorials
|
||||
public static void Init()
|
||||
{
|
||||
Tutorials = new List<Tutorial>();
|
||||
foreach (string file in GameMain.Instance.GetFilesOfType(ContentType.Tutorials))
|
||||
foreach (ContentFile file in GameMain.Instance.GetFilesOfType(ContentType.Tutorials))
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file);
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file.Path);
|
||||
if (doc?.Root == null) continue;
|
||||
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
@@ -372,9 +372,10 @@ namespace Barotrauma.Tutorials
|
||||
|
||||
private void CreateObjectiveGUI(TutorialSegment segment, int index, TutorialContentTypes type)
|
||||
{
|
||||
Point replayButtonSize = new Point((int)(GUI.ObjectiveNameFont.MeasureString(segment.Objective).X), (int)(GUI.ObjectiveNameFont.MeasureString(segment.Objective).Y * 1.45f));
|
||||
string objectiveText = TextManager.ParseInputTypes(segment.Objective);
|
||||
Point replayButtonSize = new Point((int)(GUI.LargeFont.MeasureString(objectiveText).X), (int)(GUI.LargeFont.MeasureString(objectiveText).Y * 1.45f));
|
||||
|
||||
segment.ReplayButton = new GUIButton(new RectTransform(replayButtonSize, objectiveFrame.RectTransform, Anchor.TopRight, Pivot.TopRight) { AbsoluteOffset = new Point(0, (replayButtonSize.Y + (int)(20f * GUI.Scale)) * index) }, style: null);
|
||||
segment.ReplayButton = new GUIButton(new RectTransform(replayButtonSize, objectiveFrame.RectTransform, Anchor.TopLeft, Pivot.TopLeft) { AbsoluteOffset = new Point(0, (replayButtonSize.Y + (int)(20f * GUI.Scale)) * index) }, style: null);
|
||||
segment.ReplayButton.OnClicked += (GUIButton btn, object userdata) =>
|
||||
{
|
||||
if (type == TutorialContentTypes.Video)
|
||||
@@ -389,13 +390,15 @@ namespace Barotrauma.Tutorials
|
||||
};
|
||||
|
||||
string objectiveTitleText = TextManager.ParseInputTypes(objectiveTranslated);
|
||||
int yOffset = (int)((GUI.ObjectiveNameFont.MeasureString(objectiveTitleText).Y / 2f + 5));
|
||||
segment.LinkedTitle = new GUITextBlock(new RectTransform(new Point((int)GUI.ObjectiveTitleFont.MeasureString(objectiveTitleText).X, yOffset), segment.ReplayButton.RectTransform, Anchor.CenterRight, Pivot.BottomRight) /*{ AbsoluteOffset = new Point((int)(-10 * GUI.Scale), 0) }*/,
|
||||
objectiveTitleText, textColor: Color.White, font: GUI.ObjectiveTitleFont, textAlignment: Alignment.CenterRight);
|
||||
int yOffset = (int)((GUI.SubHeadingFont.MeasureString(objectiveTitleText).Y + 5));
|
||||
segment.LinkedTitle = new GUITextBlock(new RectTransform(new Point((int)GUI.SubHeadingFont.MeasureString(objectiveTitleText).X, yOffset), segment.ReplayButton.RectTransform, Anchor.CenterLeft, Pivot.BottomLeft) /*{ AbsoluteOffset = new Point((int)(-10 * GUI.Scale), 0) }*/,
|
||||
objectiveTitleText, textColor: Color.White, font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft)
|
||||
{
|
||||
ForceUpperCase = true
|
||||
};
|
||||
|
||||
string objectiveText = TextManager.ParseInputTypes(segment.Objective);
|
||||
segment.LinkedText = new GUITextBlock(new RectTransform(new Point((int)GUI.ObjectiveNameFont.MeasureString(objectiveText).X, yOffset), segment.ReplayButton.RectTransform, Anchor.CenterRight, Pivot.TopRight) /*{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), 0) }*/,
|
||||
objectiveText, textColor: new Color(4, 180, 108), font: GUI.ObjectiveNameFont, textAlignment: Alignment.CenterRight);
|
||||
segment.LinkedText = new GUITextBlock(new RectTransform(new Point((int)GUI.LargeFont.MeasureString(objectiveText).X, yOffset), segment.ReplayButton.RectTransform, Anchor.CenterLeft, Pivot.TopLeft) /*{ AbsoluteOffset = new Point((int)(10 * GUI.Scale), 0) }*/,
|
||||
objectiveText, textColor: new Color(4, 180, 108), font: GUI.LargeFont, textAlignment: Alignment.CenterLeft);
|
||||
|
||||
segment.LinkedTitle.Color = segment.LinkedTitle.HoverColor = segment.LinkedTitle.PressedColor = segment.LinkedTitle.SelectedColor = Color.Transparent;
|
||||
segment.LinkedText.Color = segment.LinkedText.HoverColor = segment.LinkedText.PressedColor = segment.LinkedText.SelectedColor = Color.Transparent;
|
||||
@@ -449,19 +452,19 @@ namespace Barotrauma.Tutorials
|
||||
RectTransform rectTA;
|
||||
if (objectiveTextWidth > objectiveTitleWidth)
|
||||
{
|
||||
rectTA = new RectTransform(new Point(checkMarkWidth, checkMarkHeight), segment.ReplayButton.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft);
|
||||
rectTA.AbsoluteOffset = new Point(-rectTA.Rect.Width - (int)(10 * GUI.Scale), 0);
|
||||
rectTA = new RectTransform(new Point(checkMarkWidth, checkMarkHeight), segment.ReplayButton.RectTransform, Anchor.BottomRight, Pivot.BottomRight);
|
||||
rectTA.AbsoluteOffset = new Point(-rectTA.Rect.Width - (int)(25 * GUI.Scale), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
rectTA = new RectTransform(new Point(checkMarkWidth, checkMarkHeight), segment.ReplayButton.RectTransform, Anchor.BottomLeft, Pivot.BottomLeft);
|
||||
rectTA.AbsoluteOffset = new Point(-rectTA.Rect.Width - (int)(10 * GUI.Scale) - (objectiveTitleWidth - objectiveTextWidth), 0);
|
||||
rectTA = new RectTransform(new Point(checkMarkWidth, checkMarkHeight), segment.ReplayButton.RectTransform, Anchor.BottomRight, Pivot.BottomRight);
|
||||
rectTA.AbsoluteOffset = new Point(-rectTA.Rect.Width - (int)(25 * GUI.Scale) - (objectiveTitleWidth - objectiveTextWidth), 0);
|
||||
}
|
||||
|
||||
GUIImage checkmark = new GUIImage(rectTA, "CheckMark");
|
||||
checkmark.Color = checkmark.SelectedColor = checkmark.HoverColor = checkmark.PressedColor = color;
|
||||
|
||||
RectTransform rectTB = new RectTransform(new Vector2(1.1f, .8f), segment.LinkedText.RectTransform, Anchor.Center, Pivot.Center);
|
||||
RectTransform rectTB = new RectTransform(new Vector2(1.0f, .8f), segment.LinkedText.RectTransform, Anchor.Center, Pivot.Center);
|
||||
GUIImage stroke = new GUIImage(rectTB, "Stroke");
|
||||
stroke.Color = stroke.SelectedColor = stroke.HoverColor = stroke.PressedColor = color;
|
||||
|
||||
@@ -493,14 +496,6 @@ namespace Barotrauma.Tutorials
|
||||
protected GUIComponent CreateInfoFrame(string title, string text, int width = 300, int height = 80, string anchorStr = "", bool hasButton = false, Action callback = null, Action showVideo = null)
|
||||
{
|
||||
if (hasButton) height += 60;
|
||||
|
||||
string wrappedText = ToolBox.WrapText(text, width, GUI.Font);
|
||||
|
||||
height += (int)(GUI.Font.MeasureString(wrappedText).Y + 50);
|
||||
if (title.Length > 0)
|
||||
{
|
||||
height += 35;
|
||||
}
|
||||
|
||||
Anchor anchor = Anchor.TopRight;
|
||||
|
||||
@@ -509,12 +504,23 @@ namespace Barotrauma.Tutorials
|
||||
Enum.TryParse(anchorStr, out anchor);
|
||||
}
|
||||
|
||||
var background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), "InnerFrame", new Color(0, 0, 0, 1f));
|
||||
width = (int)(width * GUI.Scale);
|
||||
height = (int)(height * GUI.Scale);
|
||||
|
||||
var infoBlock = new GUIFrame(new RectTransform(new Point((int)(width * GUI.Scale), (int)(height * GUI.Scale)), background.RectTransform, anchor) { AbsoluteOffset = new Point(20) });
|
||||
infoBlock.Flash(Color.Green);
|
||||
string wrappedText = ToolBox.WrapText(text, width, GUI.Font);
|
||||
height += (int)GUI.Font.MeasureString(wrappedText).Y;
|
||||
|
||||
var infoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.8f), infoBlock.RectTransform, Anchor.Center))
|
||||
if (title.Length > 0)
|
||||
{
|
||||
height += (int)GUI.Font.MeasureString(title).Y + (int)(150 * GUI.Scale);
|
||||
}
|
||||
|
||||
var background = new GUIFrame(new RectTransform(new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight), GUI.Canvas, Anchor.Center), style: null, Color.Black * 0.5f);
|
||||
|
||||
var infoBlock = new GUIFrame(new RectTransform(new Point(width, height), background.RectTransform, anchor));
|
||||
infoBlock.Flash(GUI.Style.Green);
|
||||
|
||||
var infoContent = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.9f), infoBlock.RectTransform, Anchor.Center))
|
||||
{
|
||||
Stretch = true,
|
||||
AbsoluteSpacing = 5
|
||||
@@ -523,26 +529,35 @@ namespace Barotrauma.Tutorials
|
||||
if (title.Length > 0)
|
||||
{
|
||||
var titleBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform),
|
||||
title, font: GUI.VideoTitleFont, textAlignment: Alignment.Center, textColor: new Color(253, 174, 0));
|
||||
title, font: GUI.LargeFont, textAlignment: Alignment.Center, textColor: new Color(253, 174, 0));
|
||||
titleBlock.RectTransform.IsFixedSize = true;
|
||||
}
|
||||
|
||||
var textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), " " + text, wrap: true);
|
||||
textBlock.RectTransform.IsFixedSize = true;
|
||||
List<ColorData> colorData = ColorData.GetColorData(text, out text);
|
||||
GUITextBlock textBlock;
|
||||
if (colorData == null)
|
||||
{
|
||||
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), " " + text, wrap: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
textBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContent.RectTransform), colorData, " " + text, wrap: true);
|
||||
}
|
||||
|
||||
textBlock.RectTransform.IsFixedSize = true;
|
||||
infoBoxClosedCallback = callback;
|
||||
|
||||
if (hasButton)
|
||||
{
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), infoContent.RectTransform) { MinSize = new Point(0, 30), MaxSize = new Point((int) infoContent.Rect.X, 60) }, isHorizontal: true)
|
||||
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.15f), infoContent.RectTransform), isHorizontal: true)
|
||||
{
|
||||
Stretch = true,
|
||||
RelativeSpacing = 0.1f
|
||||
};
|
||||
buttonContainer.RectTransform.IsFixedSize = true;
|
||||
|
||||
if (showVideo != null)
|
||||
{
|
||||
buttonContainer.Stretch = true;
|
||||
var videoButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("Video"), style: "GUIButtonLarge")
|
||||
{
|
||||
@@ -553,8 +568,13 @@ namespace Barotrauma.Tutorials
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
buttonContainer.Stretch = false;
|
||||
buttonContainer.ChildAnchor = Anchor.Center;
|
||||
}
|
||||
|
||||
var okButton = new GUIButton(new RectTransform(new Vector2(0.6f, 1.0f), buttonContainer.RectTransform),
|
||||
var okButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonContainer.RectTransform),
|
||||
TextManager.Get("OK"), style: "GUIButtonLarge")
|
||||
{
|
||||
OnClicked = CloseInfoFrame
|
||||
@@ -0,0 +1,210 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class GameSession
|
||||
{
|
||||
private InfoFrameTab selectedTab;
|
||||
private GUIFrame infoFrame;
|
||||
|
||||
private readonly List<GUIButton> tabButtons = new List<GUIButton>();
|
||||
|
||||
private GUIFrame infoFrameContent;
|
||||
public RoundSummary RoundSummary { get; private set; }
|
||||
public static bool IsInfoFrameOpen => GameMain.GameSession?.infoFrame != null;
|
||||
|
||||
private bool ToggleInfoFrame()
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetLobbyScreen != null)
|
||||
{
|
||||
if (GameMain.NetLobbyScreen.HeadSelectionList != null) { GameMain.NetLobbyScreen.HeadSelectionList.Visible = false; }
|
||||
if (GameMain.NetLobbyScreen.JobSelectionFrame != null) { GameMain.NetLobbyScreen.JobSelectionFrame.Visible = false; }
|
||||
}
|
||||
if (infoFrame == null)
|
||||
{
|
||||
CreateInfoFrame();
|
||||
SelectInfoFrameTab(null, selectedTab);
|
||||
}
|
||||
else
|
||||
{
|
||||
infoFrame = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CreateInfoFrame()
|
||||
{
|
||||
int width = 600, height = 400;
|
||||
|
||||
tabButtons.Clear();
|
||||
|
||||
infoFrame = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas), style: "GUIBackgroundBlocker");
|
||||
|
||||
var innerFrame = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.35f), infoFrame.RectTransform, Anchor.Center) { MinSize = new Point(width, height), RelativeOffset = new Vector2(0.0f, 0.033f) });
|
||||
|
||||
var paddedFrame = new GUIFrame(new RectTransform(new Vector2(0.95f, 0.9f), innerFrame.RectTransform, Anchor.Center), style: null);
|
||||
var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.08f), paddedFrame.RectTransform), isHorizontal: true)
|
||||
{
|
||||
RelativeSpacing = 0.01f
|
||||
};
|
||||
infoFrameContent = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.85f), paddedFrame.RectTransform) { RelativeOffset = new Vector2(0.0f, 0.08f) }, style: "InnerFrame");
|
||||
|
||||
var crewButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonArea.RectTransform), TextManager.Get("Crew"), style: "GUITabButton")
|
||||
{
|
||||
UserData = InfoFrameTab.Crew,
|
||||
OnClicked = SelectInfoFrameTab
|
||||
};
|
||||
tabButtons.Add(crewButton);
|
||||
|
||||
var missionButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonArea.RectTransform), TextManager.Get("Mission"), style: "GUITabButton")
|
||||
{
|
||||
UserData = InfoFrameTab.Mission,
|
||||
OnClicked = SelectInfoFrameTab
|
||||
};
|
||||
tabButtons.Add(missionButton);
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
var myCharacterButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonArea.RectTransform), TextManager.Get("MyCharacter"), style: "GUITabButton")
|
||||
{
|
||||
UserData = InfoFrameTab.MyCharacter,
|
||||
OnClicked = SelectInfoFrameTab
|
||||
};
|
||||
tabButtons.Add(myCharacterButton);
|
||||
}
|
||||
|
||||
/*TODO: fix
|
||||
if (GameMain.Server != null)
|
||||
{
|
||||
var manageButton = new GUIButton(new RectTransform(new Vector2(0.2f, 1.0f), buttonArea.RectTransform), TextManager.Get("ManagePlayers"))
|
||||
{
|
||||
UserData = InfoFrameTab.ManagePlayers,
|
||||
OnClicked = SelectInfoFrameTab
|
||||
};
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
private bool SelectInfoFrameTab(GUIButton button, object userData)
|
||||
{
|
||||
selectedTab = (InfoFrameTab)userData;
|
||||
|
||||
CreateInfoFrame();
|
||||
tabButtons.ForEach(tb => tb.Selected = (InfoFrameTab)tb.UserData == selectedTab);
|
||||
|
||||
switch (selectedTab)
|
||||
{
|
||||
case InfoFrameTab.Crew:
|
||||
CrewManager.CreateCrewListFrame(CrewManager.GetCharacters(), infoFrameContent);
|
||||
break;
|
||||
case InfoFrameTab.Mission:
|
||||
CreateMissionInfo(infoFrameContent);
|
||||
break;
|
||||
case InfoFrameTab.MyCharacter:
|
||||
if (GameMain.NetworkMember == null) { return false; }
|
||||
GameMain.NetLobbyScreen.CreatePlayerFrame(infoFrameContent);
|
||||
break;
|
||||
case InfoFrameTab.ManagePlayers:
|
||||
//TODO: fix
|
||||
//GameMain.Server.ManagePlayersFrame(infoFrameContent);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CreateMissionInfo(GUIFrame infoFrame)
|
||||
{
|
||||
infoFrameContent.ClearChildren();
|
||||
|
||||
var isTraitor = GameMain.Client?.Character?.IsTraitor ?? false;
|
||||
|
||||
var missionFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, isTraitor ? 0.95f : 0.45f), infoFrameContent.RectTransform))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
|
||||
if (Mission != null)
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionFrame.RectTransform), Mission.Name, font: GUI.LargeFont);
|
||||
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionFrame.RectTransform), TextManager.GetWithVariable("MissionReward", "[reward]", Mission.Reward.ToString()));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionFrame.RectTransform), Mission.Description, wrap: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionFrame.RectTransform, Anchor.TopCenter), TextManager.Get("NoMission"), font: GUI.LargeFont);
|
||||
}
|
||||
if (isTraitor)
|
||||
{
|
||||
var traitorFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.45f), infoFrameContent.RectTransform, Anchor.BottomLeft))
|
||||
{
|
||||
RelativeSpacing = 0.05f
|
||||
};
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), traitorFrame.RectTransform), TextManager.Get("Traitors"), font: GUI.LargeFont);
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), traitorFrame.RectTransform), GameMain.Client.Character.TraitorCurrentObjective, wrap: true);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddToGUIUpdateList()
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
GameMode?.AddToGUIUpdateList();
|
||||
infoFrame?.AddToGUIUpdateList();
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
GameMain.NetLobbyScreen?.HeadSelectionList?.AddToGUIUpdateList();
|
||||
GameMain.NetLobbyScreen?.JobSelectionFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
|
||||
if (PlayerInput.KeyDown(InputType.InfoTab) &&
|
||||
(GUI.KeyboardDispatcher.Subscriber == null || GUI.KeyboardDispatcher.Subscriber is GUIListBox))
|
||||
{
|
||||
if (infoFrame == null)
|
||||
{
|
||||
ToggleInfoFrame();
|
||||
}
|
||||
}
|
||||
else if (infoFrame != null)
|
||||
{
|
||||
ToggleInfoFrame();
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
if (GameMain.NetLobbyScreen?.HeadSelectionList != null)
|
||||
{
|
||||
if (PlayerInput.PrimaryMouseButtonDown() && !GUI.IsMouseOn(GameMain.NetLobbyScreen.HeadSelectionList))
|
||||
{
|
||||
if (GameMain.NetLobbyScreen.HeadSelectionList != null) { GameMain.NetLobbyScreen.HeadSelectionList.Visible = false; }
|
||||
}
|
||||
}
|
||||
if (GameMain.NetLobbyScreen?.JobSelectionFrame != null)
|
||||
{
|
||||
if (PlayerInput.PrimaryMouseButtonDown() && !GUI.IsMouseOn(GameMain.NetLobbyScreen.JobSelectionFrame))
|
||||
{
|
||||
GameMain.NetLobbyScreen.JobList.Deselect();
|
||||
if (GameMain.NetLobbyScreen.JobSelectionFrame != null) { GameMain.NetLobbyScreen.JobSelectionFrame.Visible = false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch)
|
||||
{
|
||||
if (GUI.DisableHUD) return;
|
||||
|
||||
GameMode?.Draw(spriteBatch);
|
||||
//infoFrame?.DrawManually(spriteBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
1548
Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs
Normal file
1548
Barotrauma/BarotraumaClient/ClientSource/GameSettings.cs
Normal file
File diff suppressed because it is too large
Load Diff
1127
Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs
Normal file
1127
Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,264 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Lights;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Door : Pickable, IDrawableComponent, IServerSerializable
|
||||
{
|
||||
private ConvexHull convexHull;
|
||||
private ConvexHull convexHull2;
|
||||
|
||||
private float shake;
|
||||
private float shakeTimer;
|
||||
private Vector2 shakePos;
|
||||
|
||||
//openState when the vertices of the convex hull were last calculated
|
||||
private float lastConvexHullState;
|
||||
|
||||
[Serialize("1,1", false, description: "The scale of the shadow-casting area of the door (relative to the actual size of the door).")]
|
||||
public Vector2 ShadowScale
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Vector2 DrawSize
|
||||
{
|
||||
//use the extents of the item as the draw size
|
||||
get { return Vector2.Zero; }
|
||||
}
|
||||
|
||||
private Vector2[] GetConvexHullCorners(Rectangle rect)
|
||||
{
|
||||
Point shadowSize = rect.Size.Multiply(ShadowScale);
|
||||
Vector2 center = new Vector2(rect.Center.X, rect.Y - rect.Height / 2);
|
||||
|
||||
Vector2[] corners = new Vector2[4];
|
||||
corners[0] = center + new Vector2(-shadowSize.X, -shadowSize.Y) / 2;
|
||||
corners[1] = center + new Vector2(-shadowSize.X, shadowSize.Y) / 2;
|
||||
corners[2] = center + new Vector2(shadowSize.X, shadowSize.Y) / 2;
|
||||
corners[3] = center + new Vector2(shadowSize.X, -shadowSize.Y) / 2;
|
||||
|
||||
return corners;
|
||||
}
|
||||
|
||||
private void UpdateConvexHulls()
|
||||
{
|
||||
doorRect = new Rectangle(
|
||||
item.Rect.Center.X - (int)(doorSprite.size.X / 2 * item.Scale),
|
||||
item.Rect.Y - item.Rect.Height / 2 + (int)(doorSprite.size.Y / 2.0f * item.Scale),
|
||||
(int)(doorSprite.size.X * item.Scale),
|
||||
(int)(doorSprite.size.Y * item.Scale));
|
||||
|
||||
Rectangle rect = doorRect;
|
||||
if (IsHorizontal)
|
||||
{
|
||||
rect.Width = (int)(rect.Width * (1.0f - openState));
|
||||
}
|
||||
else
|
||||
{
|
||||
rect.Height = (int)(rect.Height * (1.0f - openState));
|
||||
}
|
||||
|
||||
if (Window.Height > 0 && Window.Width > 0)
|
||||
{
|
||||
rect.Height = -(int)(Window.Y * item.Scale);
|
||||
|
||||
rect.Y += (int)(doorRect.Height * openState);
|
||||
rect.Height = Math.Max(rect.Height - (rect.Y - doorRect.Y), 0);
|
||||
rect.Y = Math.Min(doorRect.Y, rect.Y);
|
||||
|
||||
if (convexHull2 != null)
|
||||
{
|
||||
Rectangle rect2 = doorRect;
|
||||
rect2.Y += (int)(Window.Y * item.Scale - Window.Height * item.Scale);
|
||||
|
||||
rect2.Y += (int)(doorRect.Height * openState);
|
||||
rect2.Y = Math.Min(doorRect.Y, rect2.Y);
|
||||
rect2.Height = rect2.Y - (doorRect.Y - (int)(doorRect.Height * (1.0f - openState)));
|
||||
|
||||
if (rect2.Height == 0)
|
||||
{
|
||||
convexHull2.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
convexHull2.Enabled = true;
|
||||
convexHull2.SetVertices(GetConvexHullCorners(rect2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (convexHull == null) return;
|
||||
|
||||
if (rect.Height == 0 || rect.Width == 0)
|
||||
{
|
||||
convexHull.Enabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
convexHull.Enabled = true;
|
||||
convexHull.SetVertices(GetConvexHullCorners(rect));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
if (shakeTimer > 0.0f)
|
||||
{
|
||||
shakeTimer -= deltaTime;
|
||||
Vector2 noisePos = new Vector2((float)PerlinNoise.CalculatePerlin(shakeTimer * 10.0f, shakeTimer * 10.0f, 0) - 0.5f, (float)PerlinNoise.CalculatePerlin(shakeTimer * 10.0f, shakeTimer * 10.0f, 0.5f) - 0.5f);
|
||||
shakePos = noisePos * shake * 2.0f;
|
||||
shake = Math.Min(shake, shakeTimer * 10.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
shakePos = Vector2.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
{
|
||||
Color color = item.SpriteColor;
|
||||
if (brokenSprite == null)
|
||||
{
|
||||
//broken doors turn black if no broken sprite has been configured
|
||||
color *= (item.Condition / item.Prefab.Health);
|
||||
color.A = 255;
|
||||
}
|
||||
|
||||
if (stuck > 0.0f && weldedSprite != null)
|
||||
{
|
||||
Vector2 weldSpritePos = new Vector2(item.Rect.Center.X, item.Rect.Y - item.Rect.Height / 2.0f) + shakePos;
|
||||
if (item.Submarine != null) weldSpritePos += item.Submarine.DrawPosition;
|
||||
weldSpritePos.Y = -weldSpritePos.Y;
|
||||
|
||||
weldedSprite.Draw(spriteBatch,
|
||||
weldSpritePos, item.SpriteColor * (stuck / 100.0f), scale: item.Scale);
|
||||
}
|
||||
|
||||
if (openState >= 1.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsHorizontal)
|
||||
{
|
||||
Vector2 pos = new Vector2(item.Rect.X, item.Rect.Y - item.Rect.Height / 2) + shakePos;
|
||||
if (item.Submarine != null) pos += item.Submarine.DrawPosition;
|
||||
pos.Y = -pos.Y;
|
||||
|
||||
if (brokenSprite == null || !IsBroken)
|
||||
{
|
||||
spriteBatch.Draw(doorSprite.Texture, pos,
|
||||
new Rectangle((int) (doorSprite.SourceRect.X + doorSprite.size.X * openState),
|
||||
(int) doorSprite.SourceRect.Y,
|
||||
(int) (doorSprite.size.X * (1.0f - openState)), (int) doorSprite.size.Y),
|
||||
color, 0.0f, doorSprite.Origin, item.Scale, SpriteEffects.None, doorSprite.Depth);
|
||||
}
|
||||
|
||||
if (brokenSprite != null && item.Health < item.Prefab.Health)
|
||||
{
|
||||
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f, 1.0f - item.Health / item.Prefab.Health) : Vector2.One;
|
||||
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.Prefab.Health : 1.0f;
|
||||
spriteBatch.Draw(brokenSprite.Texture, pos,
|
||||
new Rectangle((int)(brokenSprite.SourceRect.X + brokenSprite.size.X * openState), brokenSprite.SourceRect.Y,
|
||||
(int)(brokenSprite.size.X * (1.0f - openState)), (int)brokenSprite.size.Y),
|
||||
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, SpriteEffects.None,
|
||||
brokenSprite.Depth);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector2 pos = new Vector2(item.Rect.Center.X, item.Rect.Y) + shakePos;
|
||||
if (item.Submarine != null) pos += item.Submarine.DrawPosition;
|
||||
pos.Y = -pos.Y;
|
||||
|
||||
if (brokenSprite == null || !IsBroken)
|
||||
{
|
||||
spriteBatch.Draw(doorSprite.Texture, pos,
|
||||
new Rectangle(doorSprite.SourceRect.X,
|
||||
(int) (doorSprite.SourceRect.Y + doorSprite.size.Y * openState),
|
||||
(int) doorSprite.size.X, (int) (doorSprite.size.Y * (1.0f - openState))),
|
||||
color, 0.0f, doorSprite.Origin, item.Scale, SpriteEffects.None, doorSprite.Depth);
|
||||
}
|
||||
|
||||
if (brokenSprite != null && item.Health < item.Prefab.Health)
|
||||
{
|
||||
Vector2 scale = scaleBrokenSprite ? new Vector2(1.0f - item.Health / item.Prefab.Health, 1.0f) : Vector2.One;
|
||||
float alpha = fadeBrokenSprite ? 1.0f - item.Health / item.Prefab.Health : 1.0f;
|
||||
spriteBatch.Draw(brokenSprite.Texture, pos,
|
||||
new Rectangle(brokenSprite.SourceRect.X, (int)(brokenSprite.SourceRect.Y + brokenSprite.size.Y * openState),
|
||||
(int)brokenSprite.size.X, (int)(brokenSprite.size.Y * (1.0f - openState))),
|
||||
color * alpha, 0.0f, brokenSprite.Origin, scale * item.Scale, SpriteEffects.None, brokenSprite.Depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnFailedToOpen()
|
||||
{
|
||||
if (shakeTimer <= 0.0f)
|
||||
{
|
||||
PlaySound(ActionType.OnFailure);
|
||||
shake = 5.0f;
|
||||
shakeTimer = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
partial void SetState(bool open, bool isNetworkMessage, bool sendNetworkMessage, bool forcedOpen)
|
||||
{
|
||||
if ((IsStuck && !isNetworkMessage) ||
|
||||
(PredictedState == null && isOpen == open) ||
|
||||
(PredictedState != null && isOpen == PredictedState.Value && isOpen == open))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.Client != null && !isNetworkMessage)
|
||||
{
|
||||
bool stateChanged = open != PredictedState;
|
||||
|
||||
//clients can "predict" that the door opens/closes when a signal is received
|
||||
//the prediction will be reset after 1 second, setting the door to a state
|
||||
//sent by the server, or reverting it back to its old state if no msg from server was received
|
||||
PredictedState = open;
|
||||
resetPredictionTimer = CorrectionDelay;
|
||||
if (stateChanged) PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse);
|
||||
}
|
||||
else
|
||||
{
|
||||
isOpen = open;
|
||||
if (!isNetworkMessage || open != PredictedState)
|
||||
{
|
||||
StopPicking(null);
|
||||
PlaySound(forcedOpen ? ActionType.OnPicked : ActionType.OnUse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
base.ClientRead(type, msg, sendingTime);
|
||||
|
||||
bool open = msg.ReadBoolean();
|
||||
bool forcedOpen = msg.ReadBoolean();
|
||||
SetState(open, isNetworkMessage: true, sendNetworkMessage: false, forcedOpen: forcedOpen);
|
||||
Stuck = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
UInt16 lastUserID = msg.ReadUInt16();
|
||||
Character user = lastUserID == 0 ? null : Entity.FindEntityByID(lastUserID) as Character;
|
||||
if (user != lastUser)
|
||||
{
|
||||
lastUser = user;
|
||||
toggleCooldownTimer = ToggleCoolDown;
|
||||
}
|
||||
|
||||
if (isStuck) { OpenState = 0.0f; }
|
||||
PredictedState = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void DischargeProjSpecific()
|
||||
{
|
||||
PlaySound(ActionType.OnUse, item.WorldPosition);
|
||||
PlaySound(ActionType.OnUse);
|
||||
foreach (Node node in nodes)
|
||||
{
|
||||
GameMain.ParticleManager.CreateParticle("swirlysmoke", node.WorldPosition, Vector2.Zero);
|
||||
@@ -0,0 +1,108 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Holdable : IDrawableComponent
|
||||
{
|
||||
public Vector2 DrawSize
|
||||
{
|
||||
get { return item.Rect.Size.ToVector2(); }
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1)
|
||||
{
|
||||
if (!IsActive || picker == null || !CanBeAttached() || !picker.IsKeyDown(InputType.Aim) || picker != Character.Controlled) { return; }
|
||||
|
||||
Vector2 gridPos = picker.Position;
|
||||
Vector2 roundedGridPos = new Vector2(
|
||||
MathUtils.RoundTowardsClosest(picker.Position.X, Submarine.GridSize.X),
|
||||
MathUtils.RoundTowardsClosest(picker.Position.Y, Submarine.GridSize.Y));
|
||||
Vector2 attachPos = GetAttachPosition(picker);
|
||||
|
||||
if (item.Submarine == null)
|
||||
{
|
||||
Structure attachTarget = Structure.GetAttachTarget(item.WorldPosition);
|
||||
if (attachTarget != null)
|
||||
{
|
||||
if (attachTarget.Submarine != null)
|
||||
{
|
||||
//set to submarine-relative position
|
||||
gridPos += attachTarget.Submarine.Position;
|
||||
roundedGridPos += attachTarget.Submarine.Position;
|
||||
attachPos += attachTarget.Submarine.Position;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gridPos += item.Submarine.Position;
|
||||
roundedGridPos += item.Submarine.Position;
|
||||
attachPos += item.Submarine.Position;
|
||||
}
|
||||
|
||||
Submarine.DrawGrid(spriteBatch, 14, gridPos, roundedGridPos, alpha: 0.7f);
|
||||
|
||||
item.Sprite.Draw(
|
||||
spriteBatch,
|
||||
new Vector2(attachPos.X, -attachPos.Y),
|
||||
item.SpriteColor * 0.5f,
|
||||
0.0f, item.Scale, SpriteEffects.None, 0.0f);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(attachPos.X - 2, -attachPos.Y - 2), Vector2.One * 5, GUI.Style.Red, thickness: 3);
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
{
|
||||
if (!attachable || body == null) { return; }
|
||||
|
||||
Vector2 attachPos = (Vector2)extraData[2];
|
||||
msg.Write(attachPos.X);
|
||||
msg.Write(attachPos.Y);
|
||||
}
|
||||
|
||||
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
base.ClientRead(type, msg, sendingTime);
|
||||
bool shouldBeAttached = msg.ReadBoolean();
|
||||
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
|
||||
if (!attachable)
|
||||
{
|
||||
DebugConsole.ThrowError("Received an attachment event for an item that's not attachable.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldBeAttached)
|
||||
{
|
||||
if (!attached)
|
||||
{
|
||||
Drop(false, null);
|
||||
item.SetTransform(simPosition, 0.0f);
|
||||
AttachToWall();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (attached)
|
||||
{
|
||||
DropConnectedWires(null);
|
||||
|
||||
if (body != null)
|
||||
{
|
||||
item.body = body;
|
||||
item.body.Enabled = true;
|
||||
}
|
||||
IsActive = false;
|
||||
|
||||
DeattachFromWall();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using Barotrauma.Particles;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class RangedWeapon : ItemComponent
|
||||
{
|
||||
private Sprite crosshairSprite, crosshairPointerSprite;
|
||||
|
||||
private Vector2 crosshairPos, crosshairPointerPos;
|
||||
|
||||
private float currentCrossHairScale, currentCrossHairPointerScale;
|
||||
|
||||
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||
|
||||
[Serialize(1.0f, false, description: "The scale of the crosshair sprite (if there is one).")]
|
||||
public float CrossHairScale
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element)
|
||||
{
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "crosshair":
|
||||
{
|
||||
string texturePath = subElement.GetAttributeString("texture", "");
|
||||
crosshairSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath));
|
||||
}
|
||||
break;
|
||||
case "crosshairpointer":
|
||||
{
|
||||
string texturePath = subElement.GetAttributeString("texture", "");
|
||||
crosshairPointerSprite = new Sprite(subElement, texturePath.Contains("/") ? "" : Path.GetDirectoryName(item.Prefab.FilePath));
|
||||
}
|
||||
break;
|
||||
case "particleemitter":
|
||||
particleEmitters.Add(new ParticleEmitter(subElement));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateHUD(Character character, float deltaTime, Camera cam)
|
||||
{
|
||||
currentCrossHairScale = currentCrossHairPointerScale = cam == null ? 1.0f : cam.Zoom;
|
||||
if (crosshairSprite != null)
|
||||
{
|
||||
Vector2 aimRefWorldPos = character.AimRefPosition;
|
||||
if (character.Submarine != null) { aimRefWorldPos += character.Submarine.Position; }
|
||||
Vector2 itemPos = cam.WorldToScreen(aimRefWorldPos);
|
||||
float rotation = (item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi;
|
||||
Vector2 barrelDir = new Vector2((float)Math.Cos(rotation), -(float)Math.Sin(rotation));
|
||||
|
||||
Vector2 mouseDiff = itemPos - PlayerInput.MousePosition;
|
||||
crosshairPos = new Vector2(
|
||||
MathHelper.Clamp(itemPos.X + barrelDir.X * mouseDiff.Length(), 0, GameMain.GraphicsWidth),
|
||||
MathHelper.Clamp(itemPos.Y + barrelDir.Y * mouseDiff.Length(), 0, GameMain.GraphicsHeight));
|
||||
|
||||
float spread = GetSpread(character);
|
||||
Projectile projectile = FindProjectile();
|
||||
if (projectile != null)
|
||||
{
|
||||
spread += MathHelper.ToRadians(projectile.Spread);
|
||||
}
|
||||
|
||||
float crossHairDist = Vector2.Distance(item.WorldPosition, cam.ScreenToWorld(crosshairPos));
|
||||
float spreadDist = (float)Math.Sin(spread) * crossHairDist;
|
||||
|
||||
currentCrossHairPointerScale = MathHelper.Clamp(spreadDist / Math.Min(crosshairSprite.size.X, crosshairSprite.size.Y), 0.1f, 10.0f);
|
||||
}
|
||||
currentCrossHairScale *= CrossHairScale;
|
||||
crosshairPointerPos = PlayerInput.MousePosition;
|
||||
}
|
||||
|
||||
public override void DrawHUD(SpriteBatch spriteBatch, Character character)
|
||||
{
|
||||
if (crosshairSprite == null) { return; }
|
||||
if (character == null || !character.IsKeyDown(InputType.Aim)) { return; }
|
||||
|
||||
//camera focused on some other item/device, don't draw the crosshair
|
||||
if (character.ViewTarget != null && (character.ViewTarget is Item item) && item.Prefab.FocusOnSelected) { return; }
|
||||
|
||||
GUI.HideCursor = (crosshairSprite != null || crosshairPointerSprite != null) &&
|
||||
GUI.MouseOn == null && !Inventory.IsMouseOnInventory() && !GameMain.Instance.Paused;
|
||||
if (GUI.HideCursor)
|
||||
{
|
||||
crosshairSprite?.Draw(spriteBatch, crosshairPos, Color.White, 0, currentCrossHairScale);
|
||||
crosshairPointerSprite?.Draw(spriteBatch, crosshairPointerPos, 0, currentCrossHairPointerScale);
|
||||
}
|
||||
}
|
||||
|
||||
partial void LaunchProjSpecific()
|
||||
{
|
||||
Vector2 particlePos = item.WorldPosition + ConvertUnits.ToDisplayUnits(TransformedBarrelPos);
|
||||
float rotation = -item.body.Rotation;
|
||||
if (item.body.Dir < 0.0f) { rotation += MathHelper.Pi; }
|
||||
foreach (ParticleEmitter emitter in particleEmitters)
|
||||
{
|
||||
emitter.Emit(1.0f, particlePos, hullGuess: null, angle: rotation, particleRotation: rotation);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
crosshairSprite?.Remove();
|
||||
crosshairSprite = null;
|
||||
crosshairPointerSprite?.Remove();
|
||||
crosshairSprite = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,530 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Sounds;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
enum SoundSelectionMode
|
||||
{
|
||||
Random,
|
||||
CharacterSpecific,
|
||||
ItemSpecific,
|
||||
All
|
||||
}
|
||||
|
||||
class ItemSound
|
||||
{
|
||||
public readonly RoundSound RoundSound;
|
||||
public readonly ActionType Type;
|
||||
|
||||
public string VolumeProperty;
|
||||
|
||||
public float VolumeMultiplier
|
||||
{
|
||||
get { return RoundSound.Volume; }
|
||||
}
|
||||
|
||||
public float Range
|
||||
{
|
||||
get { return RoundSound.Range; }
|
||||
}
|
||||
|
||||
public readonly bool Loop;
|
||||
|
||||
public ItemSound(RoundSound sound, ActionType type, bool loop = false)
|
||||
{
|
||||
this.RoundSound = sound;
|
||||
this.Type = type;
|
||||
this.Loop = loop;
|
||||
}
|
||||
}
|
||||
|
||||
partial class ItemComponent : ISerializableEntity
|
||||
{
|
||||
public bool HasSounds
|
||||
{
|
||||
get { return sounds.Count > 0; }
|
||||
}
|
||||
|
||||
private bool[] hasSoundsOfType;
|
||||
private Dictionary<ActionType, List<ItemSound>> sounds;
|
||||
private Dictionary<ActionType, SoundSelectionMode> soundSelectionModes;
|
||||
|
||||
public GUILayoutSettings DefaultLayout { get; protected set; }
|
||||
public GUILayoutSettings AlternativeLayout { get; protected set; }
|
||||
|
||||
public class GUILayoutSettings
|
||||
{
|
||||
public Vector2? RelativeSize { get; private set; }
|
||||
public Point? AbsoluteSize { get; private set; }
|
||||
public Vector2? RelativeOffset { get; private set; }
|
||||
public Point? AbsoluteOffset { get; private set; }
|
||||
public Anchor? Anchor { get; private set; }
|
||||
public Pivot? Pivot { get; private set; }
|
||||
|
||||
public static GUILayoutSettings Load(XElement element)
|
||||
{
|
||||
var layout = new GUILayoutSettings();
|
||||
var relativeSize = XMLExtensions.GetAttributeVector2(element, "relativesize", Vector2.Zero);
|
||||
var absoluteSize = XMLExtensions.GetAttributePoint(element, "absolutesize", new Point(-1000, -1000));
|
||||
var relativeOffset = XMLExtensions.GetAttributeVector2(element, "relativeoffset", Vector2.Zero);
|
||||
var absoluteOffset = XMLExtensions.GetAttributePoint(element, "absoluteoffset", new Point(-1000, -1000));
|
||||
if (relativeSize.Length() > 0)
|
||||
{
|
||||
layout.RelativeSize = relativeSize;
|
||||
}
|
||||
if (absoluteSize.X > 0 && absoluteSize.Y > 0)
|
||||
{
|
||||
layout.AbsoluteSize = absoluteSize;
|
||||
}
|
||||
if (relativeOffset.Length() > 0)
|
||||
{
|
||||
layout.RelativeOffset = relativeOffset;
|
||||
}
|
||||
if (absoluteOffset.X > -1000 && absoluteOffset.Y > -1000)
|
||||
{
|
||||
layout.AbsoluteOffset = absoluteOffset;
|
||||
}
|
||||
if (Enum.TryParse(XMLExtensions.GetAttributeString(element, "anchor", ""), out Anchor a))
|
||||
{
|
||||
layout.Anchor = a;
|
||||
}
|
||||
if (Enum.TryParse(XMLExtensions.GetAttributeString(element, "pivot", ""), out Pivot p))
|
||||
{
|
||||
layout.Pivot = p;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
public void ApplyTo(RectTransform target)
|
||||
{
|
||||
if (RelativeOffset.HasValue)
|
||||
{
|
||||
target.RelativeOffset = RelativeOffset.Value;
|
||||
}
|
||||
else if (AbsoluteOffset.HasValue)
|
||||
{
|
||||
target.AbsoluteOffset = AbsoluteOffset.Value;
|
||||
}
|
||||
if (RelativeSize.HasValue)
|
||||
{
|
||||
target.RelativeSize = RelativeSize.Value;
|
||||
}
|
||||
else if (AbsoluteSize.HasValue)
|
||||
{
|
||||
target.NonScaledSize = AbsoluteSize.Value;
|
||||
}
|
||||
if (Anchor.HasValue)
|
||||
{
|
||||
target.Anchor = Anchor.Value;
|
||||
}
|
||||
if (Pivot.HasValue)
|
||||
{
|
||||
target.Pivot = Pivot.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.Pivot = RectTransform.MatchPivotToAnchor(target.Anchor);
|
||||
}
|
||||
target.RecalculateChildren(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
public GUIFrame GuiFrame { get; protected set; }
|
||||
|
||||
[Serialize(false, false)]
|
||||
public bool AllowUIOverlap
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private ItemComponent linkToUIComponent;
|
||||
[Serialize("", false)]
|
||||
public string LinkUIToComponent
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0, false)]
|
||||
public int HudPriority
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private bool useAlternativeLayout;
|
||||
public bool UseAlternativeLayout
|
||||
{
|
||||
get { return useAlternativeLayout; }
|
||||
set
|
||||
{
|
||||
if (AlternativeLayout != null)
|
||||
{
|
||||
if (value == useAlternativeLayout) { return; }
|
||||
useAlternativeLayout = value;
|
||||
if (useAlternativeLayout)
|
||||
{
|
||||
AlternativeLayout?.ApplyTo(GuiFrame.RectTransform);
|
||||
}
|
||||
else
|
||||
{
|
||||
DefaultLayout?.ApplyTo(GuiFrame.RectTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private bool shouldMuffleLooping;
|
||||
private float lastMuffleCheckTime;
|
||||
private ItemSound loopingSound;
|
||||
private SoundChannel loopingSoundChannel;
|
||||
private List<SoundChannel> playingOneshotSoundChannels = new List<SoundChannel>();
|
||||
|
||||
public void UpdateSounds()
|
||||
{
|
||||
if (!isActive || item.Condition <= 0.0f)
|
||||
{
|
||||
StopSounds(ActionType.OnActive);
|
||||
}
|
||||
|
||||
if (loopingSound != null && loopingSoundChannel != null && loopingSoundChannel.IsPlaying)
|
||||
{
|
||||
if (Timing.TotalTime > lastMuffleCheckTime + 0.2f)
|
||||
{
|
||||
shouldMuffleLooping = SoundPlayer.ShouldMuffleSound(Character.Controlled, item.WorldPosition, loopingSound.Range, Character.Controlled?.CurrentHull);
|
||||
lastMuffleCheckTime = (float)Timing.TotalTime;
|
||||
}
|
||||
loopingSoundChannel.Muffled = shouldMuffleLooping;
|
||||
float targetGain = GetSoundVolume(loopingSound);
|
||||
float gainDiff = targetGain - loopingSoundChannel.Gain;
|
||||
loopingSoundChannel.Gain += Math.Abs(gainDiff) < 0.1f ? gainDiff : Math.Sign(gainDiff) * 0.1f;
|
||||
loopingSoundChannel.Position = new Vector3(item.WorldPosition, 0.0f);
|
||||
}
|
||||
for (int i = 0; i < playingOneshotSoundChannels.Count; i++)
|
||||
{
|
||||
if (!playingOneshotSoundChannels[i].IsPlaying)
|
||||
{
|
||||
playingOneshotSoundChannels[i].Dispose();
|
||||
playingOneshotSoundChannels[i] = null;
|
||||
}
|
||||
}
|
||||
playingOneshotSoundChannels.RemoveAll(ch => ch == null);
|
||||
foreach (SoundChannel channel in playingOneshotSoundChannels)
|
||||
{
|
||||
channel.Position = new Vector3(item.WorldPosition, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public void PlaySound(ActionType type, Character user = null)
|
||||
{
|
||||
if (!hasSoundsOfType[(int)type]) { return; }
|
||||
|
||||
if (loopingSound != null)
|
||||
{
|
||||
float targetGain = 0.0f;
|
||||
if (Vector3.DistanceSquared(GameMain.SoundManager.ListenerPosition, new Vector3(item.WorldPosition, 0.0f)) > loopingSound.Range * loopingSound.Range ||
|
||||
(targetGain = GetSoundVolume(loopingSound)) <= 0.0001f)
|
||||
{
|
||||
if (loopingSoundChannel != null)
|
||||
{
|
||||
loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
|
||||
{
|
||||
loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null;
|
||||
}
|
||||
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
|
||||
{
|
||||
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
|
||||
new Vector3(item.WorldPosition, 0.0f),
|
||||
0.01f,
|
||||
SoundPlayer.ShouldMuffleSound(Character.Controlled, item.WorldPosition, loopingSound.Range, Character.Controlled?.CurrentHull));
|
||||
loopingSoundChannel.Looping = true;
|
||||
//TODO: tweak
|
||||
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
|
||||
loopingSoundChannel.Far = loopingSound.Range;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ItemSound itemSound = null;
|
||||
var matchingSounds = sounds[type];
|
||||
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
|
||||
{
|
||||
SoundSelectionMode soundSelectionMode = soundSelectionModes[type];
|
||||
int index;
|
||||
if (soundSelectionMode == SoundSelectionMode.CharacterSpecific && user != null)
|
||||
{
|
||||
index = user.ID % matchingSounds.Count;
|
||||
}
|
||||
else if (soundSelectionMode == SoundSelectionMode.ItemSpecific)
|
||||
{
|
||||
index = item.ID % matchingSounds.Count;
|
||||
}
|
||||
else if (soundSelectionMode == SoundSelectionMode.All)
|
||||
{
|
||||
foreach (ItemSound sound in matchingSounds)
|
||||
{
|
||||
PlaySound(sound, item.WorldPosition, user);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = Rand.Int(matchingSounds.Count);
|
||||
}
|
||||
|
||||
itemSound = matchingSounds[index];
|
||||
PlaySound(matchingSounds[index], item.WorldPosition, user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void PlaySound(ItemSound itemSound, Vector2 position, Character user = null)
|
||||
{
|
||||
if (Vector2.DistanceSquared(new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y), position) > itemSound.Range * itemSound.Range)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemSound.Loop)
|
||||
{
|
||||
loopingSound = itemSound;
|
||||
if (loopingSoundChannel != null && loopingSoundChannel.Sound != loopingSound.RoundSound.Sound)
|
||||
{
|
||||
loopingSoundChannel.FadeOutAndDispose(); loopingSoundChannel = null;
|
||||
}
|
||||
if (loopingSoundChannel == null || !loopingSoundChannel.IsPlaying)
|
||||
{
|
||||
float volume = GetSoundVolume(itemSound);
|
||||
if (volume <= 0.0001f) { return; }
|
||||
loopingSoundChannel = loopingSound.RoundSound.Sound.Play(
|
||||
new Vector3(position.X, position.Y, 0.0f),
|
||||
0.01f,
|
||||
muffle: SoundPlayer.ShouldMuffleSound(Character.Controlled, position, loopingSound.Range, Character.Controlled?.CurrentHull));
|
||||
loopingSoundChannel.Looping = true;
|
||||
//TODO: tweak
|
||||
loopingSoundChannel.Near = loopingSound.Range * 0.4f;
|
||||
loopingSoundChannel.Far = loopingSound.Range;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float volume = GetSoundVolume(itemSound);
|
||||
if (volume <= 0.0001f) { return; }
|
||||
var channel = SoundPlayer.PlaySound(itemSound.RoundSound.Sound, position, volume, itemSound.Range, item.CurrentHull);
|
||||
if (channel != null) { playingOneshotSoundChannels.Add(channel); }
|
||||
}
|
||||
}
|
||||
|
||||
public void StopSounds(ActionType type)
|
||||
{
|
||||
if (loopingSound == null) { return; }
|
||||
|
||||
if (loopingSound.Type != type) { return; }
|
||||
|
||||
if (loopingSoundChannel != null)
|
||||
{
|
||||
loopingSoundChannel.FadeOutAndDispose();
|
||||
loopingSoundChannel = null;
|
||||
loopingSound = null;
|
||||
}
|
||||
}
|
||||
|
||||
private float GetSoundVolume(ItemSound sound)
|
||||
{
|
||||
if (sound == null) { return 0.0f; }
|
||||
if (sound.VolumeProperty == "") { return sound.VolumeMultiplier; }
|
||||
|
||||
if (SerializableProperties.TryGetValue(sound.VolumeProperty, out SerializableProperty property))
|
||||
{
|
||||
float newVolume = 0.0f;
|
||||
try
|
||||
{
|
||||
newVolume = (float)property.GetValue(this);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
newVolume *= sound.VolumeMultiplier;
|
||||
|
||||
if (!MathUtils.IsValid(newVolume))
|
||||
{
|
||||
DebugConsole.Log("Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
"ItemComponent.PlaySound:" + item.Name + GetType().ToString(),
|
||||
GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
|
||||
"Invalid sound volume (item " + item.Name + ", " + GetType().ToString() + "): " + newVolume);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return MathHelper.Clamp(newVolume, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
public virtual bool ShouldDrawHUD(Character character)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public ItemComponent GetLinkUIToComponent()
|
||||
{
|
||||
if (string.IsNullOrEmpty(LinkUIToComponent))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
foreach (ItemComponent component in item.Components)
|
||||
{
|
||||
if (component.name.ToLower() == LinkUIToComponent.ToLower())
|
||||
{
|
||||
linkToUIComponent = component;
|
||||
}
|
||||
}
|
||||
if (linkToUIComponent == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to link the component \"" + Name + "\" to \"" + LinkUIToComponent + "\" in the item \"" + item.Name + "\" - component with a matching name not found.");
|
||||
}
|
||||
return linkToUIComponent;
|
||||
}
|
||||
|
||||
public virtual void DrawHUD(SpriteBatch spriteBatch, Character character) { }
|
||||
|
||||
public virtual void AddToGUIUpdateList()
|
||||
{
|
||||
GuiFrame?.AddToGUIUpdateList();
|
||||
}
|
||||
|
||||
public virtual void UpdateHUD(Character character, float deltaTime, Camera cam) { }
|
||||
|
||||
public virtual void CreateEditingHUD(SerializableEntityEditor editor)
|
||||
{
|
||||
}
|
||||
|
||||
private bool LoadElemProjSpecific(XElement subElement)
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "guiframe":
|
||||
if (subElement.Attribute("rect") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - GUIFrame defined as rect, use RectTransform instead.");
|
||||
break;
|
||||
}
|
||||
|
||||
Color? color = null;
|
||||
if (subElement.Attribute("color") != null) color = subElement.GetAttributeColor("color", Color.White);
|
||||
string style = subElement.Attribute("style") == null ?
|
||||
null : subElement.GetAttributeString("style", "");
|
||||
|
||||
GuiFrame = new GUIFrame(RectTransform.Load(subElement, GUI.Canvas, Anchor.Center), style, color);
|
||||
DefaultLayout = GUILayoutSettings.Load(subElement);
|
||||
break;
|
||||
case "alternativelayout":
|
||||
AlternativeLayout = GUILayoutSettings.Load(subElement);
|
||||
break;
|
||||
case "itemsound":
|
||||
case "sound":
|
||||
string filePath = subElement.GetAttributeString("file", "");
|
||||
|
||||
if (filePath == "") filePath = subElement.GetAttributeString("sound", "");
|
||||
|
||||
if (filePath == "")
|
||||
{
|
||||
DebugConsole.ThrowError("Error when instantiating item \"" + item.Name + "\" - sound with no file path set");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!filePath.Contains("/") && !filePath.Contains("\\") && !filePath.Contains(Path.DirectorySeparatorChar))
|
||||
{
|
||||
filePath = Path.Combine(Path.GetDirectoryName(item.Prefab.FilePath), filePath);
|
||||
}
|
||||
|
||||
ActionType type;
|
||||
try
|
||||
{
|
||||
type = (ActionType)Enum.Parse(typeof(ActionType), subElement.GetAttributeString("type", ""), true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid sound type in " + subElement + "!", e);
|
||||
break;
|
||||
}
|
||||
|
||||
RoundSound sound = Submarine.LoadRoundSound(subElement);
|
||||
if (sound == null) { break; }
|
||||
ItemSound itemSound = new ItemSound(sound, type, subElement.GetAttributeBool("loop", false))
|
||||
{
|
||||
VolumeProperty = subElement.GetAttributeString("volumeproperty", "").ToLowerInvariant()
|
||||
};
|
||||
|
||||
if (soundSelectionModes == null) soundSelectionModes = new Dictionary<ActionType, SoundSelectionMode>();
|
||||
if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] == SoundSelectionMode.Random)
|
||||
{
|
||||
SoundSelectionMode selectionMode = SoundSelectionMode.Random;
|
||||
Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out selectionMode);
|
||||
soundSelectionModes[type] = selectionMode;
|
||||
}
|
||||
|
||||
List<ItemSound> soundList = null;
|
||||
if (!sounds.TryGetValue(itemSound.Type, out soundList))
|
||||
{
|
||||
soundList = new List<ItemSound>();
|
||||
sounds.Add(itemSound.Type, soundList);
|
||||
hasSoundsOfType[(int)itemSound.Type] = true;
|
||||
}
|
||||
|
||||
soundList.Add(itemSound);
|
||||
break;
|
||||
default:
|
||||
return false; //unknown element
|
||||
}
|
||||
return true; //element processed
|
||||
}
|
||||
|
||||
//Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero.
|
||||
protected void StartDelayedCorrection(ServerNetObject type, IReadMessage buffer, float sendingTime, bool waitForMidRoundSync = false)
|
||||
{
|
||||
if (delayedCorrectionCoroutine != null) CoroutineManager.StopCoroutines(delayedCorrectionCoroutine);
|
||||
|
||||
delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime, waitForMidRoundSync));
|
||||
}
|
||||
|
||||
private IEnumerable<object> DoDelayedCorrection(ServerNetObject type, IReadMessage buffer, float sendingTime, bool waitForMidRoundSync)
|
||||
{
|
||||
while (GameMain.Client != null &&
|
||||
(correctionTimer > 0.0f || (waitForMidRoundSync && GameMain.Client.MidRoundSyncing)))
|
||||
{
|
||||
correctionTimer -= CoroutineManager.DeltaTime;
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
if (item.Removed || GameMain.Client == null)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
((IServerSerializable)this).ClientRead(type, buffer, sendingTime);
|
||||
|
||||
correctionTimer = 0.0f;
|
||||
delayedCorrectionCoroutine = null;
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user