(a00338777) v0.9.2.1

This commit is contained in:
Joonas Rikkonen
2019-08-26 19:58:19 +03:00
parent 0f63da27b2
commit 80698b58b0
311 changed files with 11763 additions and 4507 deletions
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<contentpackage name="Vanilla 0.9" path="Data/ContentPackages/Vanilla 0.9" corepackage="true" hideinworkshopmenu="true" gameversion="0.9.0.0">
<Item file="Content/Items/idcard.xml" />
<Item file="Content/Items/Alien/alienitems.xml" />
@@ -82,6 +82,7 @@
<Submarine file="Submarines/Humpback.sub" />
<Submarine file="Submarines/Dugong.sub" />
<Submarine file="Submarines/Venture.sub" />
<Submarine file="Submarines/Berilia.sub" />
<Submarine file="Submarines/Remora.sub" />
<Submarine file="Submarines/RemoraDrone.sub" />
<Text file="Content/Texts/EnglishVanilla.xml" />
@@ -91,6 +92,10 @@
<Text file="Content/Texts/BrazilianPortugueseVanilla.xml" />
<Text file="Content/Texts/SimplifiedChineseVanilla.xml" />
<Text file="Content/Texts/TraditionalChineseVanilla.xml" />
<Text file="Content/Texts/CastilianSpanishVanilla.xml" />
<Text file="Content/Texts/LatinamericanSpanishVanilla.xml" />
<Text file="Content/Texts/PolishVanilla.xml" />
<Text file="Content/Texts/TurkishVanilla.xml" />
<UIStyle file="Content/UI/style.xml"/>
<Afflictions file="Content/Afflictions.xml"/>
<Structure file="Content/Map/StructurePrefabs.xml" />
@@ -104,6 +109,7 @@
<MapGenerationParameters file="Content/Map/MapGenerationParameters.xml" />
<LevelGenerationParameters file="Content/Map/LevelGenerationParameters.xml" />
<Missions file="Content/Missions.xml" />
<TraitorMissions file="Content/TraitorMissions.xml" />
<NPCConversations file="Content/NPCConversations/NpcConversations_English.xml"/>
<NPCConversations file="Content/NPCConversations/NpcConversations_German.xml"/>
<NPCConversations file="Content/NPCConversations/NpcConversations_French.xml"/>
@@ -14,7 +14,7 @@
damageenemykarmaincrease="0.1"
damagefriendlykarmadecrease="0.2"
extinguishfirekarmaincrease="1"
allowedwiredisconnectionsperminute="5"
allowedwiredisconnectionsperminute="3"
wiredisconnectionkarmadecrease="6.0"
steersubkarmaincrease="0.15"
spamfilterkarmadecrease="15"
@@ -35,7 +35,7 @@
damageenemykarmaincrease="0.1"
damagefriendlykarmadecrease="0.3"
extinguishfirekarmaincrease="1"
allowedwiredisconnectionsperminute="4"
allowedwiredisconnectionsperminute="1"
wiredisconnectionkarmadecrease="10.0"
steersubkarmaincrease="0.15"
spamfilterkarmadecrease="25"
@@ -56,7 +56,7 @@
damageenemykarmaincrease="0.1"
damagefriendlykarmadecrease="0.2"
extinguishfirekarmaincrease="1"
allowedwiredisconnectionsperminute="5"
allowedwiredisconnectionsperminute="3"
wiredisconnectionkarmadecrease="6.0"
steersubkarmaincrease="0.15"
spamfilterkarmadecrease="15"
@@ -25,7 +25,13 @@
<Command name="kickid"/>
<Command name="campaigninfo"/>
<Command name="campaigndestination"/>
<Command name="togglekarma"/>
<Command name="setkarma"/>
<Command name="resetkarma"/>
<Command name="showkarma"/>
<Command name="togglekarmatestmode"/>
<Command name="respawnnow"/>
<Command name="traitorlist"/>
</Preset>
<Preset
@@ -77,6 +83,12 @@
<Command name="power"/>
<Command name="oxygen"/>
<Command name="setclientcharacter"/>
<Command name="togglekarma"/>
<Command name="setkarma"/>
<Command name="resetkarma"/>
<Command name="showkarma"/>
<Command name="togglekarmatestmode"/>
<Command name="respawnnow"/>
<Command name="traitorlist"/>
</Preset>
</PermissionPresets>
Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

@@ -0,0 +1,23 @@
<CrawlerRun
movementspeed="5.024437"
stepsize="0.3643546,0.2438424"
legtorque="0"
colliderstandangle="90"
flip="True"
headposition="0.7865055"
torsoposition="0.9286075"
headangle="-9.504209"
torsoangle="2.798656"
footmoveforce="25"
torsomoveforce="15"
torsotorque="100"
headmoveforce="10"
headtorque="60"
footangle="-15.42238"
backwardsmovementmultiplier="0.75"
cyclespeed="2.031435"
animationtype="Run"
foottorque="25"
footangles="10: 0.01250029"
tailtorque="50"
tailangle="10.48347" />
@@ -0,0 +1,20 @@
<CrawlerSwimFast
movementspeed="7.5"
steertorque="15"
waveamplitude="0.09180977"
wavelength="22.92186"
flip="True"
headangle="77.22551"
torsoangle="80.92885"
mirror="True"
rotatetowardsmovement="True"
torsotorque="25"
headtorque="25"
foottorque="15"
tailtorque="250"
footangle="0"
cyclespeed="5"
animationtype="SwimFast"
footangles="10: 1.580441"
usesinemovement="False"
tailangle="NaN" />
@@ -0,0 +1,20 @@
<CrawlerSwimSlow
movementspeed="4"
steertorque="20"
waveamplitude="0.1983792"
wavelength="21.09184"
flip="True"
headangle="87.16887"
torsoangle="96.21252"
mirror="True"
rotatetowardsmovement="True"
torsotorque="25"
headtorque="10"
foottorque="5"
tailtorque="50"
footangle="0"
cyclespeed="5"
animationtype="SwimSlow"
footangles="10: 1.567869"
usesinemovement="False"
tailangle="NaN" />
@@ -0,0 +1,23 @@
<CrawlerWalk
movementspeed="2.034052"
stepsize="0.2436086,0.05675382"
legtorque="0"
colliderstandangle="90"
flip="True"
headposition="0.6852945"
torsoposition="0.8662814"
headangle="10.37946"
torsoangle="23.53741"
footmoveforce="10"
torsomoveforce="10"
torsotorque="50"
headmoveforce="10"
headtorque="75"
footangle="-36.14369"
backwardsmovementmultiplier="0.75"
cyclespeed="2.883953"
animationtype="Walk"
foottorque="50"
footangles="10: -0.1484762"
tailtorque="50"
tailangle="94.89201" />
@@ -0,0 +1,116 @@
<Ragdoll type="redcrawler" colliderheightfromfloor="40" impacttolerance="15" draggable="True" limbscale="0.7" jointscale="0.7" canentersubmarine="True" texturescale="1" spritesheetorientation="90">
<collider height="80" radius="40" name="Main Collider" width="0" />
<!-- Head -->
<limb id="0" radius="50" type="Head" flip="True" steerforce="15" mouthpos="50,0" healthindex="0" attackpriority="0" stepoffset="0,0" bodytype="Dynamic" height="0" width="0" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" ignorecollisions="False" name="Limb 0" notes="">
<deformablesprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="153,2,102,82" depth="0.1" origin="0.5238374,0.4827269" subdivisions="3,3">
<spritedeformation type="noise" resolution="3,3" frequency="2" amplitude="0.05" changespeed="2" blendmode="add" stopwhenhostisdead="true"/>
<SpriteDeformation type="custom" resolution="3,3" frequency="0.0" blendmode="multiply" row0="0,0 0,0 0,0" row1="0,0 0,0 1,1" row2="0,0 1,2 1,2" />
</deformablesprite>
<attack context="Water" cooldown="5" range="80" damagerange="70" duration="0.25" stun="0.3" structuredamage="3" itemdamage="10" structuresoundtype="StructureSlash" targetimpulse="20" targetimpulseworld="0,-20" severlimbsprobability="0.5" force="5" applyforceonlimbs="0, 9" torque="50" hitdetectiontype="Distance" onlyhumans="False" targetforce="0" targetforceworld="0,0" priority="0" targettype="Any" secondarycooldown="0" applyforcesonlyonce="False" stickchance="0" cooldownrandomfactor="0.25" afterattack="FallBack" reverse="False">
<Affliction identifier="bleeding" strength="30" />
<Affliction identifier="stun" strength="0.25" />
<Affliction identifier="internaldamage" strength="10" />
</attack>
</limb>
<!-- BodyUpper (main torso) -->
<limb id="1" width="50" height="0" type="Torso" flip="True" steerforce="0.1" healthindex="0" attackpriority="0" stepoffset="0,0" bodytype="Dynamic" radius="35" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" ignorecollisions="False" name="Limb 1" notes="">
<deformablesprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="36,0,115,113" depth="0.2" origin="0.4606622,0.4951695" subdivisions="5,5">
<SpriteDeformation type="inflate" resolution="5,5" frequency="2" scale="0.2" sync="1" stopwhenhostisdead="true"/>
<SpriteDeformation type="custom" resolution="5,5" frequency="0.0" blendmode="multiply" row0="0,0 0.5,1 1,1 0.5,1 0,0" row1="0,0 0.5,1 1,1 0.5,1 0,0" row2="0,0 0.5,1 1,1 0.5,1 0,0" row3="0,0 0.5,1 1,1 0.5,1 0,0" row4="0,0 0.5,1 1,1 0.5,1 0,0" />
<spritedeformation type="bendjoint" resolution="5,5" blendmode="add" />
</deformablesprite>
</limb>
<!-- BodyLower1 -->
<limb id="2" width="80" height="70" type="Tail" flip="True" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="0" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" ignorecollisions="False" name="Limb 2" notes="">
<deformablesprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="167,173,89,83" depth="0.21" origin="0.5135868,0.5523771" subdivisions="5,5">
<SpriteDeformation type="inflate" resolution="5,5" frequency="2" scale="0.2" sync="1" stopwhenhostisdead="true"/>
<SpriteDeformation type="custom" resolution="5,5" frequency="0.0" blendmode="multiply" row0="0,0 0.5,1 1,1 0.5,1 0,0" row1="0,0 0.5,1 1,1 0.5,1 0,0" row2="0,0 0.5,1 1,1 0.5,1 0,0" row3="0,0 0.5,1 1,1 0.5,1 0,0" row4="0,0 0.5,1 1,1 0.5,1 0,0" />
<spritedeformation type="bendjoint" resolution="5,5" blendmode="add" />
</deformablesprite>
</limb>
<!-- BodyLower2 -->
<limb id="3" width="62" height="52" type="Tail" flip="True" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="0" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" ignorecollisions="False" name="Limb 3" notes="">
<deformablesprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="71,179,84,78" depth="0.22" origin="0.5427743,0.5" subdivisions="5,5">
<spritedeformation type="bendjoint" resolution="5,5" />
</deformablesprite>
</limb>
<!-- BodyLower3 -->
<limb id="4" width="45" height="33" type="Tail" flip="True" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="0" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" ignorecollisions="False" name="Limb 4" notes="">
<deformablesprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="97,113,63,57" depth="0.023" origin="0.5571429,0.4577771" subdivisions="5,5">
<spritedeformation type="bendjoint" resolution="5,5" />
</deformablesprite>
</limb>
<!-- BodyLower4 -->
<limb id="5" width="30" height="0" type="Tail" flip="True" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="12" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" ignorecollisions="False" name="Limb 5" notes="">
<deformablesprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="0,135,48,36" depth="0.24" origin="0.524421,0.5587717" subdivisions="5,5">
<spritedeformation type="bendjoint" resolution="5,5" />
</deformablesprite>
</limb>
<!-- BodyLower5 -->
<limb id="6" width="30" height="0" type="Tail" flip="True" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="8" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" ignorecollisions="False" name="Limb 6" notes="">
<deformablesprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="50,114,44,28" depth="0.025" origin="0.5984064,0.5294023" subdivisions="5,5">
<spritedeformation type="bendjoint" resolution="5,5" />
</deformablesprite>
</limb>
<!-- BodyLower6 -->
<limb id="7" width="53" height="10" type="Tail" flip="True" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="0" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" ignorecollisions="False" name="Limb 7" notes="">
<deformablesprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="47,142,48,26" depth="0.26" origin="0.5339575,0.464236" subdivisions="5,5">
<spritedeformation type="bendjoint" resolution="5,5" />
</deformablesprite>
</limb>
<!-- Jaw -->
<limb id="8" width="42" height="44" ignorecollisions="True" flip="True" type="None" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="0" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" name="Limb 8" notes="">
<sprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="161,83,44,47" depth="0.06" origin="0.3677241,0.5077608" />
<attack context="Ground" cooldown="3" range="120" damagerange="70" duration="0.25" stun="0.3" structuredamage="3" itemdamage="10" structuresoundtype="StructureSlash" targetimpulse="20" targetimpulseworld="0,-20" severlimbsprobability="0.5" force="20" applyforceonlimbs="0, 1, 9" torque="50" hitdetectiontype="Distance" onlyhumans="False" targetforce="0" targetforceworld="0,0" priority="0" targettype="Any" secondarycooldown="0" applyforcesonlyonce="False" stickchance="0" cooldownrandomfactor="0.25" afterattack="FallBack" reverse="False">
<Affliction identifier="bleeding" strength="30" />
<Affliction identifier="stun" strength="0.25" />
<Affliction identifier="internaldamage" strength="10" />
</attack>
</limb>
<!-- ArmUpper -->
<limb id="9" width="0" height="0" type="RightLeg" flip="True" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="30" mass="0" friction="0.3" restitution="0.05" density="5" pullpos="0,0" refjoint="-1" ignorecollisions="False" name="Limb 9" notes="">
<sprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="0,170,70,87" depth="0.11" origin="0.4472283,0.4274753" />
</limb>
<!-- ArmLower -->
<limb id="10" width="60" height="0" type="RightFoot" flip="True" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="10" mass="0" friction="0.3" restitution="0.05" density="10" pullpos="16.03577,-9.425465" refjoint="8" ignorecollisions="False" name="Limb 10" notes="">
<sprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="160,130,95,43" depth="0.1" origin="0.5264239,0.3615547" />
<sound tag="footstep_crawler" />
</limb>
<!-- FakeFoot1 -->
<limb id="11" width="38" height="50" ignorecollisions="True" flip="True" mass="0.5" type="None" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" name="Limb 11" notes="">
<sprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="0,0,36,50" depth="0.1" origin="0.4447618,0.4988151" />
</limb>
<!-- FakeFoot2 -->
<limb id="12" width="30" height="51" ignorecollisions="True" flip="True" mass="0.5" type="None" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" name="Limb 12" notes="">
<sprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="0,50,35,51" depth="0.1" origin="0.3939222,0.4675192" />
</limb>
<!-- FakeFoot3 -->
<limb id="13" width="21" height="22" ignorecollisions="True" flip="True" mass="0.5" type="None" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" name="Limb 13" notes="">
<sprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="0,101,21,22" depth="0.1" origin="0.4972225,0.4925883" />
</limb>
<!-- NeckFur -->
<limb id="14" width="45" height="45" ignorecollisions="True" flip="True" mass="0" type="None" healthindex="0" attackpriority="0" steerforce="0" stepoffset="0,0" bodytype="Dynamic" radius="0" friction="0.3" restitution="0.05" density="10" pullpos="0,0" refjoint="-1" name="NeckFur" notes="">
<sprite texture="Mods/ExampleMod/Redcrawler/crawler.png" sourcerect="205,87,48,43" depth="0.4" origin="0.5741225,0.4759069" />
</limb>
<!-- Joints -->
<!--Head to Torso-->
<joint limb1="0" limb2="1" limb1anchor="-25.35398,-8.442444" limb2anchor="52.93914,-5.098359" canbesevered="True" limitenabled="True" upperlimit="46.14165" lowerlimit="-25.10567" name="Joint 0 - 1" stiffness="0.25" />
<!--Torso to Tail-->
<joint limb1="1" limb2="2" limb1anchor="-19.01174,-0.5916808" limb2anchor="28.40073,3.892834" canbesevered="True" limitenabled="True" upperlimit="8.367105" lowerlimit="-66.20706" name="Joint 1 - 2" stiffness="0.25" />
<joint limb1="2" limb2="3" limb1anchor="-29.70846,-2.359332" limb2anchor="29.08622,-2.871607" canbesevered="True" limitenabled="True" upperlimit="45" lowerlimit="-30" name="Joint 2 - 3" stiffness="0.25" />
<joint limb1="3" limb2="4" limb1anchor="-28.35653,-4.043511" limb2anchor="17.33471,5.577527" canbesevered="True" limitenabled="True" upperlimit="45" lowerlimit="-30" name="Joint 3 - 4" stiffness="0.25" />
<joint limb1="4" limb2="5" limb1anchor="-27.54687,-3.155236" limb2anchor="15.81753,1.520584" canbesevered="True" limitenabled="True" upperlimit="45" lowerlimit="-30.02529" name="Joint 4 - 5" stiffness="0.25" />
<joint limb1="5" limb2="6" limb1anchor="-15.89361,-2.675064" limb2anchor="13.1668,-2.157746" canbesevered="True" limitenabled="True" upperlimit="45" lowerlimit="-29.95912" name="Joint 5 - 6" stiffness="0.25" />
<joint limb1="6" limb2="7" limb1anchor="-21.97209,2.52049" limb2anchor="12.25132,0.1370308" canbesevered="True" limitenabled="True" upperlimit="45" lowerlimit="-30" name="Joint 6 - 7" stiffness="0.25" />
<!--Head To Jaw-->
<joint limb1="0" limb2="8" limb1anchor="-13.74361,-17.22302" limb2anchor="0.8587904,16.14077" canbesevered="True" limitenabled="True" upperlimit="23.76226" lowerlimit="-21.58732" name="Joint 0 - 8" stiffness="0.25" />
<!--Shoulder-->
<joint limb1="1" limb2="9" limb1anchor="15.05702,-15.9578" limb2anchor="0.8482082,21.33283" canbesevered="True" limitenabled="True" upperlimit="41.20124" lowerlimit="-57.19871" name="Joint 1 - 9" stiffness="0.25" />
<!--Elbow-->
<joint limb1="9" limb2="10" limb1anchor="-4.516037,-28.62164" limb2anchor="-44.07518,9.280527" canbesevered="True" limitenabled="True" upperlimit="97.67264" lowerlimit="9.157016" name="Joint 9 - 10" stiffness="0.1" />
<!--FakeFeet-->
<joint limb1="2" limb2="11" limb1anchor="-1.630605,-27.50153" limb2anchor="5.272902,20.79574" canbesevered="True" limitenabled="True" upperlimit="55.2636" lowerlimit="-71.75" name="Joint 2 - 11" stiffness="0.25" />
<joint limb1="3" limb2="12" limb1anchor="12.48289,-22.7742" limb2anchor="10.91163,18.78148" canbesevered="True" limitenabled="True" upperlimit="30" lowerlimit="-57.98726" name="Joint 3 - 12" stiffness="0.25" />
<joint limb1="4" limb2="13" limb1anchor="-1.576591,-8.262894" limb2anchor="3.830437,6.737895" canbesevered="True" limitenabled="True" upperlimit="56.54782" lowerlimit="-30" name="Joint 4 - 13" stiffness="0.25" />
<joint limb1="1" limb2="14" limb1anchor="37.08093,-27.01655" limb2anchor="0.9172598,0.375894" canbesevered="True" limitenabled="True" upperlimit="7.579085" lowerlimit="3.64738" name="Joint 1 - 14" stiffness="0.25" />
</Ragdoll>
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8" ?>
<Character name="RedCrawler" humanoid="false" blooddecal="blood">
<ragdolls folder="default"/>
<animations folder="default"/>
<bloodemitter particle="blood" particleamount="10"/>
<bloodemitter particle="waterblood" particleamount="1"/>
<gibemitter particle="gib" particleamount="20" velocitymin="200" velocitymax="700" anglemin="0" anglemax="360"/>
<gibemitter particle="heavygib" particleamount="10" velocitymin="50" velocitymax="500" anglemin="0" anglemax="360"/>
<health
vitality="200"
doesbleed="true"/>
<sound file="Content/Characters/Crawler/CRAWLER_attack1.ogg" state="Attack" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_attack2.ogg" state="Attack" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_attack3.ogg" state="Attack" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_attack4.ogg" state="Attack" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_idle1.ogg" state="Idle" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_idle2.ogg" state="Idle" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_idle3.ogg" state="Idle" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_death1.ogg" state="Die" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_death2.ogg" state="Die" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_damage1.ogg" state="Damage" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_damage2.ogg" state="Damage" range="1000"/>
<sound file="Content/Characters/Crawler/CRAWLER_damage3.ogg" state="Damage" range="1000"/>
<Inventory slots="Any, Any" accessiblewhenalive="false">
<Item identifier="alienblood"/>
</Inventory>
<ai
combatstrength="300"
sight="0.5"
hearing="1.0"
fleehealththreshold="10"
aggressiveboarding="true"
aggressiongreed="10"
aggressionhurt="100">
<targetpriority tag="decoy" state="Attack" priority="600"/>
<targetpriority tag="stronger" state="Escape" priority="300"/>
<targetpriority tag="provocative" state="Attack" priority="250"/>
<targetpriority tag="light" state="Attack" priority="200"/>
<targetpriority tag="dead" state="Eat" priority="190"/>
<targetpriority tag="human" state="Attack" priority="150"/>
<targetpriority tag="wall" state="Attack" priority="100"/>
<targetpriority tag="nasonov" state="Attack" priority="70"/>
<targetpriority tag="sonar" state="Attack" priority="60"/>
<targetpriority tag="door" state="Attack" priority="40"/>
<targetpriority tag="weaker" state="Attack" priority="30"/>
<targetpriority tag="room" state="Attack" priority="20"/>
<latchonto
attachtowalls="true"
attachtosub="true"
mindeattachspeed="3.0"
maxdeattachspeed="10.0"
damageondetach="1000"
detachstun="5.0"
attachlimb="Head"
localattachpos="40,10"
attachlimbrotation="100"/>
<SwarmBehavior
mindistfromclosest="200"
maxdistfromcenter="1000"
cohesion="0.1"/>
</ai>
</Character>
Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<contentpackage name="ExampleMod" path="Mods\ExampleMod\filelist.xml" corepackage="false" gameversion="0.9.1.1">
<Submarine file="Mods\ExampleMod\Humpback2.sub" />
<Character file="Mods\ExampleMod\Redcrawler\Redcrawler.xml" />
</contentpackage>
+13 -191
View File
@@ -1,17 +1,14 @@
------------------------------------------------------------------------
Modding info/tips:
General
------------------------------------------------------------------------
When doing an automatic update through the launcher, any old/unnecessary
files in the Content folder will be deleted. This also includes any new
files you may have added to the folder.
This folder is the place where mods installed from the Steam Workshop go.
You should also place your own mods in this folder, under a separate
subfolder (e.g. "Mods/MyMod").
It is recommended to save all modifications to the ''Mods'' folder, or
in the case of custom subs, to the 'Submarines'' folder.
----------------------------------
------------------------------------------------------------------------
Content Packages:
----------------------------------
------------------------------------------------------------------------
Content packages determine which configuration files the game will be using.
This includes the configuration files for items, map structures, characters,
@@ -20,19 +17,18 @@ Content Packages:
In the multiplayer mode, players are required to use the same content package
as the server or they won't be able to join.
If you are making a mod for the game, it is recommended to create a new content
package instead of just replacing existing files in the content folder.
This way you and anyone else using the mod can easily change between different
mods and the vanilla version, without having to manually replace files in the
Content folder or keep backups of different files.
All mods published in the Steam Workshop need a content package.
The content packages are configured in XML files which are saved in Data\ContentPackages.
If you just want to publish a custom submarine in the workshop, you
don't need to worry about content packages - you can just select the
submarine from the "Publish item" tab in the Workshop menu, and the
game automatically creates a folder and content package for your mod.
Example:
A very simple content package could be configured as follows:
<contentpackage name="BestModEver" path="Data/ContentPackages/BestModEver.xml" gameversion="0.9.0.0">
<contentpackage name="BestModEver" path="Mods/BestModEver/BestModEver.xml" gameversion="0.9.1.0" corepackage="true">
<Item file="Mods/BestModEver/items.xml" />
<Character file="Mods/BestModEver/Human/Human.xml" />
<Character file="Mods/BestModEver/Cthulhu/cthulhu.xml" />
@@ -50,178 +46,4 @@ Example:
a new event that spawns Cthulhu and removing the events that spawn monsters/items
which aren't included in the mod.
It is also set to be used with the version 0.9.0.0 of the game.
----------------------------------
Creature modding:
----------------------------------
All the creatures/characters in the game are configured in XML files.
The XML files should be formatted in the following way:
<character name="something">
<ragdoll attribute1="something" attribute2="something">
<limb id = 0>
<sprite texture="texturepath" sourcerect="0,0,width,height"/>
<attack type="something"/>
</limb>
<limb id = 1>
<sprite texture="texturepath" sourcerect="0,0,width,height"/>
</limb>
<joint limb1="0" limb1anchor="x,y" limb2="1" limb2anchor="x,y"/>
</ragdoll>
<ai attribute1="something" attribute2="something"/>
</character>
------------------------------------------
Elements:
Character:
- has to be the root element of the file
Attributes:
name: the name of the creature
humanoid: true/false, if set to true the character is animated using
a bipedal animator (like humans)
needsair: true/false, does the character drown/suffocate without
oxygen (false by default)
drowningtime: how fast the character drowns (in seconds)
doesbleed: if set to false, the character takes no bleeding damage
health: self explanatory (100.0 by default)
Ragdoll:
- has to be a child element of the character element
Attributes:
headposition: how high from the ground the head of the character should
be when the character is standing (50.0 by default)
headangle: an angle which the head is rotated to when the character is walking
(0.0 by default, meaning that the head will face straight forward)
torsoposition: same as headposition but for torso (50.0 by default)
torsoangle: an angle which the torso is rotated to when the character is walking
(0.0 by default, meaning that it will face straight forward)
waveamplitude, wavelength: if the character is not a humanoid, it will
do a "wave-like" swimming movement with the selected amplitude and
wavelength. To put it simply, amplitude affects how large up-and-down
movement the character will do and wavelength affects how fast the
character does the movement (both 0.0 by default)
flip: should the entire character be "mirrored" over the y-axis when it
switches its movement direction from left to right or vice versa,
or should it just rotate along the z-axis (false by default)
walkspeed, swimspeed: how fast the character should move on land and
in water, (the actual speeds are also affected by the weight, shape
and steerforces of individual limbs) (both 1.0 by default)
swimspeed: how fast the character should move in water
Limb:
- an individual part of the ragdoll
- has to be a child element of the ragdoll element
Attributes:
id: an integer that is used to distinguish between limbs when connecting them
with joints. The first limb should have the id "0", the second "1" and so on.
radius, width, height: used for setting the dimensions of the physics body
of the limb. If only radius is set, the limb will be a circle with
the selected radius. If width and height are set, it will be a rectangle.
If radius and height are set, it will be a capsule.
density: the mass of the limb will be area_of_the_limb * density (default 10.0)
friction: the friction coefficient of the limb (0.3 by default)
flip: true/false, if set to true the limb will be "flipped" from one side
to another when the character turns around (as in, if a character is
facing left and has an arm extended left, the arm will be extended
to the right when the character faces to the right) (false by default)
ignorecollisions: true/false, should the limb collide with walls (true by default)
impacttolerance: if the limb receives an impact larger than this value, it takes
damage (20.0 by default)
type: determines how the limb should be animated and what kind of items can be
equipped on the limb. Can be set to None, LeftHand, RightHand, LeftArm,
RightArm, LeftLeg, RightLeg, LeftFoot, RightFoot, Head, Torso, Waist, Tail,
Legs, RightThigh or LeftThigh
pullpos: when animating the character, forces will be applied to this
point of the physics body of the limb. Defaults to "0.0, 0.0" which
is the center of the limb.
refjoint: index of the joint that is used as the "center point" along
the x-axis when doing a walking animation. For example, if the joint
between a characters thigh and waist is set as refjoint, the feet of the
character will be moved directly under said joint when the character is
standing still.
steerforce: how much force is applied to the limb when the character moves (0.0 by default)
armorsector: an armored sector between two angles (in degrees). For example,
-90,90 would make the front sector of the limb armored (0.0,0.0 by default)
armor: how effective the armor is: damage is divided by this value if an attack hits the
armored sector (1.0 by default)
Sprite:
- a child element of a limb element
Attributes:
texture: file path of the texture
sourcerect: which part of the texture should be used. If either width or
height are 0, they will be set to the width or height of the texture.
(0,0,0,0 by default)
origin: what point in the sprite is considered the "middle point". "0,0"
is the upper left corner of the sprite and "1,1" the lower right
corner. ("0.5, 0.5" by default)
depth: Affects the order which sprites are drawn in. Sprites with a
depth of 1.0 will be drawn under sprites that have the depth set
to 0.9 for example. Note that setting several limbs to the same
depth value may cause them to "flicker" on top of each other, so
it's recommended that every sprite has a slightly different depth.
Attack:
- a child element of a limb element
Attributes:
type: affects the logic for moving the attacking limb. At the moment the only
types are None, PinchCW and PinchCW
PinchCW: the limb rotates clockwise when attacking (or counter-clockwise
if the character is facing left). Useful for attacks like biting
or slashing
PinchCCW: the same as PinchCW, but in the limb is rotated in the
opposite direction
Hit: the limb will "punch" the target
damage: damage done to other characters (0.0 by default)
bleedingdamage: how much the attack affects the bleeding rate (0.0 by default)
structuredamage: damage done to structures (0.0 by default)
stun: how long the target is stunned (in seconds, 0.0 by default)
range: how close the limb doing the attack has to be to the target to do damage
(0.0 by default, but should be set to a higher value or otherwise it
will only do damage if the limb is exactly at the position of the target,
so practically never)
duration: how long the attack lasts - if set to zero, it will be a "one-hit"
attack, otherwise it will be active for a while and the damage values
will be damage per second
priority: can be used for adjusting how likely the character is to use specific
attacks. For example, if a character has two attacks, first one
having the priority 2.0 and the second 1.0, the character is twice as
likely to use the first one.
Joint:
- a revolute joint connecting two limbs to each other
- a child element of the ragdoll element
Attributes:
limb1, limb2: thes id of the limbs that should be connected
limb1anchor, limb2anchor: the points where the joint is attached to on
the limbs (0.0, 0.0 being the center)
lowerlimit, upperlimit: how much the joint can turn. If both are set to 0.0,
the joint can rotate freely.
----------------------------------
Editing items:
----------------------------------
(A more extensive tutorial coming up in the future)
Items are also configured in XML files. An item consist of several ''components''
which determine the functionality of the item. See the existing item files for
examples on the components.
TO BE CONTINUED
It is also set to be used with the version 0.9.1.0 of the game.
@@ -235,6 +235,13 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetIdUtils.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\NetworkMember.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\OrderChatMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Enums.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Message\IReadMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Message\IWriteMessage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\NetworkConnection\NetworkConnection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\NetworkConnection\LidgrenConnection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\Message\Message.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\Primitives\NetworkConnection\SteamP2PConnection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\RespawnManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ServerLog.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Networking\ServerSettings.cs" />
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ShowAllFiles>true</ShowAllFiles>
<ShowAllFiles>false</ShowAllFiles>
</PropertyGroup>
</Project>
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
@@ -400,12 +400,18 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Door\door2.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\FF_junctionboxAdditive.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\Lamps.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Electricity\LampsAdditive.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Jobgear\AlienGear.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Jobgear\Assistant\assistant_gear.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -520,6 +526,9 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Map\SubRearBowBack.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\DecorativeVentilation.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\Zones\Ice\Zone4BaseTexture.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -614,6 +623,9 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\BrazilianPortugueseVanilla.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\CastilianSpanishVanilla.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\Credits.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -623,6 +635,12 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\GermanVanilla.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\LatinamericanSpanishVanilla.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\PolishVanilla.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\RussianVanilla.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -632,6 +650,9 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\TraditionalChineseVanilla.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Texts\TurkishVanilla.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Tutorials\TutorialVideos\tutorial_command.mp4">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -689,6 +710,33 @@
<Content Include="$(MSBuildThisFileDirectory)Data\karmasettings.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\filelist.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\PreviewImage.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\Redcrawler\Animations\RedcrawlerRun.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\Redcrawler\Animations\RedcrawlerSwimFast.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\Redcrawler\Animations\RedcrawlerSwimSlow.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\Redcrawler\Animations\RedcrawlerWalk.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\Redcrawler\crawler.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\Redcrawler\Ragdolls\RedcrawlerDefaultRagdoll.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\Redcrawler\Redcrawler.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="$(MSBuildThisFileDirectory)Concentus_LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -2126,6 +2174,9 @@
<None Include="$(MSBuildThisFileDirectory)Content\Items\Command\SonarPingFar.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Misc\AccordionLoop.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Items\Misc\GuitarClown.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -2354,6 +2405,9 @@
<None Include="$(MSBuildThisFileDirectory)Content\Tutorials\TutorialOutpost.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Mods\ExampleMod\Humpback2.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)MonoKickstart_LICENSE">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -2361,11 +2415,11 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Berilia.sub">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\Dugong_Tutorial.sub">
<None Include="$(MSBuildThisFileDirectory)Content\TraitorMissions.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Submarines\TutorialOutpost.sub" />
</ItemGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)Content\Characters\Carrier\CARRIER_alarmLoop.ogg">
@@ -3539,4 +3593,4 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
</Project>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ShowAllFiles>true</ShowAllFiles>
<ShowAllFiles>false</ShowAllFiles>
</PropertyGroup>
</Project>
@@ -14,11 +14,6 @@ namespace Barotrauma
get;
private set;
}
/// <summary>
/// Use as a minimum or static sight range.
/// </summary>
public static float StaticSightRange = 3000;
private float soundRange;
private float sightRange;
@@ -75,7 +70,7 @@ namespace Barotrauma
public bool Enabled = true;
public float MinSoundRange, MinSightRange;
public float MaxSoundRange = float.MaxValue, MaxSightRange = float.MaxValue;
public float MaxSoundRange = 100000, MaxSightRange = 100000;
public TargetType Type { get; private set; }
@@ -128,8 +123,8 @@ namespace Barotrauma
{
SightRange = element.GetAttributeFloat("sightrange", 0.0f);
SoundRange = element.GetAttributeFloat("soundrange", 0.0f);
MinSightRange = element.GetAttributeFloat("minsightrange", SightRange);
MinSoundRange = element.GetAttributeFloat("minsoundrange", SoundRange);
MinSightRange = element.GetAttributeFloat("minsightrange", 0f);
MinSoundRange = element.GetAttributeFloat("minsoundrange", 0f);
MaxSightRange = element.GetAttributeFloat("maxsightrange", SightRange);
MaxSoundRange = element.GetAttributeFloat("maxsoundrange", SoundRange);
FadeOutTime = element.GetAttributeFloat("fadeouttime", FadeOutTime);
@@ -142,15 +137,9 @@ namespace Barotrauma
}
}
public AITarget(Entity e, float sightRange = -1, float soundRange = 0)
public AITarget(Entity e)
{
Entity = e;
if (sightRange < 0)
{
sightRange = StaticSightRange;
}
SightRange = sightRange;
SoundRange = soundRange;
List.Add(this);
}
@@ -109,9 +109,9 @@ namespace Barotrauma
private Dictionary<AITarget, AITargetMemory> targetMemories;
//the eyesight of the NPC (0.0 = blind, 1.0 = sees every target within sightRange)
private float sight;
public float sight;
//how far the NPC can hear targets from (0.0 = deaf, 1.0 = hears every target within soundRange)
private float hearing;
public float hearing;
private float colliderSize;
@@ -270,11 +270,14 @@ namespace Barotrauma
return null;
}
public override void SelectTarget(AITarget target)
public override void SelectTarget(AITarget target) => SelectTarget(target, 100);
public void SelectTarget(AITarget target, float priority)
{
SelectedAiTarget = target;
selectedTargetMemory = GetTargetMemory(target);
targetValue = 100.0f;
selectedTargetMemory.Priority = priority;
targetValue = priority;
}
public override void Update(float deltaTime)
@@ -985,7 +988,7 @@ namespace Barotrauma
var aiTarget = wallTarget.Structure.AiTarget;
if (aiTarget != null && SelectedAiTarget != aiTarget)
{
SelectTarget(aiTarget);
SelectTarget(aiTarget, GetTargetMemory(SelectedAiTarget).Priority);
}
}
if (SelectedAiTarget.Entity is IDamageable damageTarget)
@@ -370,7 +370,7 @@ namespace Barotrauma
if (item.CurrentHull != hull) { continue; }
if (AIObjectiveRepairItems.IsValidTarget(item, Character))
{
if (item.Repairables.All(r => item.Condition > r.ShowRepairUIThreshold)) { continue; }
if (item.Repairables.All(r => item.ConditionPercentage > r.ShowRepairUIThreshold)) { continue; }
AddTargets<AIObjectiveRepairItems, Item>(Character, item);
if (newOrder == null)
{
@@ -640,7 +640,7 @@ namespace Barotrauma
if (item.CurrentHull != hull) { continue; }
if (AIObjectiveRepairItems.IsValidTarget(item, character))
{
if (item.Repairables.All(r => item.Condition > r.ShowRepairUIThreshold)) { continue; }
if (item.Repairables.All(r => item.ConditionPercentage > r.ShowRepairUIThreshold)) { continue; }
AddTargets<AIObjectiveRepairItems, Item>(character, item);
}
}
@@ -142,6 +142,9 @@ namespace Barotrauma
IsPathDirty = false;
}
public Func<PathNode, bool> startNodeFilter;
public Func<PathNode, bool> endNodeFilter;
protected override Vector2 DoSteeringSeek(Vector2 target, float weight)
{
bool needsNewPath = currentPath != null && currentPath.Unreachable || Vector2.DistanceSquared(target, currentTarget) > 1;
@@ -164,7 +167,7 @@ namespace Barotrauma
}
}
var newPath = pathFinder.FindPath(pos, target, character.Submarine, "(Character: " + character.Name + ")");
var newPath = pathFinder.FindPath(pos, target, character.Submarine, "(Character: " + character.Name + ")", startNodeFilter, endNodeFilter);
bool useNewPath = currentPath == null || needsNewPath;
if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable)
{
@@ -14,7 +14,9 @@ namespace Barotrauma
private float waitUntilPathUnreachable;
private bool getDivingGearIfNeeded;
public Func<bool> customCondition;
public Func<bool> requiredCondition;
public Func<PathNode, bool> startNodeFilter;
public Func<PathNode, bool> endNodeFilter;
public bool followControlledCharacter;
public bool mimic;
@@ -137,7 +139,12 @@ namespace Barotrauma
currTargetSimPos -= diff;
}
}
character.AIController.SteeringManager.SteeringSeek(currTargetSimPos);
if (PathSteering != null)
{
PathSteering.startNodeFilter = startNodeFilter;
PathSteering.endNodeFilter = endNodeFilter;
}
SteeringManager.SteeringSeek(currTargetSimPos);
if (SteeringManager != PathSteering)
{
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: 5, weight: 1, heading: VectorExtensions.Forward(character.AnimController.Collider.Rotation));
@@ -191,7 +198,7 @@ namespace Barotrauma
}
else if (closeEnough)
{
if (customCondition == null || customCondition())
if (requiredCondition == null || requiredCondition())
{
if (Target is Item item)
{
@@ -218,7 +225,7 @@ namespace Barotrauma
private void CalculateCloseEnough()
{
float interactionDistance = Target is Item i ? i.InteractDistance * 0.9f : 0;
float interactionDistance = Target is Item i ? i.InteractDistance + Math.Max(i.Rect.Width, i.Rect.Height) / 2 : 0;
CloseEnough = Math.Max(interactionDistance, CloseEnough);
}
@@ -149,7 +149,14 @@ namespace Barotrauma
character?.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f);
}
}
repairable.CurrentFixer = abandon && repairable.CurrentFixer == character ? null : character;
if (abandon)
{
repairable.StopRepairing(character);
}
else
{
repairable.StartRepairing(character, Repairable.FixActions.Repair);
}
break;
}
}
@@ -161,7 +168,11 @@ namespace Barotrauma
constructor: () =>
{
previousCondition = -1;
var objective = new AIObjectiveGoTo(Item, character, objectiveManager);
var objective = new AIObjectiveGoTo(Item, character, objectiveManager)
{
// Don't stop in ladders, because we can't interact with other items while holding the ladders.
endNodeFilter = node => node.Waypoint.Ladders == null
};
if (repairTool != null)
{
objective.CloseEnough = repairTool.Range * 0.75f;
@@ -43,7 +43,7 @@ namespace Barotrauma
if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c))) { return false; }
if (!Objectives.ContainsKey(item))
{
if (item.Repairables.All(r => item.Condition > r.ShowRepairUIThreshold)) { return false; }
if (item.Repairables.All(r => item.ConditionPercentage > r.ShowRepairUIThreshold)) { return false; }
}
if (RequireAdequateSkills)
{
@@ -157,12 +157,13 @@ namespace Barotrauma
}
}
public SteeringPath FindPath(Vector2 start, Vector2 end, Submarine hostSub = null, string errorMsgStr = null)
public SteeringPath FindPath(Vector2 start, Vector2 end, Submarine hostSub = null, string errorMsgStr = null, Func<PathNode, bool> startNodeFilter = null, Func<PathNode, bool> endNodeFilter = null)
{
float closestDist = 0.0f;
PathNode startNode = null;
foreach (PathNode node in nodes)
{
if (startNodeFilter != null && !startNodeFilter(node)) { continue; }
Vector2 nodePos = node.Position;
if (hostSub != null)
{
@@ -219,6 +220,7 @@ namespace Barotrauma
PathNode endNode = null;
foreach (PathNode node in nodes)
{
if (endNodeFilter != null && !endNodeFilter(node)) { continue; }
Vector2 nodePos = node.Position;
if (hostSub != null)
{
@@ -96,8 +96,8 @@ namespace Barotrauma
public virtual AnimationType AnimationType { get; protected set; }
public static string GetDefaultFileName(string speciesName, AnimationType animType) => $"{speciesName.CapitaliseFirstInvariant()}{animType.ToString()}";
public static string GetDefaultFile(string speciesName, AnimationType animType, ContentPackage contentPackage = null) =>
$"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName, animType)}.xml";
public static string GetDefaultFile(string speciesName, AnimationType animType, ContentPackage contentPackage = null)
=> Path.Combine(GetFolder(speciesName, contentPackage), $"{GetDefaultFileName(speciesName, animType)}.xml");
public static string GetFolder(string speciesName, ContentPackage contentPackage = null)
{
@@ -66,7 +66,8 @@ namespace Barotrauma
.Concat(Joints.Select(j => j as RagdollSubParams)));
public static string GetDefaultFileName(string speciesName) => $"{speciesName.CapitaliseFirstInvariant()}DefaultRagdoll";
public static string GetDefaultFile(string speciesName, ContentPackage contentPackage = null) => $"{GetFolder(speciesName, contentPackage)}{GetDefaultFileName(speciesName)}.xml";
public static string GetDefaultFile(string speciesName, ContentPackage contentPackage = null)
=> Path.Combine(GetFolder(speciesName, contentPackage), $"{GetDefaultFileName(speciesName)}.xml");
private static readonly object[] dummyParams = new object[]
{
@@ -1324,7 +1324,7 @@ namespace Barotrauma
private void CheckBodyInRest(float deltaTime)
{
if (Collider.LinearVelocity.LengthSquared() > 0.01f || character.SelectedBy != null || !character.IsDead)
if (InWater || Collider.LinearVelocity.LengthSquared() > 0.01f || character.SelectedBy != null || !character.IsDead)
{
bodyInRestTimer = 0.0f;
foreach (Limb limb in Limbs)
@@ -1383,10 +1383,12 @@ namespace Barotrauma
private bool CheckValidity(PhysicsBody body)
{
string errorMsg = null;
string bodyName = body.UserData is Limb ? "Limb" : "Collider";
string bodyName = body.UserData is Limb limb ?
"Limb (" + limb.type + ")" :
"Collider";
if (!MathUtils.IsValid(body.SimPosition) || Math.Abs(body.SimPosition.X) > 1e10f || Math.Abs(body.SimPosition.Y) > 1e10f)
{
errorMsg = bodyName+ " position invalid (" + body.SimPosition + ", character: " + character.Name + "), resetting the ragdoll.";
errorMsg = bodyName + " position invalid (" + body.SimPosition + ", character: " + character.Name + "), resetting the ragdoll.";
}
else if (!MathUtils.IsValid(body.LinearVelocity) || Math.Abs(body.LinearVelocity.X) > 1000f || Math.Abs(body.LinearVelocity.Y) > 1000f)
{
@@ -1426,10 +1428,10 @@ namespace Barotrauma
{
Collider.SetTransform(Vector2.Zero, 0.0f);
}
foreach (Limb limb in Limbs)
foreach (Limb otherLimb in Limbs)
{
limb.body.SetTransform(Collider.SimPosition, 0.0f);
limb.body.ResetDynamics();
otherLimb.body.SetTransform(Collider.SimPosition, 0.0f);
otherLimb.body.ResetDynamics();
}
SetInitialLimbPositions();
return false;
@@ -104,6 +104,9 @@ namespace Barotrauma
public readonly bool IsHumanoid;
public bool IsTraitor;
public string TraitorCurrentObjective = "";
//the name of the species (e.q. human)
public readonly string SpeciesName;
@@ -1515,12 +1518,12 @@ namespace Barotrauma
bool leftHand = Inventory.IsInLimbSlot(item, InvSlotType.LeftHand);
bool selected = false;
if (rightHand && SelectedItems[0] == null)
if (rightHand && (SelectedItems[0] == null || SelectedItems[0] == item))
{
selectedItems[0] = item;
selected = true;
}
if (leftHand && SelectedItems[1] == null)
if (leftHand && (SelectedItems[1] == null || SelectedItems[1] == item))
{
selectedItems[1] = item;
selected = true;
@@ -1839,7 +1842,7 @@ namespace Barotrauma
{
DeselectCharacter();
}
else if (focusedCharacter != null && IsKeyHit(InputType.Grab) && FocusedCharacter.CanInventoryBeAccessed)
else if (focusedCharacter != null && IsKeyHit(InputType.Grab) && FocusedCharacter.CanBeDragged)
{
SelectCharacter(focusedCharacter);
}
@@ -1954,6 +1957,10 @@ namespace Barotrauma
//disable AI characters that are far away from the sub and the controlled character
float distSqr = Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, c.WorldPosition);
if (Controlled != null)
{
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(Controlled.WorldPosition, c.WorldPosition));
}
else
{
distSqr = Math.Min(distSqr, Vector2.DistanceSquared(GameMain.GameScreen.Cam.GetPosition(), c.WorldPosition));
}
@@ -2198,16 +2205,15 @@ namespace Barotrauma
private void UpdateSightRange()
{
if (aiTarget == null) { return; }
// TODO: the formula might need some tweaking
float range = (float)Math.Sqrt(Mass) * 1000.0f + AnimController.Collider.LinearVelocity.Length() * 500.0f;
aiTarget.SightRange = MathHelper.Clamp(range, 0, 15000.0f);
float range = (float)Math.Sqrt(Mass) * 250 + AnimController.Collider.LinearVelocity.Length() * 500;
aiTarget.SightRange = MathHelper.Clamp(range, 0, 10000);
}
private void UpdateSoundRange()
{
if (aiTarget == null) { return; }
float range = Mass / 5 * AnimController.TargetMovement.Length() * Noise;
aiTarget.SoundRange = MathHelper.Clamp(range, 0f, 5000f);
float range = ((float)Math.Sqrt(Mass) / 3) * (AnimController.TargetMovement.Length() * 2) * Noise;
aiTarget.SoundRange = MathHelper.Clamp(range, 0, 10000);
}
public void SetOrder(Order order, string orderOption, Character orderGiver, bool speak = true)
@@ -2598,6 +2604,7 @@ namespace Barotrauma
{
if (selectedItems[i] != null) selectedItems[i].Drop(this);
}
SelectedConstruction = null;
AnimController.ResetPullJoints();
@@ -21,7 +21,7 @@ namespace Barotrauma
/// <summary>
/// Probability for the affliction to be applied. Used by attacks.
/// </summary>
public float ApplyProbability;
public float ApplyProbability = 1.0f;
/// <summary>
/// Which character gave this affliction
@@ -9,6 +9,10 @@ namespace Barotrauma
{
private float invertControlsCooldown = 60.0f;
private float stunCoolDown = 60.0f;
private float invertControlsTimer;
private float invertControlsToggleTimer;
public AfflictionSpaceHerpes(AfflictionPrefab prefab, float strength) : base(prefab, strength)
{
}
@@ -25,10 +29,29 @@ namespace Barotrauma
//invert controls every 126-234 seconds when strength is close to 0
//every 56-104 seconds when strength is close to 100
invertControlsCooldown = (180.0f - Strength) * Rand.Range(0.7f, 1.3f);
var invertControlsAffliction = AfflictionPrefab.List.Find(ap => ap.Identifier == "invertcontrols");
float invertControlsDuration = MathHelper.Lerp(10.0f, 60.0f, Strength / 100.0f) * Rand.Range(0.7f, 1.3f);
characterHealth.ApplyAffliction(null, new Affliction(invertControlsAffliction, invertControlsDuration));
invertControlsTimer = MathHelper.Lerp(10.0f, 60.0f, Strength / 100.0f) * Rand.Range(0.7f, 1.3f);
}
else if (invertControlsTimer > 0.0f)
{
//randomly toggle inverted controls on/off every 5 seconds
invertControlsToggleTimer -= deltaTime;
if (invertControlsToggleTimer <= 0.0f)
{
invertControlsToggleTimer = 5.0f;
if (Rand.Range(0.0f, 1.0f) < 0.5f)
{
characterHealth.ReduceAffliction(null, "invertcontrols", 100);
}
else
{
var invertControlsAffliction = AfflictionPrefab.List.Find(ap => ap.Identifier == "invertcontrols");
characterHealth.ApplyAffliction(null, new Affliction(invertControlsAffliction, 5.0f));
}
}
invertControlsTimer -= deltaTime;
}
if (Strength > 50.0f)
{
@@ -1,9 +1,9 @@
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -732,14 +732,14 @@ namespace Barotrauma
return allAfflictions;
}
public void ServerWrite(NetBuffer msg)
public void ServerWrite(IWriteMessage msg)
{
List<Affliction> activeAfflictions = afflictions.FindAll(a => a.Strength > 0.0f && a.Strength >= a.Prefab.ActivationThreshold);
msg.Write((byte)activeAfflictions.Count);
foreach (Affliction affliction in activeAfflictions)
{
msg.WriteRangedInteger(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(affliction.Prefab));
msg.WriteRangedIntegerDeprecated(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(affliction.Prefab));
msg.WriteRangedSingle(
MathHelper.Clamp(affliction.Strength, 0.0f, affliction.Prefab.MaxStrength),
0.0f, affliction.Prefab.MaxStrength, 8);
@@ -758,8 +758,8 @@ namespace Barotrauma
msg.Write((byte)limbAfflictions.Count);
foreach (var limbAffliction in limbAfflictions)
{
msg.WriteRangedInteger(0, limbHealths.Count - 1, limbHealths.IndexOf(limbAffliction.First));
msg.WriteRangedInteger(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(limbAffliction.Second.Prefab));
msg.WriteRangedIntegerDeprecated(0, limbHealths.Count - 1, limbHealths.IndexOf(limbAffliction.First));
msg.WriteRangedIntegerDeprecated(0, AfflictionPrefab.List.Count - 1, AfflictionPrefab.List.IndexOf(limbAffliction.Second.Prefab));
msg.WriteRangedSingle(
MathHelper.Clamp(limbAffliction.Second.Strength, 0.0f, limbAffliction.Second.Prefab.MaxStrength),
0.0f, limbAffliction.Second.Prefab.MaxStrength, 8);
@@ -144,6 +144,15 @@ namespace Barotrauma
#if SERVER
if (GameMain.Server != null && Entity.Spawner != null)
{
if (GameMain.Server.EntityEventManager.UniqueEvents.Any(ev => ev.Entity == item))
{
string errorMsg = $"Error while spawning job items. Item {item.Name} created network events before the spawn event had been created.";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Job.InitializeJobItem:EventsBeforeSpawning", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
GameMain.Server.EntityEventManager.UniqueEvents.RemoveAll(ev => ev.Entity == item);
GameMain.Server.EntityEventManager.Events.RemoveAll(ev => ev.Entity == item);
}
Entity.Spawner.CreateNetworkEvent(item, false);
}
#endif
@@ -36,7 +36,8 @@ namespace Barotrauma
Afflictions,
Buffs,
Tutorials,
UIStyle
UIStyle,
TraitorMissions
}
public class ContentPackage
@@ -79,6 +80,7 @@ namespace Barotrauma
ContentType.LevelGenerationParameters,
ContentType.RandomEvents,
ContentType.Missions,
ContentType.TraitorMissions,
ContentType.BackgroundCreaturePrefabs,
ContentType.RuinConfig,
ContentType.NPCConversations,
@@ -96,7 +98,7 @@ namespace Barotrauma
public string Path
{
get;
private set;
set;
}
public string SteamWorkshopUrl;
@@ -419,7 +421,7 @@ namespace Barotrauma
switch (contentFile.Type)
{
case ContentType.Submarine:
return path == "Submarines";
return path == "Submarines" || path == "Mods";
default:
return path == "Mods";
}
@@ -460,8 +462,9 @@ namespace Barotrauma
return Files.Where(f => f.Type == type).Select(f => f.Path);
}
public static void LoadAll(string folder)
public static void LoadAll()
{
string folder = ContentPackage.Folder;
if (!Directory.Exists(folder))
{
try
@@ -475,14 +478,23 @@ namespace Barotrauma
}
}
string[] files = Directory.GetFiles(folder, "*.xml");
List.Clear();
string[] files = Directory.GetFiles(folder, "*.xml");
foreach (string filePath in files)
{
ContentPackage package = new ContentPackage(filePath);
List.Add(package);
List.Add(new ContentPackage(filePath));
}
string[] modDirectories = Directory.GetDirectories("Mods");
foreach (string modDirectory in modDirectories)
{
if (System.IO.Path.GetFileName(modDirectory.TrimEnd(System.IO.Path.DirectorySeparatorChar)) == "ExampleMod") { continue; }
string modFilePath = System.IO.Path.Combine(modDirectory, Steam.SteamManager.MetadataFileName);
if (File.Exists(modFilePath))
{
List.Add(new ContentPackage(modFilePath));
}
}
}
@@ -505,7 +517,7 @@ namespace Barotrauma
public class ContentFile
{
public readonly string Path;
public string Path;
public ContentType Type;
public Workshop.Item WorkShopItem;
@@ -142,6 +142,7 @@ namespace Barotrauma
}
catch (Exception e)
{
handle.Exception = e;
DebugConsole.ThrowError("Coroutine \"" + handle.Name + "\" has thrown an exception", e);
}
}
@@ -182,7 +183,7 @@ namespace Barotrauma
{
if (handle.Thread.ThreadState.HasFlag(ThreadState.Stopped))
{
if ((CoroutineStatus)handle.Coroutine.Current == CoroutineStatus.Failure)
if (handle.Exception!=null || (CoroutineStatus)handle.Coroutine.Current == CoroutineStatus.Failure)
{
DebugConsole.ThrowError("Coroutine \"" + handle.Name + "\" has failed");
}
@@ -438,7 +438,7 @@ namespace Barotrauma
});
}));
commands.Add(new Command("banip", "banip [ip]: Ban the IP address from the server.", null));
commands.Add(new Command("banendpoint|banip", "banendpoint [endpoint]: Ban the IP address/SteamID from the server.", null));
commands.Add(new Command("teleportcharacter|teleport", "teleport [character name]: Teleport the specified character to the position of the cursor. If the name parameter is omitted, the controlled character will be teleported.", (string[] args) =>
{
@@ -674,6 +674,11 @@ namespace Barotrauma
},null));
#if DEBUG
commands.Add(new Command("crash", "crash: Crashes the game.", (string[] args) =>
{
throw new Exception("crash command issued");
}));
commands.Add(new Command("teleportsub", "teleportsub [start/end]: Teleport the submarine to the start or end of the level. WARNING: does not take outposts into account, so often leads to physics glitches. Only use for debugging.", (string[] args) =>
{
if (Submarine.MainSub == null || Level.Loaded == null) return;
@@ -960,6 +965,7 @@ namespace Barotrauma
}));
#if DEBUG
/*TODO: reimplement
commands.Add(new Command("simulatedlatency", "simulatedlatency [minimumlatencyseconds] [randomlatencyseconds]: applies a simulated latency to network messages. Useful for simulating real network conditions when testing the multiplayer locally.", (string[] args) =>
{
if (args.Count() < 2 || (GameMain.NetworkMember == null)) return;
@@ -1029,7 +1035,7 @@ namespace Barotrauma
}
#endif
NewMessage("Set packet duplication to " + (int)(duplicates * 100) + "%.", Color.White);
}));
}));*/
#endif
//"dummy commands" that only exist so that the server can give clients permissions to use them
@@ -1502,8 +1508,9 @@ namespace Barotrauma
public static void NewMessage(string msg, Color color, bool isCommand = false)
{
if (string.IsNullOrEmpty((msg))) return;
var newMsg = new ColoredText(msg, color, isCommand);
lock (queuedMessages)
{
queuedMessages.Enqueue(new ColoredText(msg, color, isCommand));
@@ -36,6 +36,8 @@ namespace Barotrauma
private List<ScriptedEventSet> selectedEventSets;
private EventManagerSettings settings;
private readonly bool isClient;
public float CurrentIntensity
{
@@ -51,13 +53,15 @@ namespace Barotrauma
{
events = new List<ScriptedEvent>();
selectedEventSets = new List<ScriptedEventSet>();
isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
}
public bool Enabled = true;
public void StartRound(Level level)
{
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
if (isClient) { return; }
var suitableSettings = EventManagerSettings.List.FindAll(s =>
level.Difficulty >= s.MinLevelDifficulty &&
@@ -193,13 +197,13 @@ namespace Barotrauma
public void Update(float deltaTime)
{
if (!Enabled) return;
if (!Enabled) { return; }
//clients only calculate the intensity but don't create any events
//(the intensity is used for controlling the background music)
CalculateCurrentIntensity(deltaTime);
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
if (isClient) { return; }
roundDuration += deltaTime;
@@ -32,7 +32,7 @@ namespace Barotrauma
private set { failureMessage = value; }
}
private string description;
protected string description;
public virtual string Description
{
get { return description; }
@@ -155,6 +155,13 @@ namespace Barotrauma
return false;
}
protected void ShowMessage(int index)
{
ShowMessageProjSpecific(index);
}
partial void ShowMessageProjSpecific(int index);
/// <summary>
/// End the mission and give a reward if it was completed successfully
/// </summary>
@@ -5,7 +5,7 @@ using System.Xml.Linq;
namespace Barotrauma
{
enum MissionType
public enum MissionType
{
Random,
None,
@@ -28,6 +28,9 @@ namespace Barotrauma
{
monsterFile = prefab.ConfigElement.GetAttributeString("monsterfile", "");
monsterCount = prefab.ConfigElement.GetAttributeInt("monstercount", 1);
description = description.Replace("[monster]",
TextManager.Get("character." + System.IO.Path.GetFileNameWithoutExtension(monsterFile)));
}
public override void Start(Level level)
@@ -66,9 +69,9 @@ namespace Barotrauma
if (activeMonsters.Any()) { return; }
#if CLIENT
ShowMessage(state);
#endif
state = 1;
break;
}
@@ -94,16 +94,15 @@ namespace Barotrauma
if (item.ParentInventory != null) item.body.FarseerBody.IsKinematic = false;
if (item.CurrentHull?.Submarine == null) return;
#if CLIENT
ShowMessage(state);
#endif
state = 1;
break;
case 1:
if (!Submarine.MainSub.AtEndPosition && !Submarine.MainSub.AtStartPosition) return;
#if CLIENT
ShowMessage(state);
#endif
state = 2;
break;
}
@@ -6,7 +6,7 @@ using System.Xml.Linq;
namespace Barotrauma
{
abstract class CampaignMode : GameMode
abstract partial class CampaignMode : GameMode
{
public readonly CargoManager CargoManager;
@@ -111,9 +111,8 @@ namespace Barotrauma
base.Update(deltaTime);
if (!IsRunning) { return; }
#if CLIENT
if (GameMain.Client != null) { return; }
#endif
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (!watchmenSpawned)
{
if (Level.Loaded.StartOutpost != null) { startWatchman = SpawnWatchman(Level.Loaded.StartOutpost); }
@@ -128,7 +127,7 @@ namespace Barotrauma
foreach (Character character in Character.CharacterList)
{
#if SERVER
if (string.IsNullOrEmpty(character.OwnerClientIP)) { continue; }
if (string.IsNullOrEmpty(character.OwnerClientEndPoint)) { continue; }
#else
if (!CrewManager.GetCharacters().Contains(character)) { continue; }
#endif
@@ -13,7 +13,7 @@ namespace Barotrauma
public readonly string Name;
public string ClientIP
public string ClientEndPoint
{
get;
private set;
@@ -42,7 +42,7 @@ namespace Barotrauma
public CharacterCampaignData(XElement element)
{
Name = element.GetAttributeString("name", "Unnamed");
ClientIP = element.GetAttributeString("ip", "");
ClientEndPoint = element.GetAttributeString("endpoint", null) ?? element.GetAttributeString("ip", "");
string steamID = element.GetAttributeString("steamid", "");
if (!string.IsNullOrEmpty(steamID))
{
@@ -69,7 +69,7 @@ namespace Barotrauma
{
XElement element = new XElement("CharacterCampaignData",
new XAttribute("name", Name),
new XAttribute("ip", ClientIP),
new XAttribute("endpoint", ClientEndPoint),
new XAttribute("steamid", SteamID));
CharacterInfo?.Save(element);
@@ -64,7 +64,7 @@ namespace Barotrauma
isRunning = true;
}
public virtual void MsgBox() { }
public virtual void ShowStartMessage() { }
public virtual void AddToGUIUpdateList()
{
@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Xml.Linq;
using Lidgren.Network;
using System.Collections.Generic;
using System.IO;
@@ -98,14 +97,11 @@ namespace Barotrauma
GameMain.GameSession.EndRound("");
//client character has spawned this round -> remove old data (and replace with an up-to-date one if the client still has an alive character)
characterData.RemoveAll(cd => cd.HasSpawned);
foreach (Client c in GameMain.Server.ConnectedClients)
{
if (c.HasSpawned)
{
//client has spawned this round -> remove old data (and replace with new one if the client still has an alive character)
characterData.RemoveAll(cd => cd.MatchesClient(c));
}
if (c.Character?.Info != null && !c.Character.IsDead)
{
characterData.Add(new CharacterCampaignData(c));
@@ -260,7 +260,7 @@ namespace Barotrauma
if (GameMode != null)
{
GameMode.MsgBox();
GameMode.ShowStartMessage();
if (GameMode is MultiPlayerCampaign mpCampaign && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
{
@@ -46,6 +46,8 @@ namespace Barotrauma
public bool PauseOnFocusLost { get; set; }
public bool MuteOnFocusLost { get; set; }
public bool DynamicRangeCompressionEnabled { get; set; }
public bool VoipAttenuationEnabled { get; set; }
public bool UseDirectionalVoiceChat { get; set; }
public enum VoiceMode
@@ -176,9 +178,9 @@ namespace Barotrauma
#if CLIENT
if (GameMain.SoundManager != null)
{
GameMain.SoundManager.SetCategoryGainMultiplier("default", soundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("ui", soundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", soundVolume);
GameMain.SoundManager.SetCategoryGainMultiplier("default", soundVolume, 0);
GameMain.SoundManager.SetCategoryGainMultiplier("ui", soundVolume, 0);
GameMain.SoundManager.SetCategoryGainMultiplier("waterambience", soundVolume, 0);
}
#endif
}
@@ -191,7 +193,7 @@ namespace Barotrauma
{
musicVolume = MathHelper.Clamp(value, 0.0f, 1.0f);
#if CLIENT
GameMain.SoundManager?.SetCategoryGainMultiplier("music", musicVolume);
GameMain.SoundManager?.SetCategoryGainMultiplier("music", musicVolume, 0);
#endif
}
}
@@ -203,7 +205,7 @@ namespace Barotrauma
{
voiceChatVolume = MathHelper.Clamp(value, 0.0f, 1.0f);
#if CLIENT
GameMain.SoundManager?.SetCategoryGainMultiplier("voip", voiceChatVolume * 20.0f);
GameMain.SoundManager?.SetCategoryGainMultiplier("voip", voiceChatVolume * 20.0f, 0);
#endif
}
}
@@ -286,7 +288,7 @@ namespace Barotrauma
public GameSettings()
{
ContentPackage.LoadAll(ContentPackage.Folder);
ContentPackage.LoadAll();
CompletedTutorialNames = new List<string>();
LoadDefaultConfig();
@@ -443,6 +445,11 @@ namespace Barotrauma
LoadAudioSettings(doc);
LoadControls(doc);
LoadContentPackages(doc);
#if DEBUG
WindowMode = WindowMode.Windowed;
#endif
UnsavedSettings = false;
}
@@ -797,6 +804,8 @@ namespace Barotrauma
new XAttribute("voicechatvolume", voiceChatVolume),
new XAttribute("microphonevolume", microphoneVolume),
new XAttribute("muteonfocuslost", MuteOnFocusLost),
new XAttribute("dynamicrangecompressionenabled", DynamicRangeCompressionEnabled),
new XAttribute("voipattenuationenabled", VoipAttenuationEnabled),
new XAttribute("usedirectionalvoicechat", UseDirectionalVoiceChat),
new XAttribute("voicesetting", VoiceSetting),
new XAttribute("voicecapturedevice", VoiceCaptureDevice ?? ""),
@@ -910,7 +919,7 @@ namespace Barotrauma
}
AutoCheckUpdates = doc.Root.GetAttributeBool("autocheckupdates", AutoCheckUpdates);
sendUserStatistics = doc.Root.GetAttributeBool("senduserstatistics", sendUserStatistics);
QuickStartSubmarineName = doc.Root.GetAttributeString("quickstartsubmarine", "");
QuickStartSubmarineName = doc.Root.GetAttributeString("quickstartsub", QuickStartSubmarineName);
useSteamMatchmaking = doc.Root.GetAttributeBool("usesteammatchmaking", useSteamMatchmaking);
requireSteamAuthentication = doc.Root.GetAttributeBool("requiresteamauthentication", requireSteamAuthentication);
EnableSplashScreen = doc.Root.GetAttributeBool("enablesplashscreen", EnableSplashScreen);
@@ -997,8 +1006,11 @@ namespace Barotrauma
{
SoundVolume = audioSettings.GetAttributeFloat("soundvolume", SoundVolume);
MusicVolume = audioSettings.GetAttributeFloat("musicvolume", MusicVolume);
DynamicRangeCompressionEnabled = audioSettings.GetAttributeBool("dynamicrangecompressionenabled", DynamicRangeCompressionEnabled);
VoipAttenuationEnabled = audioSettings.GetAttributeBool("voipattenuationenabled", VoipAttenuationEnabled);
VoiceChatVolume = audioSettings.GetAttributeFloat("voicechatvolume", VoiceChatVolume);
MuteOnFocusLost = audioSettings.GetAttributeBool("muteonfocuslost", MuteOnFocusLost);
UseDirectionalVoiceChat = audioSettings.GetAttributeBool("usedirectionalvoicechat", UseDirectionalVoiceChat);
VoiceCaptureDevice = audioSettings.GetAttributeString("voicecapturedevice", VoiceCaptureDevice);
NoiseGateThreshold = audioSettings.GetAttributeFloat("noisegatethreshold", NoiseGateThreshold);
@@ -1108,6 +1120,8 @@ namespace Barotrauma
ChatOpen = true;
soundVolume = 0.5f;
musicVolume = 0.3f;
DynamicRangeCompressionEnabled = true;
VoipAttenuationEnabled = true;
voiceChatVolume = 0.5f;
microphoneVolume = 1.0f;
AutoCheckUpdates = true;
@@ -82,7 +82,7 @@ namespace Barotrauma.Items.Components
}
}
}
public DockingPort(Item item, XElement element)
: base(item, element)
{
@@ -160,7 +160,7 @@ namespace Barotrauma.Items.Components
if (DockingTarget != null)
{
Undock();
}
}
if (target.item.Submarine == item.Submarine)
{
@@ -168,7 +168,7 @@ namespace Barotrauma.Items.Components
DockingTarget = null;
return;
}
target.InitializeLinks();
if (!item.linkedTo.Contains(target.item)) item.linkedTo.Add(target.item);
@@ -193,7 +193,7 @@ namespace Barotrauma.Items.Components
Math.Sign(DockingTarget.item.WorldPosition.X - item.WorldPosition.X) :
Math.Sign(DockingTarget.item.WorldPosition.Y - item.WorldPosition.Y);
DockingTarget.DockingDir = -DockingDir;
if (door != null && DockingTarget.door != null)
{
WayPoint myWayPoint = WayPoint.WayPointList.Find(wp => door.LinkedGap == wp.ConnectedGap);
@@ -205,7 +205,7 @@ namespace Barotrauma.Items.Components
targetWayPoint.linkedTo.Add(myWayPoint);
}
}
CreateJoint(false);
#if SERVER
@@ -246,7 +246,7 @@ namespace Barotrauma.Items.Components
else if (DockingTarget.item.Submarine.PhysicsBody.Mass < item.Submarine.PhysicsBody.Mass ||
item.Submarine.IsOutpost)
{
DockingTarget.item.Submarine.SubBody.SetPosition(item.Submarine.SubBody.Position - ConvertUnits.ToDisplayUnits(jointDiff));
DockingTarget.item.Submarine.SubBody.SetPosition(DockingTarget.item.Submarine.SubBody.Position - ConvertUnits.ToDisplayUnits(jointDiff));
}
ConnectWireBetweenPorts();
@@ -401,7 +401,7 @@ namespace Barotrauma.Items.Components
}
private void CreateHulls()
{
{
var hullRects = new Rectangle[] { item.WorldRect, DockingTarget.item.WorldRect };
var subs = new Submarine[] { item.Submarine, DockingTarget.item.Submarine };
@@ -416,18 +416,18 @@ namespace Barotrauma.Items.Components
{
DockingTarget.CreateDoorBody();
}
if (IsHorizontal)
{
if (hullRects[0].Center.X > hullRects[1].Center.X)
{
hullRects = new Rectangle[] { DockingTarget.item.WorldRect, item.WorldRect };
subs = new Submarine[] { DockingTarget.item.Submarine,item.Submarine };
subs = new Submarine[] { DockingTarget.item.Submarine, item.Submarine };
}
hullRects[0] = new Rectangle(hullRects[0].Center.X, hullRects[0].Y, ((int)DockedDistance / 2), hullRects[0].Height);
hullRects[1] = new Rectangle(hullRects[1].Center.X - ((int)DockedDistance / 2), hullRects[1].Y, ((int)DockedDistance / 2), hullRects[1].Height);
//expand hulls if needed, so there's no empty space between the sub's hulls and docking port hulls
int leftSubRightSide = int.MinValue, rightSubLeftSide = int.MaxValue;
foreach (Hull hull in Hull.hullList)
@@ -478,7 +478,7 @@ namespace Barotrauma.Items.Components
hullRects[1].Width += rightHullDiff;
}
}
for (int i = 0; i < 2; i++)
{
hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition));
@@ -503,7 +503,7 @@ namespace Barotrauma.Items.Components
hullRects = new Rectangle[] { DockingTarget.item.WorldRect, item.WorldRect };
subs = new Submarine[] { DockingTarget.item.Submarine, item.Submarine };
}
hullRects[0] = new Rectangle(hullRects[0].X, hullRects[0].Y + (int)(-hullRects[0].Height + DockedDistance) / 2, hullRects[0].Width, ((int)DockedDistance / 2));
hullRects[1] = new Rectangle(hullRects[1].X, hullRects[1].Y - hullRects[1].Height / 2, hullRects[1].Width, ((int)DockedDistance / 2));
@@ -954,7 +954,7 @@ namespace Barotrauma.Items.Components
#endif
}
public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(docked);
@@ -965,7 +965,7 @@ namespace Barotrauma.Items.Components
}
}
public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
bool isDocked = msg.ReadBoolean();
@@ -313,8 +313,7 @@ namespace Barotrauma.Items.Components
PredictedState = null;
}
}
LinkedGap.Open = openState;
LinkedGap.Open = isBroken ? 1.0f : openState;
}
if (isClosing)
@@ -371,7 +370,7 @@ namespace Barotrauma.Items.Components
{
LinkedGap.AutoOrient();
}
LinkedGap.Open = openState;
LinkedGap.Open = isBroken ? 1.0f : openState;
LinkedGap.PassAmbientLight = Window != Rectangle.Empty;
}
@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System.Collections.Generic;
using System.Xml.Linq;
@@ -286,14 +285,21 @@ namespace Barotrauma.Items.Components
item.SetTransform(rightHand.SimPosition, 0.0f);
}
bool alreadySelected = character.HasEquippedItem(item);
if (picker.TrySelectItem(item) || picker.HasEquippedItem(item))
bool alreadyEquipped = character.HasEquippedItem(item);
bool canSelect = picker.TrySelectItem(item);
if (canSelect || picker.HasEquippedItem(item))
{
if (!canSelect)
{
character.DeselectItem(item);
}
item.body.Enabled = true;
IsActive = true;
#if SERVER
if (!alreadySelected) GameServer.Log(character.LogName + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction);
if (!alreadyEquipped) GameServer.Log(character.LogName + " equipped " + item.Name, ServerLog.MessageType.ItemInteraction);
#endif
}
}
@@ -557,7 +563,7 @@ namespace Barotrauma.Items.Components
DeattachFromWall();
}
}
public override XElement Save(XElement parentElement)
{
if (!attachable)
@@ -581,8 +587,8 @@ namespace Barotrauma.Items.Components
return saveElement;
}
public override void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public override void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
base.ServerWrite(msg, c, extraData);
if (!attachable || body == null) { return; }
@@ -592,11 +598,11 @@ namespace Barotrauma.Items.Components
msg.Write(body.SimPosition.Y);
}
public override void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
base.ClientRead(type, msg, sendingTime);
bool shouldBeAttached = msg.ReadBoolean();
Vector2 simPosition = new Vector2(msg.ReadFloat(), msg.ReadFloat());
Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle());
if (!attachable)
{
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
@@ -116,7 +115,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(deattachTimer);
}
@@ -56,11 +56,9 @@ namespace Barotrauma.Items.Components
public MeleeWeapon(Item item, XElement element)
: base(item, element)
{
//throwForce = ToolBox.GetAttributeFloat(element, "throwforce", 1.0f);
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "attack") continue;
if (subElement.Name.ToString().ToLowerInvariant() != "attack") { continue; }
attack = new Attack(subElement, item.Name + ", MeleeWeapon");
}
item.IsShootable = true;
@@ -70,23 +68,22 @@ namespace Barotrauma.Items.Components
public override bool Use(float deltaTime, Character character = null)
{
if (character == null || reloadTimer > 0.0f) return false;
if (Item.RequireAimToUse && !character.IsKeyDown(InputType.Aim) || hitting) return false;
if (character == null || reloadTimer > 0.0f) { return false; }
if (Item.RequireAimToUse && !character.IsKeyDown(InputType.Aim) || hitting) { return false; }
//don't allow hitting if the character is already hitting with another weapon
for (int i = 0; i < 2; i++ )
{
if (character.SelectedItems[i] == null || character.SelectedItems[i] == Item) continue;
if (character.SelectedItems[i] == null || character.SelectedItems[i] == Item) { continue; }
var otherWeapon = character.SelectedItems[i].GetComponent<MeleeWeapon>();
if (otherWeapon == null) continue;
if (otherWeapon.hitting) return false;
if (otherWeapon == null) { continue; }
if (otherWeapon.hitting) { return false; }
}
SetUser(character);
if (hitPos < MathHelper.PiOver4) return false;
if (hitPos < MathHelper.PiOver4) { return false; }
reloadTimer = reload;
@@ -94,21 +91,20 @@ namespace Barotrauma.Items.Components
item.body.FarseerBody.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall;
item.body.FarseerBody.OnCollision += OnCollision;
foreach (Limb l in character.AnimController.Limbs)
if (!character.AnimController.InWater)
{
//item.body.FarseerBody.IgnoreCollisionWith(l.body.FarseerBody);
if (character.AnimController.InWater) continue;
if (l.type == LimbType.LeftFoot || l.type == LimbType.LeftThigh || l.type == LimbType.LeftLeg) continue;
if (l.type == LimbType.Head || l.type == LimbType.Torso)
foreach (Limb l in character.AnimController.Limbs)
{
l.body.ApplyLinearImpulse(new Vector2(character.AnimController.Dir * 7.0f, -4.0f));
if (l.type == LimbType.LeftFoot || l.type == LimbType.LeftThigh || l.type == LimbType.LeftLeg) { continue; }
if (l.type == LimbType.Head || l.type == LimbType.Torso)
{
l.body.ApplyLinearImpulse(new Vector2(character.AnimController.Dir * 7.0f, -4.0f));
}
else
{
l.body.ApplyLinearImpulse(new Vector2(character.AnimController.Dir * 5.0f, -2.0f));
}
}
else
{
l.body.ApplyLinearImpulse(new Vector2(character.AnimController.Dir * 5.0f, -2.0f));
}
}
hitting = true;
@@ -121,7 +117,6 @@ namespace Barotrauma.Items.Components
public override void Drop(Character dropper)
{
base.Drop(dropper);
hitting = false;
hitPos = 0.0f;
}
@@ -133,17 +128,17 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (!item.body.Enabled) return;
if (!picker.HasSelectedItem(item)) IsActive = false;
if (!item.body.Enabled) { return; }
if (!picker.HasSelectedItem(item)) { IsActive = false; }
reloadTimer -= deltaTime;
if (reloadTimer < 0) { reloadTimer = 0; }
if (!picker.IsKeyDown(InputType.Aim) && !hitting) hitPos = 0.0f;
if (!picker.IsKeyDown(InputType.Aim) && !hitting) { hitPos = 0.0f; }
ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
if (item.body.Dir != picker.AnimController.Dir) Flip();
if (item.body.Dir != picker.AnimController.Dir) { Flip(); }
AnimController ac = picker.AnimController;
@@ -236,16 +231,16 @@ namespace Barotrauma.Items.Components
if (f2.Body.UserData is Limb)
{
targetLimb = (Limb)f2.Body.UserData;
if (targetLimb.IsSevered || targetLimb.character == null) return false;
if (targetLimb.IsSevered || targetLimb.character == null) { return false; }
targetCharacter = targetLimb.character;
if (targetCharacter == picker) return false;
if (targetCharacter == picker){ return false; }
if (AllowHitMultiple)
{
if (hitTargets.Contains(targetCharacter)) return false;
if (hitTargets.Contains(targetCharacter)) { return false; }
}
else
{
if (hitTargets.Any(t => t is Character)) return false;
if (hitTargets.Any(t => t is Character)) { return false; }
}
hitTargets.Add(targetCharacter);
}
@@ -256,11 +251,11 @@ namespace Barotrauma.Items.Components
targetLimb = targetCharacter.AnimController.GetLimb(LimbType.Torso); //Otherwise armor can be bypassed in strange ways
if (AllowHitMultiple)
{
if (hitTargets.Contains(targetCharacter)) return false;
if (hitTargets.Contains(targetCharacter)) { return false; }
}
else
{
if (hitTargets.Any(t => t is Character)) return false;
if (hitTargets.Any(t => t is Character)) { return false; }
}
hitTargets.Add(targetCharacter);
}
@@ -269,11 +264,11 @@ namespace Barotrauma.Items.Components
targetStructure = (Structure)f2.Body.UserData;
if (AllowHitMultiple)
{
if (hitTargets.Contains(targetStructure)) return true;
if (hitTargets.Contains(targetStructure)) { return true; }
}
else
{
if (hitTargets.Any(t => t is Structure)) return true;
if (hitTargets.Any(t => t is Structure)) { return true; }
}
hitTargets.Add(targetStructure);
}
@@ -303,8 +298,8 @@ namespace Barotrauma.Items.Components
return false;
}
}
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return true;
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return true; }
#if SERVER
if (GameMain.Server != null && targetCharacter != null) //TODO: Log structure hits
@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Lidgren.Network;
namespace Barotrauma.Items.Components
{
@@ -233,12 +232,12 @@ namespace Barotrauma.Items.Components
}
}
public virtual void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public virtual void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(activePicker == null ? (ushort)0 : activePicker.ID);
}
public virtual void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
ushort pickerID = msg.ReadUInt16();
if (pickerID == 0)
@@ -54,6 +54,9 @@ namespace Barotrauma.Items.Components
[Serialize(false, false)]
public bool RepairMultiple { get; set; }
[Serialize(false, false)]
public bool RepairThroughHoles { get; set; }
[Serialize(0.0f, false)]
public float FireProbability { get; set; }
@@ -146,10 +149,24 @@ namespace Barotrauma.Items.Components
}
}
Vector2 targetPosition = item.WorldPosition;
targetPosition += new Vector2(
(float)Math.Cos(item.body.Rotation),
(float)Math.Sin(item.body.Rotation)) * Range * item.body.Dir;
Vector2 rayStart;
Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos;
Vector2 barrelPos = item.SimPosition + ConvertUnits.ToSimUnits(TransformedBarrelPos);
//make sure there's no obstacles between the base of the item (or the shoulder of the character) and the end of the barrel
if (Submarine.PickBody(sourcePos, barrelPos, collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null)
{
//no obstacles -> we start the raycast at the end of the barrel
rayStart = ConvertUnits.ToSimUnits(item.WorldPosition + TransformedBarrelPos);
}
else
{
rayStart = ConvertUnits.ToSimUnits(item.WorldPosition);
}
Vector2 rayEnd = rayStart +
ConvertUnits.ToSimUnits(new Vector2(
(float)Math.Cos(item.body.Rotation),
(float)Math.Sin(item.body.Rotation)) * Range * item.body.Dir);
List<Body> ignoredBodies = new List<Body>();
foreach (Limb limb in character.AnimController.Limbs)
@@ -161,11 +178,8 @@ namespace Barotrauma.Items.Components
IsActive = true;
activeTimer = 0.1f;
Vector2 rayStart = ConvertUnits.ToSimUnits(item.WorldPosition);
Vector2 rayEnd = ConvertUnits.ToSimUnits(targetPosition);
debugRayStartPos = item.WorldPosition;
debugRayStartPos = ConvertUnits.ToDisplayUnits(rayStart);
debugRayEndPos = ConvertUnits.ToDisplayUnits(rayEnd);
if (character.Submarine == null)
@@ -203,7 +217,7 @@ namespace Barotrauma.Items.Components
float lastPickedFraction = 0.0f;
if (RepairMultiple)
{
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, allowInsideFixture: true);
var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: RepairThroughHoles, allowInsideFixture: true);
lastPickedFraction = Submarine.LastPickedFraction;
Type lastHitType = null;
hitCharacters.Clear();
@@ -243,7 +257,7 @@ namespace Barotrauma.Items.Components
{
FixBody(user, deltaTime, degreeOfSuccess,
Submarine.PickBody(rayStart, rayEnd,
ignoredBodies, collisionCategories, ignoreSensors: false,
ignoredBodies, collisionCategories, ignoreSensors: RepairThroughHoles,
customPredicate: (Fixture f) => { return f?.Body?.UserData != null; },
allowInsideFixture: true));
lastPickedFraction = Submarine.LastPickedFraction;
@@ -324,6 +338,7 @@ namespace Barotrauma.Items.Components
}
else if (targetBody.UserData is Character targetCharacter)
{
if (targetCharacter.Removed) { return false; }
targetCharacter.LastDamageSource = item;
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new List<ISerializableEntity>() { targetCharacter });
FixCharacterProjSpecific(user, deltaTime, targetCharacter);
@@ -331,6 +346,7 @@ namespace Barotrauma.Items.Components
}
else if (targetBody.UserData is Limb targetLimb)
{
if (targetLimb.character == null || targetLimb.character.Removed) { return false; }
targetLimb.character.LastDamageSource = item;
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, new List<ISerializableEntity>() { targetLimb.character, targetLimb });
FixCharacterProjSpecific(user, deltaTime, targetLimb.character);
@@ -506,9 +522,9 @@ namespace Barotrauma.Items.Components
if (propertyName != "stuck") { continue; }
if (door.SerializableProperties == null || !door.SerializableProperties.TryGetValue(propertyName, out SerializableProperty property)) { continue; }
object value = property.GetValue(target);
if (value.GetType() == typeof(float))
if (door.Stuck > 0)
{
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, (float)value / 100, Color.DarkGray * 0.5f, Color.White);
var progressBar = user.UpdateHUDProgressBar(door, door.Item.WorldPosition, door.Stuck / 100, Color.DarkGray * 0.5f, Color.White);
if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); }
}
}
@@ -6,12 +6,10 @@ namespace Barotrauma.Items.Components
{
class Throwable : Holdable
{
float throwForce;
private float throwForce, throwPos;
private bool throwing, throwDone;
float throwPos;
bool throwing;
bool throwDone;
private bool midAir;
[Serialize(1.0f, false)]
public float ThrowForce
@@ -57,7 +55,17 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (!item.body.Enabled) return;
if (!item.body.Enabled) { return; }
if (midAir)
{
if (item.body.LinearVelocity.LengthSquared() < 0.01f)
{
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform;
midAir = false;
}
return;
}
if (picker == null || picker.Removed || !picker.HasSelectedItem(item))
{
IsActive = false;
@@ -113,6 +121,10 @@ namespace Barotrauma.Items.Components
item.Drop(thrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
//disable platform collisions until the item comes back to rest again
item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
midAir = true;
ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
ac.GetLimb(LimbType.Torso).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
@@ -82,13 +82,14 @@ namespace Barotrauma.Items.Components
}
}
#endif
if (AITarget != null) AITarget.Enabled = value;
isActive = value;
}
}
private bool drawable = true;
public List<PropertyConditional> IsActiveConditionals;
public bool Drawable
{
get { return drawable; }
@@ -208,11 +209,6 @@ namespace Barotrauma.Items.Components
set;
}
public AITarget AITarget
{
get;
private set;
}
/// <summary>
/// How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not enforced).
@@ -267,6 +263,15 @@ namespace Barotrauma.Items.Components
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "activeconditional":
case "isactive":
IsActiveConditionals = IsActiveConditionals ?? new List<PropertyConditional>();
foreach (XAttribute attribute in subElement.Attributes())
{
if (attribute.Name.ToString().ToLowerInvariant() == "targetitemcomponent") { continue; }
IsActiveConditionals.Add(new PropertyConditional(attribute));
}
break;
case "requireditem":
case "requireditems":
RelatedItem ri = RelatedItem.Load(subElement, item.Name);
@@ -308,12 +313,6 @@ namespace Barotrauma.Items.Components
effectList.Add(statusEffect);
break;
case "aitarget":
AITarget = new AITarget(item, subElement)
{
Enabled = isActive
};
break;
default:
if (LoadElemProjSpecific(subElement)) break;
@@ -474,12 +473,6 @@ namespace Barotrauma.Items.Components
delayedCorrectionCoroutine = null;
}
if (AITarget != null)
{
AITarget.Remove();
AITarget = null;
}
RemoveComponentSpecific();
}
@@ -496,11 +489,6 @@ namespace Barotrauma.Items.Components
loopingSoundChannel = null;
}
#endif
if (AITarget != null)
{
AITarget.Remove();
AITarget = null;
}
ShallowRemoveComponentSpecific();
}
@@ -788,6 +776,7 @@ namespace Barotrauma.Items.Components
public virtual void Reset()
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, originalElement);
if (this is Pickable) { canBePicked = true; }
ParseMsg();
OverrideRequiredItems(originalElement);
}
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using System;
using System.Linq;
using System.Xml.Linq;
@@ -84,6 +83,8 @@ namespace Barotrauma.Items.Components
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
if (progressTimer > deconstructTime)
{
int emptySlots = outputContainer.Inventory.Items.Where(i => i == null).Count();
foreach (DeconstructItem deconstructProduct in targetItem.Prefab.DeconstructItems)
{
float percentageHealth = targetItem.Condition / targetItem.Prefab.Health;
@@ -100,13 +101,14 @@ namespace Barotrauma.Items.Components
itemPrefab.Health * deconstructProduct.OutCondition;
//container full, drop the items outside the deconstructor
if (outputContainer.Inventory.Items.All(i => i != null))
if (emptySlots <= 0)
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, item.Position, item.Submarine, condition);
}
else
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, outputContainer.Inventory, condition);
emptySlots--;
}
}
@@ -196,6 +198,7 @@ namespace Barotrauma.Items.Components
if (inputContainer.Inventory.Items.All(i => i == null)) { active = false; }
IsActive = active;
currPowerConsumption = IsActive ? powerConsumption : 0.0f;
#if SERVER
if (user != null)
@@ -3,7 +3,6 @@ using System;
using System.Globalization;
using System.Xml.Linq;
using Barotrauma.Networking;
using Lidgren.Network;
namespace Barotrauma.Items.Components
{
@@ -98,11 +97,17 @@ namespace Barotrauma.Items.Components
UpdatePropellerDamage(deltaTime);
if (item.AiTarget != null)
{
var aiTarget = item.AiTarget;
aiTarget.SoundRange = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, currForce.Length() / maxForce);
}
if (item.CurrentHull != null)
{
item.CurrentHull.AiTarget.SoundRange = Math.Max(currForce.Length(), item.CurrentHull.AiTarget.SoundRange);
var aiTarget = item.CurrentHull.AiTarget;
float noise = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, currForce.Length() / maxForce);
aiTarget.SoundRange = Math.Max(noise, aiTarget.SoundRange);
}
#if CLIENT
for (int i = 0; i < 5; i++)
{
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -12,6 +12,8 @@ namespace Barotrauma.Items.Components
private float maxFlow;
private float? targetLevel;
private float controlLockTimer;
private bool hasPower;
@@ -57,18 +59,24 @@ namespace Barotrauma.Items.Components
currFlow = 0.0f;
hasPower = false;
controlLockTimer -= deltaTime;
if (targetLevel != null)
{
float hullPercentage = 0.0f;
if (item.CurrentHull != null) hullPercentage = (item.CurrentHull.WaterVolume / item.CurrentHull.Volume) * 100.0f;
if (item.CurrentHull != null) { hullPercentage = (item.CurrentHull.WaterVolume / item.CurrentHull.Volume) * 100.0f; }
FlowPercentage = ((float)targetLevel - hullPercentage) * 10.0f;
if (controlLockTimer <= 0.0f)
{
targetLevel = null;
}
}
currPowerConsumption = powerConsumption * Math.Abs(flowPercentage / 100.0f);
//pumps consume more power when in a bad condition
currPowerConsumption *= MathHelper.Lerp(2.0f, 1.0f, item.Condition / item.MaxCondition);
if (voltage < minVoltage) return;
if (voltage < minVoltage) { return; }
UpdateProjSpecific(deltaTime);
@@ -109,6 +117,7 @@ namespace Barotrauma.Items.Components
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempSpeed))
{
flowPercentage = MathHelper.Clamp(tempSpeed, -100.0f, 100.0f);
controlLockTimer = 0.1f;
}
}
else if (connection.Name == "set_targetlevel")
@@ -116,6 +125,7 @@ namespace Barotrauma.Items.Components
if (float.TryParse(signal, NumberStyles.Any, CultureInfo.InvariantCulture, out float tempTarget))
{
targetLevel = MathHelper.Clamp((tempTarget + 100.0f) / 2.0f, 0.0f, 100.0f);
controlLockTimer = 0.1f;
}
}
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -271,7 +270,7 @@ namespace Barotrauma.Items.Components
//calculate how much external power there is in the grid
//(power coming from somewhere else than this reactor, e.g. batteries)
float externalPower = CurrPowerConsumption - pt.CurrPowerConsumption;
float externalPower = Math.Max(CurrPowerConsumption - pt.CurrPowerConsumption, 0);
//reduce the external power from the load to prevent overloading the grid
load = Math.Max(load, pt.PowerLoad - externalPower);
}
@@ -288,10 +287,17 @@ namespace Barotrauma.Items.Components
if (item.CurrentHull != null)
{
//the sound can be heard from 20 000 display units away when running at full power
item.CurrentHull.SoundRange = Math.Max(
(-currPowerConsumption / MaxPowerOutput) * 20000.0f,
item.CurrentHull.AiTarget.SoundRange);
var aiTarget = item.CurrentHull.AiTarget;
float range = Math.Abs(currPowerConsumption) / MaxPowerOutput;
float noise = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range);
aiTarget.SoundRange = Math.Max(aiTarget.SoundRange, noise);
}
if (item.AiTarget != null)
{
var aiTarget = item.AiTarget;
float range = Math.Abs(currPowerConsumption) / MaxPowerOutput;
aiTarget.SoundRange = MathHelper.Lerp(aiTarget.MinSoundRange, aiTarget.MaxSoundRange, range);
}
}
@@ -439,6 +445,8 @@ namespace Barotrauma.Items.Components
{
base.UpdateBroken(deltaTime, cam);
item.SendSignal(0, ((int)(temperature * 100.0f)).ToString(), "temperature_out", null);
currPowerConsumption = 0.0f;
Temperature -= deltaTime * 1000.0f;
targetFissionRate = Math.Max(targetFissionRate - deltaTime * 10.0f, 0.0f);
@@ -114,9 +114,9 @@ namespace Barotrauma.Items.Components
if (value == Mode.Passive)
{
currentPingIndex = -1;
if (item.CurrentHull != null)
if (item.AiTarget != null)
{
item.CurrentHull.AiTarget.SectorDegrees = 360.0f;
item.AiTarget.SectorDegrees = 360.0f;
}
}
#if CLIENT
@@ -168,15 +168,10 @@ namespace Barotrauma.Items.Components
var activePing = activePings[currentPingIndex];
if (activePing.State > 1.0f)
{
if (item.CurrentHull != null)
{
item.CurrentHull.AiTarget.SoundRange = Math.Max(Range * activePing.State / zoom, item.CurrentHull.AiTarget.SoundRange);
item.CurrentHull.AiTarget.SectorDegrees = activePing.IsDirectional ? DirectionalPingSector : 360.0f;
item.CurrentHull.AiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
}
if (item.AiTarget != null)
{
item.AiTarget.SoundRange = Math.Max(Range * activePing.State / zoom, item.AiTarget.SoundRange);
float range = MathUtils.InverseLerp(item.AiTarget.MinSoundRange, item.AiTarget.MaxSoundRange, Range * activePing.State / zoom);
item.AiTarget.SoundRange = MathHelper.Lerp(item.AiTarget.MinSoundRange, item.AiTarget.MaxSoundRange, range);
item.AiTarget.SectorDegrees = activePing.IsDirectional ? DirectionalPingSector : 360.0f;
item.AiTarget.SectorDir = new Vector2(pingDirection.X, -pingDirection.Y);
}
@@ -200,9 +195,9 @@ namespace Barotrauma.Items.Components
}
else
{
if (item.CurrentHull != null)
if (item.AiTarget != null)
{
item.CurrentHull.AiTarget.SectorDegrees = 360.0f;
item.AiTarget.SectorDegrees = 360.0f;
}
currentPingIndex = -1;
aiPingCheckPending = false;
@@ -345,7 +340,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Client c)
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c)
{
bool isActive = msg.ReadBoolean();
bool directionalPing = useDirectionalPing;
@@ -388,7 +383,7 @@ namespace Barotrauma.Items.Components
#endif
}
public void ServerWrite(Lidgren.Network.NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(currentMode == Mode.Active);
if (currentMode == Mode.Active)
@@ -522,7 +522,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerRead(ClientNetObject type, Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c)
public void ServerRead(ClientNetObject type, IReadMessage msg, Barotrauma.Networking.Client c)
{
bool autoPilot = msg.ReadBoolean();
bool dockingButtonClicked = msg.ReadBoolean();
@@ -537,8 +537,8 @@ namespace Barotrauma.Items.Components
if (maintainPos)
{
newPosToMaintain = new Vector2(
msg.ReadFloat(),
msg.ReadFloat());
msg.ReadSingle(),
msg.ReadSingle());
}
else
{
@@ -547,7 +547,7 @@ namespace Barotrauma.Items.Components
}
else
{
newSteeringInput = new Vector2(msg.ReadFloat(), msg.ReadFloat());
newSteeringInput = new Vector2(msg.ReadSingle(), msg.ReadSingle());
}
if (!item.CanClientAccess(c)) return;
@@ -587,7 +587,7 @@ namespace Barotrauma.Items.Components
unsentChanges = true;
}
public void ServerWrite(Lidgren.Network.NetBuffer msg, Barotrauma.Networking.Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Barotrauma.Networking.Client c, object[] extraData = null)
{
msg.Write(autoPilot);
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -64,6 +64,13 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(false, false)]
public bool Overload
{
get;
set;
}
//can the component transfer power
private bool canTransfer;
public bool CanTransfer
@@ -115,6 +122,8 @@ namespace Barotrauma.Items.Components
{
base.UpdateBroken(deltaTime, cam);
Overload = false;
if (!isBroken)
{
powerLoad = 0.0f;
@@ -128,7 +137,8 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
RefreshConnections();
if (!CanTransfer) return;
if (!CanTransfer) { return; }
if (isBroken)
{
@@ -143,6 +153,8 @@ namespace Barotrauma.Items.Components
return;
}
Overload = false;
//reset and recalculate the power generated/consumed
//by the constructions connected to the grid
fullPower = 0.0f;
@@ -156,10 +168,11 @@ namespace Barotrauma.Items.Components
foreach (Powered p in connectedList)
{
PowerTransfer pt = p as PowerTransfer;
if (pt == null || pt.updateCount == 0) continue;
if (pt == null || pt.updateCount == 0) { continue; }
if (pt is RelayComponent != this is RelayComponent) continue;
if (pt is RelayComponent != this is RelayComponent) { continue; }
pt.Overload = false;
pt.powerLoad += (fullLoad - pt.powerLoad) / inertia;
pt.currPowerConsumption += (-fullPower - pt.currPowerConsumption) / inertia;
@@ -173,38 +186,38 @@ namespace Barotrauma.Items.Components
pt.Item.SendSignal(0, "", "power", null, voltage);
pt.Item.SendSignal(0, "", "power_out", null, voltage);
#if CLIENT
//damage the item if voltage is too high
//(except if running as a client)
if (GameMain.Client != null) continue;
#endif
//items in a bad condition are more sensitive to overvoltage
float maxOverVoltage = MathHelper.Lerp(OverloadVoltage * 0.75f, OverloadVoltage, item.Condition / item.MaxCondition);
float maxOverVoltage = MathHelper.Lerp(OverloadVoltage * 0.75f, OverloadVoltage, pt.item.Condition / pt.item.MaxCondition);
maxOverVoltage = Math.Max(OverloadVoltage, 1.0f);
//if the item can't be fixed, don't allow it to break
if (!item.Repairables.Any() || !CanBeOverloaded) continue;
if (!pt.item.Repairables.Any() || !pt.CanBeOverloaded) { continue; }
//relays don't blow up if the power is higher than load, only if the output is high enough
//(i.e. enough power passing through the relay)
if (this is RelayComponent) continue;
if (pt is RelayComponent) { continue; }
if (-pt.currPowerConsumption < Math.Max(pt.powerLoad, 200.0f) * maxOverVoltage) continue;
if (-pt.currPowerConsumption < Math.Max(pt.powerLoad, 200.0f) * maxOverVoltage) { continue; }
pt.Overload = true;
#if CLIENT
//damage the item if voltage is too high
//(except if running as a client)
if (GameMain.Client != null) { continue; }
#endif
float prevCondition = pt.item.Condition;
pt.item.Condition -= deltaTime * 10.0f;
if (pt.item.Condition <= 0.0f && prevCondition > 0.0f)
{
#if CLIENT
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: item.CurrentHull);
SoundPlayer.PlaySound("zap", item.WorldPosition, hullGuess: pt.item.CurrentHull);
Vector2 baseVel = Rand.Vector(300.0f);
for (int i = 0; i < 10; i++)
{
var particle = GameMain.ParticleManager.CreateParticle("spark", pt.item.WorldPosition,
baseVel + Rand.Vector(100.0f), 0.0f, item.CurrentHull);
baseVel + Rand.Vector(100.0f), 0.0f, pt.item.CurrentHull);
if (particle != null) particle.Size *= Rand.Range(0.5f, 1.0f);
}
@@ -214,8 +227,8 @@ namespace Barotrauma.Items.Components
GameMain.GameSession.EventManager.CurrentIntensity : 0.5f;
//higher probability for fires if the current intensity is low
if (FireProbability > 0.0f &&
Rand.Range(0.0f, 1.0f) < MathHelper.Lerp(FireProbability, FireProbability * 0.1f, currentIntensity))
if (pt.FireProbability > 0.0f &&
Rand.Range(0.0f, 1.0f) < MathHelper.Lerp(pt.FireProbability, pt.FireProbability * 0.1f, currentIntensity))
{
new FireSource(pt.item.WorldPosition);
}
@@ -409,11 +409,11 @@ namespace Barotrauma.Items.Components
private bool OnProjectileCollision(Fixture target, Vector2 collisionNormal)
{
if (User != null && User.Removed) User = null;
if (User != null && User.Removed) { User = null; }
if (IgnoredBodies.Contains(target.Body)) return false;
if (IgnoredBodies.Contains(target.Body)) { return false; }
if (target.UserData is Item) return false;
if (target.UserData is Item) { return false; }
if (target.CollisionCategories == Physics.CollisionCharacter && !(target.Body.UserData is Limb))
{
@@ -447,10 +447,21 @@ namespace Barotrauma.Items.Components
if (attack != null) { attackResult = attack.DoDamage(User, structure, item.WorldPosition, 1.0f); }
}
if (character != null) character.LastDamageSource = item;
ApplyStatusEffects(ActionType.OnUse, 1.0f, character, target.Body.UserData as Limb, user: user);
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character, target.Body.UserData as Limb, user: user);
if (character != null) { character.LastDamageSource = item; }
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{
ApplyStatusEffects(ActionType.OnUse, 1.0f, character, target.Body.UserData as Limb, user: user);
ApplyStatusEffects(ActionType.OnImpact, 1.0f, character, target.Body.UserData as Limb, user: user);
#if SERVER
if (GameMain.NetworkMember.IsServer)
{
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnUse });
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnImpact });
}
#endif
}
item.body.FarseerBody.OnCollision -= OnProjectileCollision;
item.body.CollisionCategories = Physics.CollisionItem;
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
@@ -10,12 +9,15 @@ namespace Barotrauma.Items.Components
partial class Repairable : ItemComponent, IServerSerializable, IClientSerializable
{
public static float SkillIncreasePerRepair = 5.0f;
public static float SkillIncreasePerSabotage = 3.0f;
private string header;
private float deteriorationTimer;
private float deteriorateAlwaysResetTimer;
bool wasBroken;
bool wasGoodCondition;
public float LastActiveTime;
@@ -40,14 +42,21 @@ namespace Barotrauma.Items.Components
set;
}
[Serialize(50.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The item won't deteriorate spontaneously if the condition is below this value. For example, if set to 10, the condition will spontaneously drop to 10 and then stop dropping (unless the item is damaged further by external factors).")]
[Serialize(50.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The item won't deteriorate spontaneously if the condition is below this value. For example, if set to 10, the condition will spontaneously drop to 10 and then stop dropping (unless the item is damaged further by external factors). Percentages of max condition.")]
public float MinDeteriorationCondition
{
get;
set;
}
[Serialize(80.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The condition of the item has to be below this before the repair UI becomes usable.")]
[Serialize(0f, true)]
public float MinSabotageCondition
{
get;
set;
}
[Serialize(80.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, ToolTip = "The condition of the item has to be below this before the repair UI becomes usable. Percentages of max condition.")]
public float ShowRepairUIThreshold
{
get;
@@ -76,16 +85,20 @@ namespace Barotrauma.Items.Components
set;
}
private Character currentFixer;
public Character CurrentFixer
public Character CurrentFixer { get; private set; }
public enum FixActions : int
{
get { return currentFixer; }
set
{
if (currentFixer == value || item.IsFullCondition) return;
if (currentFixer != null) currentFixer.AnimController.Anim = AnimController.Animation.None;
currentFixer = value;
}
None = 0,
Repair = 1,
Sabotage = 2
}
private FixActions currentFixerAction = FixActions.None;
public FixActions CurrentFixerAction
{
get => currentFixerAction;
private set { currentFixerAction = value; }
}
public Repairable(Item item, XElement element)
@@ -105,18 +118,41 @@ namespace Barotrauma.Items.Components
public override void OnItemLoaded()
{
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
#if SERVER
//let the clients know the initial deterioration delay
item.CreateServerEvent(this);
#endif
}
partial void InitProjSpecific(XElement element);
public void StartRepairing(Character character)
public bool StartRepairing(Character character, FixActions action)
{
CurrentFixer = character;
if (character == null || character.IsDead || action == FixActions.None)
{
DebugConsole.ThrowError("Invalid repair command!");
return false;
}
else
{
CurrentFixer = character;
CurrentFixerAction = action;
return true;
}
}
public bool StopRepairing(Character character)
{
if (CurrentFixer == character)
{
CurrentFixer.AnimController.Anim = AnimController.Animation.None;
CurrentFixer = null;
currentFixerAction = FixActions.None;
#if SERVER
item.CreateServerEvent(this);
#endif
return true;
}
else
{
return false;
}
}
public override void UpdateBroken(float deltaTime, Camera cam)
@@ -129,7 +165,7 @@ namespace Barotrauma.Items.Components
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
item.Condition = item.Prefab.Health;
#if SERVER
//let the clients know the initial deterioration delay
//let the clients know the deterioration delay
item.CreateServerEvent(this);
#endif
}
@@ -140,6 +176,18 @@ namespace Barotrauma.Items.Components
if (CurrentFixer == null)
{
if (deteriorateAlwaysResetTimer > 0.0f)
{
deteriorateAlwaysResetTimer -= deltaTime;
if (deteriorateAlwaysResetTimer <= 0.0f)
{
DeteriorateAlways = false;
#if SERVER
//let the clients know the deterioration delay
item.CreateServerEvent(this);
#endif
}
}
if (!ShouldDeteriorate()) { return; }
if (item.Condition > 0.0f)
{
@@ -155,7 +203,7 @@ namespace Barotrauma.Items.Components
return;
}
if (item.Condition > MinDeteriorationCondition)
if (item.ConditionPercentage > MinDeteriorationCondition)
{
item.Condition -= DeteriorationSpeed * deltaTime;
}
@@ -163,10 +211,9 @@ namespace Barotrauma.Items.Components
return;
}
if (Item.IsFullCondition || CurrentFixer.SelectedConstruction != item || !currentFixer.CanInteractWith(item))
if (CurrentFixer != null && (CurrentFixer.SelectedConstruction != item || !CurrentFixer.CanInteractWith(item) || CurrentFixer.IsDead))
{
currentFixer.AnimController.Anim = AnimController.Animation.None;
currentFixer = null;
StopRepairing(CurrentFixer);
return;
}
@@ -177,44 +224,89 @@ namespace Barotrauma.Items.Components
float successFactor = requiredSkills.Count == 0 ? 1.0f : 0.0f;
//item must have been below the repair threshold for the player to get an achievement or XP for repairing it
if (item.Condition < ShowRepairUIThreshold)
if (item.ConditionPercentage < ShowRepairUIThreshold)
{
wasBroken = true;
}
if (item.ConditionPercentage > MinSabotageCondition)
{
wasGoodCondition = true;
}
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
if (fixDuration <= 0.0f)
if (currentFixerAction == FixActions.Repair)
{
item.Condition = item.MaxCondition;
if (fixDuration <= 0.0f)
{
item.Condition = item.MaxCondition;
}
else
{
float conditionIncrease = deltaTime / (fixDuration / item.MaxCondition);
item.Condition += conditionIncrease;
#if SERVER
GameMain.Server.KarmaManager.OnItemRepaired(CurrentFixer, this, conditionIncrease);
#endif
}
if (item.IsFullCondition)
{
if (wasBroken)
{
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
}
SteamAchievementManager.OnItemRepaired(item, CurrentFixer);
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
wasBroken = false;
}
StopRepairing(CurrentFixer);
}
}
else if (currentFixerAction == FixActions.Sabotage)
{
if (fixDuration <= 0.0f)
{
item.Condition = item.MaxCondition * (MinSabotageCondition / 100);
}
else
{
float conditionDecrease = deltaTime / (fixDuration / item.MaxCondition);
item.Condition -= conditionDecrease;
}
if (item.ConditionPercentage <= MinSabotageCondition)
{
if (wasGoodCondition)
{
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreasePerSabotage / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
}
deteriorationTimer = 0.0f;
deteriorateAlwaysResetTimer = item.Condition / DeteriorationSpeed;
DeteriorateAlways = true;
item.Condition = item.MaxCondition * (MinSabotageCondition / 100);
wasGoodCondition = false;
}
StopRepairing(CurrentFixer);
}
}
else
{
float conditionIncrease = deltaTime / (fixDuration / item.MaxCondition);
item.Condition += conditionIncrease;
#if SERVER
GameMain.Server.KarmaManager.OnItemRepaired(CurrentFixer, this, conditionIncrease);
#endif
}
if (wasBroken && item.IsFullCondition)
{
foreach (Skill skill in requiredSkills)
{
float characterSkillLevel = CurrentFixer.GetSkillLevel(skill.Identifier);
CurrentFixer.Info.IncreaseSkillLevel(skill.Identifier,
SkillIncreasePerRepair / Math.Max(characterSkillLevel, 1.0f),
CurrentFixer.WorldPosition + Vector2.UnitY * 100.0f);
}
SteamAchievementManager.OnItemRepaired(item, currentFixer);
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
wasBroken = false;
#if SERVER
item.CreateServerEvent(this);
#endif
throw new NotImplementedException(currentFixerAction.ToString());
}
}
partial void UpdateProjSpecific(float deltaTime);
private bool ShouldDeteriorate()
@@ -218,6 +218,16 @@ namespace Barotrauma.Items.Components
public void SetWire(int index, Wire wire)
{
Wire previousWire = wires[index];
if (wire != previousWire && previousWire != null)
{
var otherConnection = previousWire.OtherConnection(this);
if (otherConnection != null)
{
otherConnection.recipientsDirty = true;
}
}
wires[index] = wire;
recipientsDirty = true;
if (wire != null)
@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -19,7 +18,9 @@ namespace Barotrauma.Items.Components
/// Wires that have been disconnected from the panel, but not removed completely (visible at the bottom of the connection panel).
/// </summary>
public readonly HashSet<Wire> DisconnectedWires = new HashSet<Wire>();
private List<ushort> disconnectedWireIds;
[Serialize(false, true), Editable(ToolTip = "Locked connection panels cannot be rewired in-game.")]
public bool Locked
{
@@ -64,6 +65,19 @@ namespace Barotrauma.Items.Components
{
c.ConnectLinked();
}
if (disconnectedWireIds != null)
{
foreach (ushort disconnectedWireId in disconnectedWireIds)
{
if (!(Entity.FindEntityByID(disconnectedWireId) is Item wireItem)) { continue; }
Wire wire = wireItem.GetComponent<Wire>();
if (wire != null)
{
DisconnectedWires.Add(wire);
}
}
}
}
public override void OnItemLoaded()
@@ -193,6 +207,8 @@ namespace Barotrauma.Items.Components
{
loadedConnections[i].wireId.CopyTo(Connections[i].wireId, 0);
}
disconnectedWireIds = element.GetAttributeUshortArray("disconnectedwires", new ushort[0]).ToList();
}
public override XElement Save(XElement parentElement)
@@ -204,6 +220,11 @@ namespace Barotrauma.Items.Components
c.Save(componentElement);
}
if (DisconnectedWires.Count > 0)
{
componentElement.Add(new XAttribute("disconnectedwires", string.Join(",", DisconnectedWires.Select(w => w.Item.ID))));
}
return componentElement;
}
@@ -214,6 +235,14 @@ namespace Barotrauma.Items.Components
protected override void RemoveComponentSpecific()
{
foreach (Wire wire in DisconnectedWires.ToList())
{
if (wire.OtherConnection(null) == null) //wire not connected to anything else
{
wire.Item.Drop(null);
}
}
DisconnectedWires.Clear();
foreach (Connection c in Connections)
{
@@ -233,7 +262,7 @@ namespace Barotrauma.Items.Components
}
}
public void ClientWrite(NetBuffer msg, object[] extraData = null)
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
{
foreach (Connection connection in Connections)
{
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
@@ -2,7 +2,6 @@
using System;
using System.Xml.Linq;
using Barotrauma.Networking;
using Lidgren.Network;
#if CLIENT
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Lights;
@@ -251,10 +250,6 @@ namespace Barotrauma.Items.Components
light.Range = range;
#endif
}
if (AITarget != null)
{
UpdateAITarget(AITarget);
}
if (item.AiTarget != null)
{
UpdateAITarget(item.AiTarget);
@@ -267,6 +262,7 @@ namespace Barotrauma.Items.Components
public override void UpdateBroken(float deltaTime, Camera cam)
{
light.Color = Color.Transparent;
lightBrightness = 0.0f;
}
protected override void RemoveComponentSpecific()
@@ -298,7 +294,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(IsOn);
}
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Xml.Linq;
@@ -104,12 +103,12 @@ namespace Barotrauma.Items.Components
IsOn = on;
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
msg.Write(isOn);
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
SetState(msg.ReadBoolean(), true);
}
@@ -1,5 +1,4 @@
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -282,7 +281,7 @@ namespace Barotrauma.Items.Components
Structure attachTarget = Structure.GetAttachTarget(item.WorldPosition);
canPlaceNode = attachTarget != null;
sub = attachTarget?.Submarine;
sub = sub ?? attachTarget?.Submarine;
newNodePos = sub == null ?
item.WorldPosition :
item.WorldPosition - sub.Position - sub.HiddenSubPosition;
@@ -333,7 +332,8 @@ namespace Barotrauma.Items.Components
}
else
{
newNodePos = RoundNode(item.Position, item.CurrentHull) - sub.HiddenSubPosition;
newNodePos = RoundNode(item.Position, item.CurrentHull);
if (sub != null) { newNodePos -= sub.HiddenSubPosition; }
canPlaceNode = true;
}
@@ -724,7 +724,7 @@ namespace Barotrauma.Items.Components
base.RemoveComponentSpecific();
}
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
{
int eventIndex = msg.ReadRangedInteger(0, (int)Math.Ceiling(MaxNodeCount / (float)MaxNodesPerNetworkEvent));
int nodeCount = msg.ReadRangedInteger(0, MaxNodesPerNetworkEvent);
@@ -738,7 +738,7 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < nodeCount; i++)
{
nodePositions[nodeStartIndex + i] = new Vector2(msg.ReadFloat(), msg.ReadFloat());
nodePositions[nodeStartIndex + i] = new Vector2(msg.ReadSingle(), msg.ReadSingle());
}
if (nodePositions.Any(n => !MathUtils.IsValid(n)))
@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -674,7 +673,7 @@ namespace Barotrauma.Items.Components
}
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
Item item = (Item)extraData[2];
msg.Write(item.Removed ? (ushort)0 : item.ID);
@@ -1,6 +1,5 @@
using Barotrauma.Items.Components;
using Barotrauma.Networking;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -375,7 +374,7 @@ namespace Barotrauma
}
}
public void SharedWrite(NetBuffer msg, object[] extraData = null)
public void SharedWrite(IWriteMessage msg, object[] extraData = null)
{
for (int i = 0; i < capacity; i++)
{
@@ -3,7 +3,6 @@ using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -127,6 +126,24 @@ namespace Barotrauma
}
}
public delegate bool InventoryFilter(Inventory inventory);
public Inventory FindParentInventory(InventoryFilter filter)
{
if (parentInventory != null)
{
if (filter(parentInventory))
{
return parentInventory;
}
var owner = parentInventory.Owner as Item;
if (owner != null)
{
return owner.FindParentInventory(filter);
}
}
return null;
}
private Item container;
public Item Container
{
@@ -597,12 +614,15 @@ namespace Barotrauma
case "fabricate":
case "fabricable":
case "fabricableitem":
case "upgrade":
break;
case "staticbody":
StaticBodyConfig = subElement;
break;
case "aitarget":
aiTarget = new AITarget(this, subElement);
aiTarget.SoundRange = aiTarget.MinSoundRange;
aiTarget.SightRange = aiTarget.MinSightRange;
break;
default:
ItemComponent ic = ItemComponent.Load(subElement, this, itemPrefab.ConfigFile);
@@ -674,9 +694,6 @@ namespace Barotrauma
}
InitProjSpecific();
InsertToList();
ItemList.Add(this);
if (callOnItemLoaded)
{
@@ -686,6 +703,9 @@ namespace Barotrauma
}
}
InsertToList();
ItemList.Add(this);
DebugConsole.Log("Created " + Name + " (" + ID + ")");
}
@@ -988,7 +1008,24 @@ namespace Barotrauma
}
return false;
}
private bool ConditionalMatches(PropertyConditional conditional)
{
if (string.IsNullOrEmpty(conditional.TargetItemComponentName))
{
if (!conditional.Matches(this)) { return false; }
}
else
{
foreach (ItemComponent component in components)
{
if (component.Name != conditional.TargetItemComponentName) { continue; }
if (!conditional.Matches(component)) { return false; }
}
}
return true;
}
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null, bool isNetworkEvent = false)
{
if (!hasStatusEffectsOfType[(int)type]) { return; }
@@ -1103,6 +1140,7 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
//aitarget goes silent/invisible if the components don't keep it active
if (aiTarget != null)
{
@@ -1131,7 +1169,11 @@ namespace Barotrauma
foreach (ItemComponent ic in components)
{
if (ic.Parent != null) ic.IsActive = ic.Parent.IsActive;
if (ic.Parent != null) { ic.IsActive = ic.Parent.IsActive; }
if (ic.IsActiveConditionals != null)
{
ic.IsActive = ic.IsActiveConditionals.All(conditional => ConditionalMatches(conditional));
}
#if CLIENT
if (!ic.WasUsed)
@@ -1827,7 +1869,7 @@ namespace Barotrauma
return allProperties;
}
private void WritePropertyChange(NetBuffer msg, object[] extraData, bool inGameEditableOnly)
private void WritePropertyChange(IWriteMessage msg, object[] extraData, bool inGameEditableOnly)
{
var allProperties = inGameEditableOnly ? GetProperties<InGameEditable>() : GetProperties<Editable>();
SerializableProperty property = extraData[1] as SerializableProperty;
@@ -1836,7 +1878,7 @@ namespace Barotrauma
var propertyOwner = allProperties.Find(p => p.Second == property);
if (allProperties.Count > 1)
{
msg.WriteRangedInteger(0, allProperties.Count - 1, allProperties.FindIndex(p => p.Second == property));
msg.WriteRangedIntegerDeprecated(0, allProperties.Count - 1, allProperties.FindIndex(p => p.Second == property));
}
object value = property.GetValue(propertyOwner.First);
@@ -1908,7 +1950,7 @@ namespace Barotrauma
}
}
private void ReadPropertyChange(NetBuffer msg, bool inGameEditableOnly, Client sender = null)
private void ReadPropertyChange(IReadMessage msg, bool inGameEditableOnly, Client sender = null)
{
var allProperties = inGameEditableOnly ? GetProperties<InGameEditable>() : GetProperties<Editable>();
if (allProperties.Count == 0) { return; }
@@ -1940,7 +1982,7 @@ namespace Barotrauma
}
else if (type == typeof(float))
{
float val = msg.ReadFloat();
float val = msg.ReadSingle();
if (allowEditing) property.TrySetValue(parentObject, val);
}
else if (type == typeof(int))
@@ -1960,17 +2002,17 @@ namespace Barotrauma
}
else if (type == typeof(Vector2))
{
Vector2 val = new Vector2(msg.ReadFloat(), msg.ReadFloat());
Vector2 val = new Vector2(msg.ReadSingle(), msg.ReadSingle());
if (allowEditing) property.TrySetValue(parentObject, val);
}
else if (type == typeof(Vector3))
{
Vector3 val = new Vector3(msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat());
Vector3 val = new Vector3(msg.ReadSingle(), msg.ReadSingle(), msg.ReadSingle());
if (allowEditing) property.TrySetValue(parentObject, val);
}
else if (type == typeof(Vector4))
{
Vector4 val = new Vector4(msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat(), msg.ReadFloat());
Vector4 val = new Vector4(msg.ReadSingle(), msg.ReadSingle(), msg.ReadSingle(), msg.ReadSingle());
if (allowEditing) property.TrySetValue(parentObject, val);
}
else if (type == typeof(Point))
@@ -2038,7 +2080,7 @@ namespace Barotrauma
{
return null;
}
Rectangle rect = element.GetAttributeRect("rect", Rectangle.Empty);
if (rect.Width == 0 && rect.Height == 0)
{
@@ -2090,7 +2132,7 @@ namespace Barotrauma
foreach (XElement subElement in element.Elements())
{
ItemComponent component = unloadedComponents.Find(x => x.Name == subElement.Name.ToString());
if (component == null) continue;
if (component == null) { continue; }
component.Load(subElement);
unloadedComponents.Remove(component);
@@ -2104,6 +2146,11 @@ namespace Barotrauma
item.SetActiveSprite();
if (submarine?.GameVersion != null)
{
SerializableProperty.UpgradeGameVersion(item, item.Prefab.ConfigElement, submarine.GameVersion);
}
foreach (ItemComponent component in item.components)
{
component.OnItemLoaded();
@@ -2164,6 +2211,7 @@ namespace Barotrauma
SerializableProperties = SerializableProperty.DeserializeProperties(this, Prefab.ConfigElement);
Sprite.ReloadXML();
SpriteDepth = Sprite.Depth;
condition = Prefab.Health;
components.ForEach(c => c.Reset());
}
@@ -2249,4 +2297,4 @@ namespace Barotrauma
partial void RemoveProjSpecific();
}
}
}
@@ -1,7 +1,6 @@
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -229,7 +228,13 @@ namespace Barotrauma
surface = rect.Y - rect.Height;
aiTarget = new AITarget(this);
aiTarget = new AITarget(this)
{
MinSightRange = 2000,
MaxSightRange = 5000,
MaxSoundRange = 5000,
SoundRange = 0
};
hullList.Add(this);
@@ -418,13 +423,14 @@ namespace Barotrauma
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
UpdateProjSpecific(deltaTime, cam);
Oxygen -= OxygenDeteriorationSpeed * deltaTime;
FireSource.UpdateAll(FireSources, deltaTime);
aiTarget.SightRange = Submarine == null ? 0.0f : Math.Max(Submarine.Velocity.Length() * 2000.0f, AITarget.StaticSightRange);
aiTarget.SightRange = Submarine == null ? aiTarget.MinSightRange : Submarine.Velocity.Length() / 2 * aiTarget.MaxSightRange;
aiTarget.SoundRange -= deltaTime * 1000.0f;
if (!update)
@@ -5,7 +5,6 @@ using Barotrauma.RuinGeneration;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Factories;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -1673,7 +1672,7 @@ namespace Barotrauma
loaded = null;
}
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
foreach (LevelWall levelWall in extraWalls)
{
@@ -1,6 +1,5 @@
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -122,7 +121,7 @@ namespace Barotrauma
return "LevelObject (" + ActivePrefab.Name + ")";
}
public void ServerWrite(NetBuffer msg, Client c)
public void ServerWrite(IWriteMessage msg, Client c)
{
for (int j = 0; j < Triggers.Count; j++)
{
@@ -3,7 +3,6 @@ using Barotrauma.Particles;
#endif
using Barotrauma.Networking;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -407,10 +406,10 @@ namespace Barotrauma
partial void RemoveProjSpecific();
public void ServerWrite(NetBuffer msg, Client c, object[] extraData = null)
public void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null)
{
LevelObject obj = extraData[0] as LevelObject;
msg.WriteRangedInteger(0, objects.Count, objects.IndexOf(obj));
msg.WriteRangedIntegerDeprecated(0, objects.Count, objects.IndexOf(obj));
obj.ServerWrite(msg, c);
}
}
@@ -2,7 +2,6 @@
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -598,7 +597,7 @@ namespace Barotrauma
return vel.ClampLength(ConvertUnits.ToDisplayUnits(ForceVelocityLimit)) * currentForceFluctuation;
}
public void ServerWrite(NetBuffer msg, Client c)
public void ServerWrite(IWriteMessage msg, Client c)
{
if (ForceFluctuationStrength > 0.0f)
{
@@ -339,6 +339,11 @@ namespace Barotrauma
hull.Update(deltaTime, cam);
}
foreach (Structure structure in Structure.WallList)
{
structure.Update(deltaTime, cam);
}
foreach (Gap gap in Gap.GapList)
{
gap.Update(deltaTime, cam);
@@ -40,6 +40,18 @@ namespace Barotrauma
get { return name; }
}
public string GetItemNameTextId()
{
var textId = $"entityname.{Identifier}";
return TextManager.ContainsTag(textId) ? textId : null;
}
public string GetHullNameTextId()
{
var textId = $"roomname.{Identifier}";
return TextManager.ContainsTag(textId) ? textId : null;
}
//Used to differentiate between items when saving/loading
//Allows changing the name of an item without breaking existing subs or having multiple items with the same name
public string Identifier
@@ -4,7 +4,6 @@ using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Factories;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
@@ -373,7 +372,12 @@ namespace Barotrauma
// Only add ai targets automatically to submarine/outpost walls
if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null && !Prefab.NoAITarget)
{
aiTarget = new AITarget(this);
aiTarget = new AITarget(this)
{
MinSightRange = 2000,
MaxSightRange = 5000,
MaxSoundRange = 0
};
}
InsertToList();
@@ -460,15 +464,16 @@ namespace Barotrauma
{
if (IsHorizontal)
{
xsections = (int)Math.Ceiling((float)rect.Width / WallSectionSize);
//equivalent to (int)Math.Ceiling((double)rect.Width / WallSectionSize) without the potential for floating point indeterminism
xsections = (rect.Width + WallSectionSize - 1) / WallSectionSize;
Sections = new WallSection[xsections];
width = (int)WallSectionSize;
width = WallSectionSize;
}
else
{
ysections = (int)Math.Ceiling((float)rect.Height / WallSectionSize);
ysections = (rect.Height + WallSectionSize - 1) / WallSectionSize;
Sections = new WallSection[ysections];
height = (int)WallSectionSize;
height = WallSectionSize;
}
}
@@ -1184,21 +1189,32 @@ namespace Barotrauma
ID = (ushort)int.Parse(element.Attribute("ID").Value)
};
SerializableProperty.DeserializeProperties(s, element);
foreach (XElement subElement in element.Elements())
{
switch (subElement.Name.ToString())
{
case "section":
int index = subElement.GetAttributeInt("i", -1);
if (index == -1) continue;
s.Sections[index].damage = subElement.GetAttributeFloat("damage", 0.0f);
if (index == -1) { continue; }
if (index < 0 || index >= s.SectionCount)
{
string errorMsg = $"Error while loading structure \"{s.Name}\". Section damage index out of bounds. Index: {index}, section count: {s.SectionCount}.";
DebugConsole.ThrowError(errorMsg);
GameAnalyticsManager.AddErrorEventOnce("Structure.Load:SectionIndexOutOfBounds", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
}
else
{
s.Sections[index].damage = subElement.GetAttributeFloat("damage", 0.0f);
}
break;
}
}
if (element.GetAttributeBool("flippedx", false)) s.FlipX(false);
if (element.GetAttributeBool("flippedy", false)) s.FlipY(false);
SerializableProperty.DeserializeProperties(s, element);
//structures with a body drop a shadow by default
if (element.Attribute("usedropshadow") == null)
@@ -1277,5 +1293,14 @@ namespace Barotrauma
{
SerializableProperties = SerializableProperty.DeserializeProperties(this, Prefab.ConfigElement);
}
public override void Update(float deltaTime, Camera cam)
{
base.Update(deltaTime, cam);
if (aiTarget != null)
{
aiTarget.SightRange = Submarine == null ? aiTarget.MinSightRange : Submarine.Velocity.Length() / 2 * aiTarget.MaxSightRange;
}
}
}
}
@@ -3,7 +3,6 @@ using Barotrauma.Networking;
using Barotrauma.RuinGeneration;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -1143,7 +1142,6 @@ namespace Barotrauma
savedSubmarines.Add(sub);
}
public static void RefreshSavedSub(string filePath)
{
string fullPath = Path.GetFullPath(filePath);
@@ -1154,12 +1152,15 @@ namespace Barotrauma
savedSubmarines[i].Dispose();
}
}
var sub = new Submarine(filePath);
if (!sub.IsFileCorrupted)
if (File.Exists(filePath))
{
savedSubmarines.Add(sub);
var sub = new Submarine(filePath);
if (!sub.IsFileCorrupted)
{
savedSubmarines.Add(sub);
}
savedSubmarines = savedSubmarines.OrderBy(s => s.filePath ?? "").ToList();
}
savedSubmarines = savedSubmarines.OrderBy(s => s.filePath ?? "").ToList();
}
public static void RefreshSavedSubs()
@@ -1210,6 +1211,15 @@ namespace Barotrauma
}
}
var contentPackageSubs = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.Submarine);
foreach (string subPath in contentPackageSubs)
{
if (!filePaths.Any(fp => Path.GetFullPath(fp) == Path.GetFullPath(subPath)))
{
filePaths.Add(subPath);
}
}
foreach (string path in filePaths)
{
var sub = new Submarine(path);
@@ -1,5 +1,4 @@
using Barotrauma.Items.Components;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
@@ -7,9 +6,9 @@ using System.Text;
namespace Barotrauma.Networking
{
enum ChatMessageType
public enum ChatMessageType
{
Default, Error, Dead, Server, Radio, Private, Console, MessageBox, Order, ServerLog
Default, Error, Dead, Server, Radio, Private, Console, MessageBox, Order, ServerLog, ServerMessageBox
}
partial class ChatMessage
@@ -1,4 +1,4 @@
using Lidgren.Network;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -12,7 +12,8 @@ namespace Barotrauma.Networking
public string Name;
public byte ID;
public UInt64 SteamID;
public Character.TeamType TeamID;
private Character character;
@@ -38,6 +39,12 @@ namespace Barotrauma.Networking
HasSpawned = true;
#if CLIENT
GameMain.GameSession?.CrewManager?.SetPlayerVoiceIconState(this, muted, mutedLocally);
if (character == GameMain.Client.Character && GameMain.Client.SpawnAsTraitor)
{
character.IsTraitor = true;
character.TraitorCurrentObjective = GameMain.Client.TraitorFirstObjective;
}
#endif
}
}
@@ -179,7 +186,7 @@ namespace Barotrauma.Networking
}
}
public void WritePermissions(NetBuffer msg)
public void WritePermissions(IWriteMessage msg)
{
msg.Write(ID);
msg.Write((UInt16)Permissions);
@@ -192,7 +199,7 @@ namespace Barotrauma.Networking
}
}
}
public static void ReadPermissions(NetBuffer inc, out ClientPermissions permissions, out List<DebugConsole.Command> permittedCommands)
public static void ReadPermissions(IReadMessage inc, out ClientPermissions permissions, out List<DebugConsole.Command> permittedCommands)
{
UInt16 permissionsInt = inc.ReadUInt16();
@@ -221,7 +228,7 @@ namespace Barotrauma.Networking
}
}
public void ReadPermissions(NetIncomingMessage inc)
public void ReadPermissions(IReadMessage inc)
{
ClientPermissions permissions = ClientPermissions.None;
List<DebugConsole.Command> permittedCommands = new List<DebugConsole.Command>();
@@ -7,7 +7,7 @@ using System.Xml.Linq;
namespace Barotrauma.Networking
{
[Flags]
enum ClientPermissions
public enum ClientPermissions
{
None = 0x0,
ManageRound = 0x1,
@@ -1,6 +1,4 @@
using Lidgren.Network;
namespace Barotrauma.Networking
namespace Barotrauma.Networking
{
interface INetSerializable { }
@@ -10,10 +8,10 @@ namespace Barotrauma.Networking
interface IClientSerializable : INetSerializable
{
#if CLIENT
void ClientWrite(NetBuffer msg, object[] extraData = null);
void ClientWrite(IWriteMessage msg, object[] extraData = null);
#endif
#if SERVER
void ServerRead(ClientNetObject type, NetBuffer msg, Client c);
void ServerRead(ClientNetObject type, IReadMessage msg, Client c);
#endif
}
@@ -23,10 +21,10 @@ namespace Barotrauma.Networking
interface IServerSerializable : INetSerializable
{
#if SERVER
void ServerWrite(NetBuffer msg, Client c, object[] extraData = null);
void ServerWrite(IWriteMessage msg, Client c, object[] extraData = null);
#endif
#if CLIENT
void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime);
void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime);
#endif
}
}
@@ -53,12 +53,12 @@ namespace Barotrauma
public float ExtinguishFireKarmaIncrease { get; set; }
private float allowedWireDisconnectionsPerMinute;
[Serialize(5.0f, true)]
public float AllowedWireDisconnectionsPerMinute
private int allowedWireDisconnectionsPerMinute;
[Serialize(5, true)]
public int AllowedWireDisconnectionsPerMinute
{
get { return allowedWireDisconnectionsPerMinute; }
set { allowedWireDisconnectionsPerMinute = Math.Max(0.0f, value); }
set { allowedWireDisconnectionsPerMinute = Math.Max(0, value); }
}
[Serialize(6.0f, true)]
@@ -76,6 +76,9 @@ namespace Barotrauma
[Serialize(1.0f, true)]
public float KickBanThreshold { get; set; }
[Serialize(0, true)]
public int KicksBeforeBan { get; set; }
[Serialize(10.0f, true)]
public float KarmaNotificationInterval { get; set; }
@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
namespace Barotrauma.Networking
{
@@ -1,5 +1,4 @@
using Lidgren.Network;
using System;
using System;
using System.Collections.Generic;
namespace Barotrauma.Networking
@@ -11,10 +10,10 @@ namespace Barotrauma.Networking
/// <summary>
/// Write the events to the outgoing message. The recipient parameter is only needed for ServerEntityEventManager
/// </summary>
protected void Write(NetOutgoingMessage msg, List<NetEntityEvent> eventsToSync, out List<NetEntityEvent> sentEvents, Client recipient = null)
protected void Write(IWriteMessage msg, List<NetEntityEvent> eventsToSync, out List<NetEntityEvent> sentEvents, Client recipient = null)
{
//write into a temporary buffer so we can write the number of events before the actual data
NetBuffer tempBuffer = new NetBuffer();
IWriteMessage tempBuffer = new WriteOnlyMessage();
sentEvents = new List<NetEntityEvent>();
@@ -22,7 +21,7 @@ namespace Barotrauma.Networking
foreach (NetEntityEvent e in eventsToSync)
{
//write into a temporary buffer so we can write the length before the actual data
NetBuffer tempEventBuffer = new NetBuffer();
IWriteMessage tempEventBuffer = new WriteOnlyMessage();
try
{
WriteEvent(tempEventBuffer, e, recipient);
@@ -67,7 +66,7 @@ namespace Barotrauma.Networking
tempBuffer.Write(e.EntityID);
tempBuffer.Write((byte)tempEventBuffer.LengthBytes);
tempBuffer.Write(tempEventBuffer);
tempBuffer.Write(tempEventBuffer.Buffer, 0, tempEventBuffer.LengthBytes);
tempBuffer.WritePadBits();
sentEvents.Add(e);
@@ -78,10 +77,10 @@ namespace Barotrauma.Networking
{
msg.Write(eventsToSync[0].ID);
msg.Write((byte)eventCount);
msg.Write(tempBuffer);
msg.Write(tempBuffer.Buffer, 0, tempBuffer.LengthBytes);
}
}
protected abstract void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null);
protected abstract void WriteEvent(IWriteMessage buffer, NetEntityEvent entityEvent, Client recipient = null);
}
}
@@ -1,5 +1,4 @@
using Barotrauma.Items.Components;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -9,9 +8,6 @@ namespace Barotrauma.Networking
{
enum ClientPacketHeader
{
REQUEST_AUTH, //ask the server if a password is needed, if so we'll get nonce for encryption
REQUEST_STEAMAUTH, //the same as REQUEST_AUTH, but in addition we want to authenticate the player's Steam ID
REQUEST_INIT, //ask the server to give you initialization
UPDATE_LOBBY, //update state in lobby
UPDATE_INGAME, //update state ingame
@@ -64,7 +60,9 @@ namespace Barotrauma.Networking
QUERY_STARTGAME, //ask the clients whether they're ready to start
STARTGAME, //start a new round
ENDGAME
ENDGAME,
TRAITOR_MESSAGE
}
enum ServerNetObject
{
@@ -78,6 +76,14 @@ namespace Barotrauma.Networking
ENTITY_EVENT_INITIAL,
}
enum TraitorMessageType
{
Server,
ServerMessageBox,
Objective,
Console
}
enum VoteType
{
Unknown,
@@ -94,6 +100,7 @@ namespace Barotrauma.Networking
Banned,
Kicked,
ServerShutdown,
ServerCrashed,
ServerFull,
AuthenticationRequired,
SteamAuthenticationRequired,
@@ -132,13 +139,7 @@ namespace Barotrauma.Networking
#if DEBUG
public Dictionary<string, long> messageCount = new Dictionary<string, long>();
#endif
public NetPeer NetPeer
{
get;
protected set;
}
protected string name;
protected ServerSettings serverSettings;
@@ -154,12 +155,6 @@ namespace Barotrauma.Networking
public bool ShowNetStats;
public int Port
{
get;
set;
}
public int TickRate
{
get { return serverSettings.TickRate; }
@@ -205,13 +200,7 @@ namespace Barotrauma.Networking
{
get { return serverSettings; }
}
public NetPeerConfiguration NetPeerConfiguration
{
get;
protected set;
}
public bool CanUseRadio(Character sender)
{
if (sender == null) return false;
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
namespace Barotrauma.Networking
{
@@ -0,0 +1,37 @@
using System;
namespace Barotrauma.Networking
{
public enum DeliveryMethod : byte
{
Unreliable = 0x0,
Reliable = 0x1,
ReliableOrdered = 0x2
}
public enum ConnectionInitialization : byte
{
//used by all peer implementations
SteamTicketAndVersion = 0x1,
Password = 0x2,
Success = 0x0,
//used only by SteamP2P implementations
ConnectionStarted = 0x3
}
[Flags]
public enum PacketHeader : byte
{
//used by all peer implementations
None = 0x0,
IsCompressed = 0x1,
IsConnectionInitializationStep = 0x2,
//used only by SteamP2P implementations
IsDisconnectMessage = 0x4,
IsServerMessage = 0x8,
IsHeartbeatMessage = 0x10
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Barotrauma.Networking
{
public interface IReadMessage
{
bool ReadBoolean();
void ReadPadBits();
byte ReadByte();
UInt16 ReadUInt16();
Int16 ReadInt16();
UInt32 ReadUInt32();
Int32 ReadInt32();
UInt64 ReadUInt64();
Int64 ReadInt64();
Single ReadSingle();
Double ReadDouble();
UInt32 ReadVariableUInt32();
String ReadString();
int ReadRangedInteger(int min, int max);
Single ReadRangedSingle(Single min, Single max, int bitCount);
byte[] ReadBytes(int numberOfBytes);
int BitPosition { get; set; }
int BytePosition { get; }
byte[] Buffer { get; }
int LengthBits { get; set; }
int LengthBytes { get; }
NetworkConnection Sender { get; }
}
}
@@ -0,0 +1,33 @@
using System;
namespace Barotrauma.Networking
{
public interface IWriteMessage
{
void Write(bool val);
void WritePadBits();
void Write(byte val);
void Write(Int16 val);
void Write(UInt16 val);
void Write(Int32 val);
void Write(UInt32 val);
void Write(Int64 val);
void Write(UInt64 val);
void Write(Single val);
void Write(Double val);
void WriteVariableUInt32(UInt32 val);
void Write(string val);
void WriteRangedIntegerDeprecated(int min, int max, int val); //TODO: remove this, val should be first parameter >:(
void WriteRangedInteger(int val, int min, int max);
void WriteRangedSingle(Single val, Single min, Single max, int bitCount);
void Write(byte[] val, int startIndex, int length);
void PrepareForSending(ref byte[] outBuf, out bool isCompressed, out int outLength);
int BitPosition { get; set; }
int BytePosition { get; }
byte[] Buffer { get; }
int LengthBits { get; set; }
int LengthBytes { get; }
}
}
@@ -0,0 +1,955 @@
using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Text;
namespace Barotrauma.Networking
{
public static class MsgConstants
{
public const int MTU = 1200;
public const int CompressionThreshold = 1000;
public const int InitialBufferSize = 256;
public const int BufferOverAllocateAmount = 4;
}
/// <summary>
/// Utility struct for writing Singles
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct SingleUIntUnion
{
/// <summary>
/// Value as a 32 bit float
/// </summary>
[FieldOffset(0)]
public float SingleValue;
/// <summary>
/// Value as an unsigned 32 bit integer
/// </summary>
[FieldOffset(0)]
public uint UIntValue;
}
internal static class MsgWriter
{
internal static void Write(ref byte[] buf, ref int bitPos, bool val)
{
#if DEBUG
int resetPos = bitPos;
#endif
EnsureBufferSize(ref buf, bitPos + 1);
int bytePos = bitPos / 8;
int bitOffset = bitPos % 8;
byte bitFlag = (byte)(1 << bitOffset);
byte bitMask = (byte)((~bitFlag) & 0xff);
buf[bytePos] &= bitMask;
if (val) buf[bytePos] |= bitFlag;
bitPos++;
#if DEBUG
bool testVal = MsgReader.ReadBoolean(buf, ref resetPos);
if (testVal != val || resetPos != bitPos)
{
DebugConsole.ThrowError("Boolean written incorrectly! " + testVal + ", " + val + "; " + resetPos + ", " + bitPos);
}
#endif
}
internal static void WritePadBits(ref byte[] buf, ref int bitPos)
{
int bitOffset = bitPos % 8;
bitPos += ((8 - bitOffset) % 8);
EnsureBufferSize(ref buf, bitPos);
}
internal static void Write(ref byte[] buf, ref int bitPos, byte val)
{
EnsureBufferSize(ref buf, bitPos + 8);
NetBitWriter.WriteByte(val, 8, buf, bitPos);
bitPos += 8;
}
internal static void Write(ref byte[] buf, ref int bitPos, UInt16 val)
{
EnsureBufferSize(ref buf, bitPos + 16);
NetBitWriter.WriteUInt16(val, 16, buf, bitPos);
bitPos += 16;
}
internal static void Write(ref byte[] buf, ref int bitPos, Int16 val)
{
EnsureBufferSize(ref buf, bitPos + 16);
NetBitWriter.WriteUInt16((UInt16)val, 16, buf, bitPos);
bitPos += 16;
}
internal static void Write(ref byte[] buf, ref int bitPos, UInt32 val)
{
EnsureBufferSize(ref buf, bitPos + 32);
NetBitWriter.WriteUInt32(val, 32, buf, bitPos);
bitPos += 32;
}
internal static void Write(ref byte[] buf, ref int bitPos, Int32 val)
{
EnsureBufferSize(ref buf, bitPos + 32);
NetBitWriter.WriteUInt32((UInt32)val, 32, buf, bitPos);
bitPos += 32;
}
internal static void Write(ref byte[] buf, ref int bitPos, UInt64 val)
{
EnsureBufferSize(ref buf, bitPos + 64);
NetBitWriter.WriteUInt64(val, 64, buf, bitPos);
bitPos += 64;
}
internal static void Write(ref byte[] buf, ref int bitPos, Int64 val)
{
EnsureBufferSize(ref buf, bitPos + 64);
NetBitWriter.WriteUInt64((UInt64)val, 64, buf, bitPos);
bitPos += 64;
}
internal static void Write(ref byte[] buf, ref int bitPos, Single val)
{
// Use union to avoid BitConverter.GetBytes() which allocates memory on the heap
SingleUIntUnion su;
su.UIntValue = 0; // must initialize every member of the union to avoid warning
su.SingleValue = val;
EnsureBufferSize(ref buf, bitPos + 32);
NetBitWriter.WriteUInt32(su.UIntValue, 32, buf, bitPos);
bitPos += 32;
}
internal static void Write(ref byte[] buf, ref int bitPos, Double val)
{
EnsureBufferSize(ref buf, bitPos + 64);
byte[] bytes = BitConverter.GetBytes(val);
WriteBytes(ref buf, ref bitPos, bytes, 0, bytes.Length);
bitPos += 64;
}
internal static void Write(ref byte[] buf, ref int bitPos, string val)
{
if (string.IsNullOrEmpty(val))
{
WriteVariableUInt32(ref buf, ref bitPos, (uint)0);
return;
}
byte[] bytes = Encoding.UTF8.GetBytes(val);
WriteVariableUInt32(ref buf, ref bitPos, (uint)bytes.Length);
WriteBytes(ref buf, ref bitPos, bytes, 0, bytes.Length);
}
internal static int WriteVariableUInt32(ref byte[] buf, ref int bitPos, uint value)
{
int retval = 1;
uint num1 = (uint)value;
while (num1 >= 0x80)
{
Write(ref buf, ref bitPos, (byte)(num1 | 0x80));
num1 = num1 >> 7;
retval++;
}
Write(ref buf, ref bitPos, (byte)num1);
return retval;
}
internal static void WriteRangedInteger(ref byte[] buf, ref int bitPos, int val, int min, int max)
{
uint range = (uint)(max - min);
int numberOfBits = NetUtility.BitsToHoldUInt(range);
EnsureBufferSize(ref buf, bitPos + numberOfBits);
uint rvalue = (uint)(val - min);
NetBitWriter.WriteUInt32(rvalue, numberOfBits, buf, bitPos);
bitPos += numberOfBits;
}
internal static void WriteRangedSingle(ref byte[] buf, ref int bitPos, Single val, Single min, Single max, int numberOfBits)
{
float range = max - min;
float unit = ((val - min) / range);
int maxVal = (1 << numberOfBits) - 1;
EnsureBufferSize(ref buf, bitPos + numberOfBits);
NetBitWriter.WriteUInt32((UInt32)((float)maxVal * unit), numberOfBits, buf, bitPos);
bitPos += numberOfBits;
}
internal static void WriteBytes(ref byte[] buf, ref int bitPos, byte[] val, int pos, int length)
{
EnsureBufferSize(ref buf, bitPos + length * 8);
NetBitWriter.WriteBytes(val, pos, length, buf, bitPos);
bitPos += length * 8;
}
internal static void EnsureBufferSize(ref byte[] buf, int numberOfBits)
{
int byteLen = ((numberOfBits + 7) >> 3);
if (buf == null)
{
buf = new byte[byteLen + MsgConstants.BufferOverAllocateAmount];
return;
}
if (buf.Length < byteLen)
{
Array.Resize<byte>(ref buf, byteLen + MsgConstants.BufferOverAllocateAmount);
}
}
}
internal static class MsgReader
{
internal static bool ReadBoolean(byte[] buf, ref int bitPos)
{
byte retval = NetBitWriter.ReadByte(buf, 1, bitPos);
bitPos++;
return (retval > 0 ? true : false);
}
internal static void ReadPadBits(byte[] buf, ref int bitPos)
{
int bitOffset = bitPos % 8;
bitPos += (8 - bitOffset) % 8;
}
internal static byte ReadByte(byte[] buf, ref int bitPos)
{
byte retval = NetBitWriter.ReadByte(buf, 8, bitPos);
bitPos += 8;
return retval;
}
internal static UInt16 ReadUInt16(byte[] buf, ref int bitPos)
{
uint retval = NetBitWriter.ReadUInt16(buf, 16, bitPos);
bitPos += 16;
return (ushort)retval;
}
internal static Int16 ReadInt16(byte[] buf, ref int bitPos)
{
return (Int16)ReadUInt16(buf, ref bitPos);
}
internal static UInt32 ReadUInt32(byte[] buf, ref int bitPos)
{
uint retval = NetBitWriter.ReadUInt32(buf, 32, bitPos);
bitPos += 32;
return retval;
}
internal static Int32 ReadInt32(byte[] buf, ref int bitPos)
{
return (Int32)ReadUInt32(buf, ref bitPos);
}
internal static UInt64 ReadUInt64(byte[] buf, ref int bitPos)
{
ulong low = NetBitWriter.ReadUInt32(buf, 32, bitPos);
bitPos += 32;
ulong high = NetBitWriter.ReadUInt32(buf, 32, bitPos);
ulong retval = low + (high << 32);
bitPos += 32;
return retval;
}
internal static Int64 ReadInt64(byte[] buf, ref int bitPos)
{
return (Int64)ReadUInt64(buf, ref bitPos);
}
internal static Single ReadSingle(byte[] buf, ref int bitPos)
{
if ((bitPos & 7) == 0) // read directly
{
float retval = BitConverter.ToSingle(buf, bitPos >> 3);
bitPos += 32;
return retval;
}
byte[] bytes = ReadBytes(buf, ref bitPos, 4);
return BitConverter.ToSingle(bytes, 0);
}
internal static Double ReadDouble(byte[] buf, ref int bitPos)
{
if ((bitPos & 7) == 0) // read directly
{
// read directly
double retval = BitConverter.ToDouble(buf, bitPos >> 3);
bitPos += 64;
return retval;
}
byte[] bytes = ReadBytes(buf, ref bitPos, 8);
return BitConverter.ToDouble(bytes, 0);
}
internal static UInt32 ReadVariableUInt32(byte[] buf, ref int bitPos)
{
int bitLength = buf.Length * 8;
int num1 = 0;
int num2 = 0;
while (bitLength - bitPos >= 8)
{
byte num3 = ReadByte(buf, ref bitPos);
num1 |= (num3 & 0x7f) << num2;
num2 += 7;
if ((num3 & 0x80) == 0)
return (uint)num1;
}
// ouch; failed to find enough bytes; malformed variable length number?
return (uint)num1;
}
internal static String ReadString(byte[] buf, ref int bitPos)
{
int bitLength = buf.Length * 8;
int byteLen = (int)ReadVariableUInt32(buf, ref bitPos);
if (byteLen <= 0) { return String.Empty; }
if ((ulong)(bitLength - bitPos) < ((ulong)byteLen * 8))
{
// not enough data
return null;
}
if ((bitPos & 7) == 0)
{
// read directly
string retval = System.Text.Encoding.UTF8.GetString(buf, bitPos >> 3, byteLen);
bitPos += (8 * byteLen);
return retval;
}
byte[] bytes = ReadBytes(buf, ref bitPos, byteLen);
return System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
internal static int ReadRangedInteger(byte[] buf, ref int bitPos, int min, int max)
{
uint range = (uint)(max - min);
int numBits = NetUtility.BitsToHoldUInt(range);
uint rvalue = NetBitWriter.ReadUInt32(buf, numBits, bitPos);
bitPos += numBits;
return (int)(min + rvalue);
}
internal static Single ReadRangedSingle(byte[] buf, ref int bitPos, Single min, Single max, int bitCount)
{
int maxInt = (1 << bitCount) - 1;
int intVal = ReadRangedInteger(buf, ref bitPos, 0, maxInt);
Single range = max - min;
return min + (range * ((Single)intVal) / ((Single)maxInt));
}
internal static byte[] ReadBytes(byte[] buf, ref int bitPos, int numberOfBytes)
{
byte[] retval = new byte[numberOfBytes];
NetBitWriter.ReadBytes(buf, numberOfBytes, bitPos, retval, 0);
bitPos += (8 * numberOfBytes);
return retval;
}
}
public class WriteOnlyMessage : IWriteMessage
{
private byte[] buf = new byte[MsgConstants.InitialBufferSize];
private int seekPos = 0;
private int lengthBits = 0;
public int BitPosition
{
get
{
return seekPos;
}
set
{
seekPos = value;
}
}
public int BytePosition
{
get
{
return seekPos / 8;
}
}
public byte[] Buffer
{
get
{
return buf;
}
}
public int LengthBits
{
get
{
lengthBits = seekPos > lengthBits ? seekPos : lengthBits;
return lengthBits;
}
set
{
lengthBits = value;
seekPos = seekPos > lengthBits ? lengthBits : seekPos;
}
}
public int LengthBytes
{
get
{
return (LengthBits + ((8 - (LengthBits % 8)) % 8)) / 8;
}
}
public void Write(bool val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WritePadBits()
{
MsgWriter.WritePadBits(ref buf, ref seekPos);
}
public void Write(byte val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt16 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int16 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt32 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int32 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt64 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int64 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Single val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Double val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WriteVariableUInt32(UInt32 val)
{
MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, val);
}
public void Write(String val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WriteRangedIntegerDeprecated(int min, int max, int val)
{
MsgWriter.WriteRangedInteger(ref buf, ref seekPos, val, min, max);
}
public void WriteRangedInteger(int val, int min, int max)
{
MsgWriter.WriteRangedInteger(ref buf, ref seekPos, val, min, max);
}
public void WriteRangedSingle(Single val, Single min, Single max, int bitCount)
{
MsgWriter.WriteRangedSingle(ref buf, ref seekPos, val, min, max, bitCount);
}
public void Write(byte[] val, int startPos, int length)
{
MsgWriter.WriteBytes(ref buf, ref seekPos, val, startPos, length);
}
public void PrepareForSending(ref byte[] outBuf, out bool isCompressed, out int length)
{
if (LengthBytes <= MsgConstants.CompressionThreshold)
{
isCompressed = false;
if (LengthBytes > outBuf.Length) { Array.Resize(ref outBuf, LengthBytes); }
Array.Copy(buf, outBuf, LengthBytes);
length = LengthBytes;
}
else
{
using (MemoryStream output = new MemoryStream())
{
using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Fastest))
{
dstream.Write(buf, 0, LengthBytes);
}
byte[] compressedBuf = output.ToArray();
//don't send the data as compressed if the data takes up more space after compression
//(which may happen when sending a sub/save file that's already been compressed with a better compression ratio)
if (compressedBuf.Length >= outBuf.Length)
{
isCompressed = false;
if (LengthBytes > outBuf.Length) { Array.Resize(ref outBuf, LengthBytes); }
Array.Copy(buf, outBuf, LengthBytes);
length = LengthBytes;
}
else
{
isCompressed = true;
if (compressedBuf.Length > outBuf.Length) { Array.Resize(ref outBuf, compressedBuf.Length); }
Array.Copy(compressedBuf, outBuf, compressedBuf.Length);
length = compressedBuf.Length;
DebugConsole.NewMessage("Compressed message: " + LengthBytes + " to " + length);
}
}
}
}
}
public class ReadOnlyMessage : IReadMessage
{
private byte[] buf;
private int seekPos = 0;
private int lengthBits = 0;
public int BitPosition
{
get
{
return seekPos;
}
set
{
seekPos = value;
}
}
public int BytePosition
{
get
{
return seekPos / 8;
}
}
public byte[] Buffer
{
get
{
return buf;
}
}
public int LengthBits
{
get
{
lengthBits = seekPos > lengthBits ? seekPos : lengthBits;
return lengthBits;
}
set
{
lengthBits = value;
seekPos = seekPos > lengthBits ? lengthBits : seekPos;
}
}
public int LengthBytes
{
get
{
return lengthBits / 8;
}
}
public NetworkConnection Sender { get; private set; }
public ReadOnlyMessage(byte[] inBuf, bool isCompressed, int startPos, int inLength, NetworkConnection sender)
{
Sender = sender;
if (isCompressed)
{
byte[] decompressedData;
using (MemoryStream input = new MemoryStream(inBuf, startPos, inLength))
{
using (MemoryStream output = new MemoryStream())
{
using (DeflateStream dstream = new DeflateStream(input, CompressionMode.Decompress))
{
dstream.CopyTo(output);
}
decompressedData = output.ToArray();
}
}
buf = new byte[decompressedData.Length];
Array.Copy(decompressedData, 0, buf, 0, decompressedData.Length);
lengthBits = decompressedData.Length * 8;
DebugConsole.NewMessage("Decompressing message: " + inLength + " to " + LengthBytes);
}
else
{
buf = new byte[inBuf.Length];
Array.Copy(inBuf, startPos, buf, 0, inLength);
lengthBits = inLength * 8;
}
seekPos = 0;
}
public bool ReadBoolean()
{
return MsgReader.ReadBoolean(buf, ref seekPos);
}
public void ReadPadBits()
{
MsgReader.ReadPadBits(buf, ref seekPos);
}
public byte ReadByte()
{
return MsgReader.ReadByte(buf, ref seekPos);
}
public UInt16 ReadUInt16()
{
return MsgReader.ReadUInt16(buf, ref seekPos);
}
public Int16 ReadInt16()
{
return MsgReader.ReadInt16(buf, ref seekPos);
}
public UInt32 ReadUInt32()
{
return MsgReader.ReadUInt32(buf, ref seekPos);
}
public Int32 ReadInt32()
{
return MsgReader.ReadInt32(buf, ref seekPos);
}
public UInt64 ReadUInt64()
{
return MsgReader.ReadUInt64(buf, ref seekPos);
}
public Int64 ReadInt64()
{
return MsgReader.ReadInt64(buf, ref seekPos);
}
public Single ReadSingle()
{
return MsgReader.ReadSingle(buf, ref seekPos);
}
public Double ReadDouble()
{
return MsgReader.ReadDouble(buf, ref seekPos);
}
public UInt32 ReadVariableUInt32()
{
return MsgReader.ReadVariableUInt32(buf, ref seekPos);
}
public String ReadString()
{
return MsgReader.ReadString(buf, ref seekPos);
}
public int ReadRangedInteger(int min, int max)
{
return MsgReader.ReadRangedInteger(buf, ref seekPos, min, max);
}
public Single ReadRangedSingle(Single min, Single max, int bitCount)
{
return MsgReader.ReadRangedSingle(buf, ref seekPos, min, max, bitCount);
}
public byte[] ReadBytes(int numberOfBytes)
{
return MsgReader.ReadBytes(buf, ref seekPos, numberOfBytes);
}
}
public class ReadWriteMessage : IWriteMessage, IReadMessage
{
private byte[] buf = new byte[MsgConstants.InitialBufferSize];
private int seekPos = 0;
private int lengthBits = 0;
public int BitPosition
{
get
{
return seekPos;
}
set
{
seekPos = value;
}
}
public int BytePosition
{
get
{
return seekPos / 8;
}
}
public byte[] Buffer
{
get
{
return buf;
}
}
public int LengthBits
{
get
{
lengthBits = seekPos > lengthBits ? seekPos : lengthBits;
return lengthBits;
}
set
{
lengthBits = value;
seekPos = seekPos > lengthBits ? lengthBits : seekPos;
}
}
public int LengthBytes
{
get
{
return (LengthBits + ((8 - (LengthBits % 8)) % 8)) / 8;
}
}
public NetworkConnection Sender { get { return null; } }
public void Write(bool val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WritePadBits()
{
MsgWriter.WritePadBits(ref buf, ref seekPos);
}
public void Write(byte val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt16 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int16 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt32 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int32 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(UInt64 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Int64 val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Single val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void Write(Double val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WriteVariableUInt32(UInt32 val)
{
MsgWriter.WriteVariableUInt32(ref buf, ref seekPos, val);
}
public void Write(String val)
{
MsgWriter.Write(ref buf, ref seekPos, val);
}
public void WriteRangedIntegerDeprecated(int min, int max, int val)
{
MsgWriter.WriteRangedInteger(ref buf, ref seekPos, val, min, max);
}
public void WriteRangedInteger(int val, int min, int max)
{
MsgWriter.WriteRangedInteger(ref buf, ref seekPos, val, min, max);
}
public void WriteRangedSingle(Single val, Single min, Single max, int bitCount)
{
MsgWriter.WriteRangedSingle(ref buf, ref seekPos, val, min, max, bitCount);
}
public void Write(byte[] val, int startPos, int length)
{
MsgWriter.WriteBytes(ref buf, ref seekPos, val, startPos, length);
}
public bool ReadBoolean()
{
return MsgReader.ReadBoolean(buf, ref seekPos);
}
public void ReadPadBits()
{
MsgReader.ReadPadBits(buf, ref seekPos);
}
public byte ReadByte()
{
return MsgReader.ReadByte(buf, ref seekPos);
}
public UInt16 ReadUInt16()
{
return MsgReader.ReadUInt16(buf, ref seekPos);
}
public Int16 ReadInt16()
{
return MsgReader.ReadInt16(buf, ref seekPos);
}
public UInt32 ReadUInt32()
{
return MsgReader.ReadUInt32(buf, ref seekPos);
}
public Int32 ReadInt32()
{
return MsgReader.ReadInt32(buf, ref seekPos);
}
public UInt64 ReadUInt64()
{
return MsgReader.ReadUInt64(buf, ref seekPos);
}
public Int64 ReadInt64()
{
return MsgReader.ReadInt64(buf, ref seekPos);
}
public Single ReadSingle()
{
return MsgReader.ReadSingle(buf, ref seekPos);
}
public Double ReadDouble()
{
return MsgReader.ReadDouble(buf, ref seekPos);
}
public UInt32 ReadVariableUInt32()
{
return MsgReader.ReadVariableUInt32(buf, ref seekPos);
}
public String ReadString()
{
return MsgReader.ReadString(buf, ref seekPos);
}
public int ReadRangedInteger(int min, int max)
{
return MsgReader.ReadRangedInteger(buf, ref seekPos, min, max);
}
public Single ReadRangedSingle(Single min, Single max, int bitCount)
{
return MsgReader.ReadRangedSingle(buf, ref seekPos, min, max, bitCount);
}
public byte[] ReadBytes(int numberOfBytes)
{
return MsgReader.ReadBytes(buf, ref seekPos, numberOfBytes);
}
public void PrepareForSending(ref byte[] outBuf, out bool isCompressed, out int outLength)
{
throw new InvalidOperationException("ReadWriteMessages are not to be sent");
}
}
}
@@ -0,0 +1,37 @@
using System;
using System.Net;
using Lidgren.Network;
namespace Barotrauma.Networking
{
public class LidgrenConnection : NetworkConnection
{
public NetConnection NetConnection { get; private set; }
public IPEndPoint IPEndPoint => NetConnection.RemoteEndPoint;
public string IPString
{
get
{
return IPEndPoint.Address.IsIPv4MappedToIPv6 ? IPEndPoint.Address.MapToIPv4().ToString() : IPEndPoint.Address.ToString();
}
}
public UInt16 Port
{
get
{
return (UInt16)IPEndPoint.Port;
}
}
public LidgrenConnection(string name, NetConnection netConnection, UInt64 steamId)
{
Name = name;
NetConnection = netConnection;
SteamID = steamId;
EndPointString = IPString;
}
}
}

Some files were not shown because too many files have changed in this diff Show More