├── .gitattributes ├── .gitignore ├── Buffs ├── SuperToxic.cs └── SuperToxic.png ├── Effects ├── EDge.fx ├── EDge.xnb ├── GBlur.fx ├── GBlur.xnb ├── GrayScale.fx ├── GrayScale.xnb ├── ShockWave.fx ├── ShockWave.xnb ├── Test.xnb ├── font.fx ├── font.xnb ├── fuzzy.fx ├── fuzzy.xnb └── test.fx ├── Images ├── AdvInvBack1.png ├── AdvInvBack2.png ├── AdvInvBack3.png ├── AdvInvBackRej.png ├── Arrow.png ├── Arrow1.png ├── Bar.png ├── BarFrameRank.png ├── Box.png ├── Box2.png ├── CloseButton.png ├── Cog.png ├── CollapseButtonDown.png ├── CollapseButtonUp.png ├── Trans.png ├── icon.png └── noise1.png ├── Items ├── Accessories │ ├── ExampleWings.cs │ ├── ExampleWings.png │ ├── ExampleWings_Wings.png │ ├── PurpleStone.cs │ └── PurpleStone.png ├── Armors │ ├── ExampleBreastplate.cs │ ├── ExampleBreastplate.png │ ├── ExampleBreastplate_Arms.png │ ├── ExampleBreastplate_Body.png │ ├── ExampleBreastplate_FemaleBody.png │ ├── ExampleHelmet.cs │ ├── ExampleHelmet.png │ ├── ExampleHelmet_Head.png │ ├── ExampleLeggings.cs │ ├── ExampleLeggings.png │ └── ExampleLeggings_Legs.png ├── PoisonousPotion.cs ├── PoisonousPotion.png ├── SkirtSword.cs ├── SkirtSword.png ├── SkirtSword2.cs ├── SkirtSword2.png ├── TemplateGun.cs └── TemplateGun.png ├── LICENSE ├── NPCs ├── StateMachine │ ├── NPCState.cs │ └── SMNPC.cs ├── SuperMage.cs ├── SuperMage.png ├── WormBody.cs ├── WormBody.png ├── WormFakeHead.cs ├── WormFakeHead.png ├── WormHead.cs ├── WormHead.png ├── WormTail.cs └── WormTail.png ├── Projectiles ├── AvoidProj.cs ├── AvoidProj.png ├── GravProj.cs ├── GravProj.png ├── LightTreePro.cs ├── LightTreePro.png ├── ProjProj.cs ├── ProjProj.png ├── ProjUtils.cs ├── Raze │ ├── MagicLine.cs │ └── MagicLine.png ├── StateMachine │ ├── ProjState.cs │ └── SMProjectile.cs ├── SuperGCD.cs ├── SuperGCD.png ├── YinYangTower.cs └── YinYangTower.png ├── Properties └── launchSettings.json ├── Sky ├── TestScreenShader.cs └── TestSky.cs ├── TemplateGlobaProjectile.cs ├── TemplateGlobalItem.cs ├── TemplateGlobalNPC.cs ├── TemplateMod2.cs ├── TemplateMod2.csproj ├── TemplateMod2.sln ├── TemplatePlayer.cs ├── TemplateWorld.cs ├── Utils ├── Drawing.cs ├── LightTree.cs ├── MazeTree.cs ├── MyDustID.cs └── PriorityQueue.cs ├── build.txt ├── description.txt └── icon.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb 341 | 342 | Properties/ 343 | *.zip 344 | Effects/*.dll 345 | Effects/*.exe 346 | Effects/*.txt 347 | Effects/Content/* -------------------------------------------------------------------------------- /Buffs/SuperToxic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using TemplateMod2.Utils; 7 | using Terraria; 8 | using Terraria.ModLoader; 9 | 10 | namespace TemplateMod2.Buffs { 11 | public class SuperToxic : ModBuff { 12 | public override void SetDefaults() { 13 | // 设置buff名字和描述 14 | DisplayName.SetDefault("剧毒"); 15 | Description.SetDefault("你中毒了,祝你好运"); 16 | 17 | // 因为buff严格意义上不是一个TR里面自定义的数据类型,所以没有像buff.XXXX这样的设置属性方式了 18 | // 我们需要用另外一种方式设置属性 19 | 20 | // 这个属性决定buff在游戏退出再进来后会不会仍然持续,true就是不会,false就是会 21 | Main.buffNoSave[Type] = false; 22 | 23 | // 用来判定这个buff算不算一个debuff,如果设置为true会得到TR里对于debuff的限制,比如无法取消 24 | Main.debuff[Type] = true; 25 | 26 | // 当然你也可以用这个属性让这个buff即使不是debuff也不能取消,设为false就是不能取消了 27 | this.canBeCleared = false; 28 | 29 | // 决定这个buff是不是照明宠物的buff,以后讲宠物和召唤物的时候会用到的,现在先设为false 30 | Main.lightPet[Type] = false; 31 | 32 | // 决定这个buff会不会显示持续时间,false就是会显示,true就是不会显示,一般宠物buff都不会显示 33 | Main.buffNoTimeDisplay[Type] = false; 34 | 35 | // 决定这个buff在专家模式会不会持续时间加长,false是不会,true是会 36 | this.longerExpertDebuff = false; 37 | 38 | // 如果这个属性为true,pvp的时候就可以给对手加上这个debuff/buff 39 | Main.pvpBuff[Type] = true; 40 | 41 | // 决定这个buff是不是一个装饰性宠物,用来判定的,比如消除buff的时候不会消除它 42 | Main.vanityPet[Type] = false; 43 | } 44 | // 注意这里我们选择的是对Player生效的Update,另一个是对NPC生效的Update 45 | public override void Update(Player player, ref int buffIndex) { 46 | // 把玩家的所有生命回复清除 47 | if (player.lifeRegen > 0) { 48 | player.lifeRegen = 0; 49 | } 50 | player.lifeRegenTime = 0; 51 | 52 | player.buffTime[buffIndex] = 2; 53 | // 让玩家的减血速率随着时间而减少 54 | // player.buffTime[buffIndex]就是这个buff的剩余时间 55 | player.lifeRegen -= player.buffTime[buffIndex]; 56 | } 57 | public override void Update(NPC npc, ref int buffIndex) { 58 | if (npc.lifeRegen > 0) { 59 | npc.lifeRegen = 0; 60 | } 61 | npc.lifeRegen -= 50; 62 | } 63 | public override bool ReApply(Player player, int time, int buffIndex) { 64 | player.buffTime[buffIndex] += time; 65 | return true; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Buffs/SuperToxic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Buffs/SuperToxic.png -------------------------------------------------------------------------------- /Effects/EDge.fx: -------------------------------------------------------------------------------- 1 | sampler uImage0 : register(s0); 2 | float uTime; 3 | float2 uImageSize; 4 | 5 | 6 | float4 edge(float2 coords : TEXCOORD0) : COLOR0 { 7 | float4 color = tex2D(uImage0, coords); 8 | if (any(color)) 9 | return color; 10 | float dx = 1 / uImageSize.x; 11 | float dy = 1 / uImageSize.y; 12 | bool flag = false; 13 | for(int i = -1; i <= 1; i++){ 14 | for(int j = -1; j <= 1; j++){ 15 | float4 c = tex2D(uImage0, coords + float2(dx * i, dy * j)); 16 | if(any(c)){ 17 | flag = true; 18 | } 19 | } 20 | } 21 | if(flag) return float4(1, 1, 1, 1); 22 | return color; 23 | } 24 | 25 | 26 | technique Technique1 { 27 | pass Edge { 28 | PixelShader = compile ps_2_0 edge(); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Effects/EDge.xnb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Effects/EDge.xnb -------------------------------------------------------------------------------- /Effects/GBlur.fx: -------------------------------------------------------------------------------- 1 | sampler uImage0 : register(s0); 2 | sampler uImage1 : register(s1); 3 | float3 uColor; 4 | float uOpacity; 5 | float3 uSecondaryColor; 6 | float uTime; 7 | float2 uScreenResolution; 8 | float2 uScreenPosition; 9 | float2 uTargetPosition; 10 | float2 uImageOffset; 11 | float uIntensity; 12 | float uProgress; 13 | float2 uDirection; 14 | float2 uZoom; 15 | float2 uEffectPos; 16 | float2 uImageSize0; 17 | float2 uImageSize1; 18 | 19 | float gauss[3][3] = { 20 | 0.075, 0.124, 0.075, 21 | 0.124, 0.204, 0.124, 22 | 0.075, 0.124, 0.075 23 | }; 24 | 25 | 26 | float4 PixelShaderFunction(float2 coords : TEXCOORD0) : COLOR0 { 27 | float4 color = tex2D(uImage0, coords); 28 | if (!any(color)) 29 | return color; 30 | float dx = 2 / uScreenResolution.x; 31 | float dy = 2 / uScreenResolution.y; 32 | color = float4(0, 0, 0, 0); 33 | for(int i = -1; i <= 1; i++) { 34 | for(int j = -1; j <= 1; j++) { 35 | color += gauss[i + 1][j + 1] * tex2D(uImage0, float2(coords.x + dx * i, coords.y + dy * j)); 36 | } 37 | } 38 | return color; 39 | 40 | } 41 | 42 | technique Technique1 { 43 | pass Test { 44 | PixelShader = compile ps_2_0 PixelShaderFunction(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Effects/GBlur.xnb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Effects/GBlur.xnb -------------------------------------------------------------------------------- /Effects/GrayScale.fx: -------------------------------------------------------------------------------- 1 | sampler uImage0 : register(s0); 2 | float uTime; 3 | float2 uImageSize; 4 | 5 | float ffmod(float v, float m){ 6 | int f = floor(v / m); 7 | return v - f * m; 8 | } 9 | 10 | float3 HUEtoRGB(float H) 11 | { 12 | float R = abs(H * 6 - 3) - 1; 13 | float G = 2 - abs(H * 6 - 2); 14 | float B = 2 - abs(H * 6 - 4); 15 | return saturate(float3(R,G,B)); 16 | } 17 | 18 | float4 rainbow(float2 coords : TEXCOORD0) : COLOR0 { 19 | float4 color = tex2D(uImage0, coords); 20 | if (!any(color)) 21 | return color; 22 | return float4(HUEtoRGB(ffmod(uTime * 0.01 + coords.x, 1)), color.a); 23 | } 24 | 25 | 26 | 27 | float4 PixelShaderFunction(float2 coords : TEXCOORD0) : COLOR0 { 28 | float4 color = tex2D(uImage0, coords); 29 | if (!any(color)) 30 | return color; 31 | float gs = dot(float3(0.3, 0.59, 0.11), color.rgb); 32 | return float4(gs, gs, gs, color.a); 33 | } 34 | 35 | 36 | 37 | 38 | technique Technique1 { 39 | pass Test { 40 | PixelShader = compile ps_2_0 PixelShaderFunction(); 41 | } 42 | pass Rainbow { 43 | PixelShader = compile ps_2_0 rainbow(); 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /Effects/GrayScale.xnb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Effects/GrayScale.xnb -------------------------------------------------------------------------------- /Effects/ShockWave.fx: -------------------------------------------------------------------------------- 1 | // Screen Shader 模板 2 | // ---------------------------- 3 | // 以下参数*必须*添加 4 | // 因为这些参数都有原版的默认传入值 5 | // 如果缺失将导致Null Reference Exception 6 | // 0号采样贴图 7 | sampler uImage0 : register(s0); 8 | // 1号采样贴图 9 | sampler uImage1 : register(s1); 10 | // 颜色参数(默认传入白色,自己设定 11 | float3 uColor; 12 | // 透明度参数(自己设定 13 | float uOpacity; 14 | // 二级颜色参数 15 | float3 uSecondaryColor; 16 | float uTime; 17 | float2 uScreenResolution; 18 | float2 uScreenPosition; 19 | float2 uTargetPosition; 20 | float2 uImageOffset; 21 | float uIntensity; 22 | float uProgress; 23 | float2 uDirection; 24 | float2 uZoom; 25 | float2 uEffectPos; 26 | float2 uImageSize0; 27 | float2 uImageSize1; 28 | 29 | 30 | float4 PixelShaderFunction(float2 coords : TEXCOORD0) : COLOR0 { 31 | float4 color = tex2D(uImage0, coords); 32 | if (!any(color)) 33 | return color; 34 | // pos 就是中心了 35 | float2 pos = float2(0.5, 0.5); 36 | // offset 是中心到当前点的向量 37 | float2 offset = (coords - pos); 38 | // 因为长宽比不同进行修正 39 | float2 rpos = offset * float2(uScreenResolution.x / uScreenResolution.y, 1); 40 | float dis = length(rpos); 41 | float r = dis; 42 | // 将向量旋转相当于距离的弧度 43 | float2 target = mul(offset, float2x2(cos(r), -sin(r), sin(r), cos(r))); 44 | return tex2D(uImage0, pos + target); 45 | } 46 | 47 | technique Technique1 { 48 | pass Test { 49 | PixelShader = compile ps_2_0 PixelShaderFunction(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Effects/ShockWave.xnb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Effects/ShockWave.xnb -------------------------------------------------------------------------------- /Effects/Test.xnb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Effects/Test.xnb -------------------------------------------------------------------------------- /Effects/font.fx: -------------------------------------------------------------------------------- 1 | // Screen Shader 模板 2 | // ---------------------------- 3 | // 以下参数*必须*添加 4 | // 因为这些参数都有原版的默认传入值 5 | // 如果缺失将导致Null Reference Exception 6 | // 0号采样贴图 7 | sampler uImage0 : register(s0); 8 | // 1号采样贴图 9 | sampler uImage1 : register(s1); 10 | // 颜色参数(默认传入白色,自己设定 11 | float3 uColor; 12 | // 透明度参数(自己设定 13 | float uOpacity; 14 | // 二级颜色参数 15 | float3 uSecondaryColor; 16 | float uTime; 17 | float2 uScreenResolution; 18 | float2 uScreenPosition; 19 | float2 uTargetPosition; 20 | float2 uImageOffset; 21 | float uIntensity; 22 | float uProgress; 23 | float2 uDirection; 24 | float2 uZoom; 25 | float2 uImageSize0; 26 | float2 uImageSize1; 27 | 28 | float gauss[3][3] = { 29 | 0.075, 0.124, 0.075, 30 | 0.124, 0.204, 0.124, 31 | 0.075, 0.124, 0.075 32 | }; 33 | struct PS_INPUT 34 | { 35 | float4 Position : SV_POSITION; // interpolated vertex position (system value) 36 | float4 Color : COLOR0; // interpolated diffuse color 37 | }; 38 | 39 | float4 PixelShaderFunction(float2 coords : TEXCOORD0) : COLOR0 { 40 | float4 color = tex2D(uImage0, coords); 41 | if (!any(color)) 42 | return color; 43 | float2 offset = coords - float2(0.5, 0.5); 44 | float dis = offset * offset; 45 | return tex2D(uImage0, float2(0.5, 0.5) + offset * dis); 46 | } 47 | 48 | float4 colored(float2 coords : TEXCOORD0) : COLOR0 { 49 | float4 color = tex2D(uImage0, coords); 50 | if (!any(color)) 51 | return color; 52 | return float4(sin(coords.y * 5 + uTime), cos(coords.y * 5 + uTime), coords.y, color.a); 53 | } 54 | 55 | 56 | 57 | technique Technique1 { 58 | pass Test { 59 | PixelShader = compile ps_2_0 PixelShaderFunction(); 60 | } 61 | pass Color { 62 | PixelShader = compile ps_2_0 colored(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Effects/font.xnb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Effects/font.xnb -------------------------------------------------------------------------------- /Effects/fuzzy.fx: -------------------------------------------------------------------------------- 1 | // Screen Shader 模板 2 | // ---------------------------- 3 | // 以下参数*必须*添加 4 | // 因为这些参数都有原版的默认传入值 5 | // 如果缺失将导致Null Reference Exception 6 | // 0号采样贴图 7 | sampler uImage0 : register(s0); 8 | // 1号采样贴图 9 | sampler uImage1 : register(s1); 10 | // 颜色参数(默认传入白色,自己设定 11 | float3 uColor; 12 | // 透明度参数(自己设定 13 | float uOpacity; 14 | // 二级颜色参数 15 | float3 uSecondaryColor; 16 | float uTime; 17 | float2 uScreenResolution; 18 | float2 uScreenPosition; 19 | float2 uTargetPosition; 20 | float2 uImageOffset; 21 | float2 uEffectPos; 22 | float uIntensity; 23 | float uProgress; 24 | float2 uDirection; 25 | float2 uZoom; 26 | float2 uImageSize0; 27 | float2 uImageSize1; 28 | 29 | float4x4 WorldViewProjection; 30 | 31 | float gauss[3][3] = { 32 | 0.075, 0.124, 0.075, 33 | 0.124, 0.204, 0.124, 34 | 0.075, 0.124, 0.075 35 | }; 36 | float mod(float x, float y) { 37 | return x - y * floor(x / y); 38 | } 39 | 40 | 41 | float2 rotate(float2 vec, float r) { 42 | return mul(float1x2(vec), float2x2(cos(r), -sin(r), sin(r), cos(r))); 43 | } 44 | 45 | float lenFix(float2 vec) { 46 | return length(vec) * float2(uScreenResolution.y / uScreenResolution.x, 1); 47 | } 48 | 49 | 50 | float4 PixelShaderFunction(float2 coords : TEXCOORD0) : COLOR0 { 51 | float4 color = tex2D(uImage0, coords); 52 | float4 color2 = tex2D(uImage1, coords + float2(uTime, 0) * 0.1); 53 | if (!any(color)) 54 | return color; 55 | // float2 uv = uTargetPosition / uScreenResolution; 56 | float2 uv = uEffectPos / uScreenResolution; 57 | float2 dis = (coords - uv) * float2(uScreenResolution.x / uScreenResolution.y, 1); 58 | float2 offset = coords - uv; 59 | float distance = length(dis); 60 | if (distance > 0.5) 61 | return color; 62 | return tex2D(uImage0, uv + rotate(offset, sin(distance * 3.14) + uIntensity)); 63 | } 64 | 65 | 66 | 67 | technique Technique1 { 68 | pass Test { 69 | PixelShader = compile ps_2_0 PixelShaderFunction(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Effects/fuzzy.xnb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Effects/fuzzy.xnb -------------------------------------------------------------------------------- /Effects/test.fx: -------------------------------------------------------------------------------- 1 | // Screen Shader 模板 2 | // ---------------------------- 3 | // 以下参数*必须*添加 4 | // 因为这些参数都有原版的默认传入值 5 | // 如果缺失将导致Null Reference Exception 6 | // 0号采样贴图 7 | sampler uImage0 : register(s0); 8 | // 1号采样贴图 9 | sampler uImage1 : register(s1); 10 | // 颜色参数(默认传入白色,自己设定 11 | float3 uColor; 12 | // 透明度参数(自己设定 13 | float uOpacity; 14 | // 二级颜色参数 15 | float3 uSecondaryColor; 16 | float uTime; 17 | float2 uScreenResolution; 18 | float2 uScreenPosition; 19 | float2 uTargetPosition; 20 | float2 uImageOffset; 21 | float uIntensity; 22 | float uProgress; 23 | float2 uDirection; 24 | float2 uZoom; 25 | float2 uImageSize0; 26 | float2 uImageSize1; 27 | 28 | float gauss[3][3] = { 29 | 0.075, 0.124, 0.075, 30 | 0.124, 0.204, 0.124, 31 | 0.075, 0.124, 0.075 32 | }; 33 | 34 | float4 PixelShaderFunction(float2 coords : TEXCOORD0) : COLOR0 { 35 | float4 color = tex2D(uImage0, coords); 36 | if (!any(color)) 37 | return color; 38 | return float4(sin(uTime + coords.y), cos(uTime + coords.x), 1.0, color.a); 39 | } 40 | 41 | technique Technique1 { 42 | pass Test { 43 | PixelShader = compile ps_2_0 PixelShaderFunction(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Images/AdvInvBack1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/AdvInvBack1.png -------------------------------------------------------------------------------- /Images/AdvInvBack2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/AdvInvBack2.png -------------------------------------------------------------------------------- /Images/AdvInvBack3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/AdvInvBack3.png -------------------------------------------------------------------------------- /Images/AdvInvBackRej.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/AdvInvBackRej.png -------------------------------------------------------------------------------- /Images/Arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/Arrow.png -------------------------------------------------------------------------------- /Images/Arrow1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/Arrow1.png -------------------------------------------------------------------------------- /Images/Bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/Bar.png -------------------------------------------------------------------------------- /Images/BarFrameRank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/BarFrameRank.png -------------------------------------------------------------------------------- /Images/Box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/Box.png -------------------------------------------------------------------------------- /Images/Box2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/Box2.png -------------------------------------------------------------------------------- /Images/CloseButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/CloseButton.png -------------------------------------------------------------------------------- /Images/Cog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/Cog.png -------------------------------------------------------------------------------- /Images/CollapseButtonDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/CollapseButtonDown.png -------------------------------------------------------------------------------- /Images/CollapseButtonUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/CollapseButtonUp.png -------------------------------------------------------------------------------- /Images/Trans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/Trans.png -------------------------------------------------------------------------------- /Images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/icon.png -------------------------------------------------------------------------------- /Images/noise1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Images/noise1.png -------------------------------------------------------------------------------- /Items/Accessories/ExampleWings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Terraria; 7 | using Terraria.Localization; 8 | using Terraria.ModLoader; 9 | 10 | namespace TemplateMod2.Items.Accessories { 11 | [AutoloadEquip(EquipType.Wings)] 12 | public class ExampleWings : ModItem { 13 | public override void SetStaticDefaults() { 14 | base.SetStaticDefaults(); 15 | } 16 | public override void SetDefaults() { 17 | item.width = 22; 18 | item.height = 20; 19 | item.value = 10000; 20 | item.rare = 2; 21 | item.accessory = true; 22 | } 23 | 24 | public override void UpdateAccessory(Player player, bool hideVisual) { 25 | if (hideVisual) { 26 | player.wingTimeMax = 50; 27 | } else { 28 | player.wingTimeMax = 200; 29 | } 30 | player.wingTime = player.wingTimeMax; 31 | 32 | // 让玩家可以虚空行走 33 | if (!player.controlJump && !player.controlDown) { 34 | player.gravDir = 0f; 35 | player.velocity.Y = 0; 36 | player.gravity = 0; 37 | player.noFallDmg = true; 38 | } 39 | if (player.controlDown) { 40 | player.gravity = Player.defaultGravity; 41 | player.gravDir = 1; 42 | player.noFallDmg = true; 43 | } 44 | } 45 | 46 | // 翅膀垂直移动数据 47 | public override void VerticalWingSpeeds(Player player, ref float ascentWhenFalling, ref float ascentWhenRising, ref float maxCanAscendMultiplier, ref float maxAscentMultiplier, ref float constantAscend) { 48 | constantAscend = 1f; 49 | maxAscentMultiplier = 2f; 50 | } 51 | // 翅膀水平移动数据 52 | public override void HorizontalWingSpeeds(Player player, ref float speed, ref float acceleration) { 53 | speed = 20f; 54 | acceleration = 2.5f; 55 | } 56 | 57 | public override void AddRecipes() { 58 | base.AddRecipes(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Items/Accessories/ExampleWings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Accessories/ExampleWings.png -------------------------------------------------------------------------------- /Items/Accessories/ExampleWings_Wings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Accessories/ExampleWings_Wings.png -------------------------------------------------------------------------------- /Items/Accessories/PurpleStone.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Terraria; 7 | using Terraria.Localization; 8 | using Terraria.ModLoader; 9 | 10 | namespace TemplateMod2.Items.Accessories { 11 | public class PurpleStone : ModItem { 12 | public override void SetStaticDefaults() { 13 | DisplayName.SetDefault(""); 14 | DisplayName.AddTranslation(GameCulture.Chinese, "紫色闪光石"); 15 | Tooltip.SetDefault(""); 16 | Tooltip.AddTranslation(GameCulture.Chinese, "闪光石,只不过是紫色品质的"); 17 | base.SetStaticDefaults(); 18 | } 19 | 20 | public override void SetDefaults() { 21 | // 跟以前没啥区别 22 | item.width = 22; 23 | item.height = 22; 24 | 25 | // 重点在这里,这个属性设为true才能带在身上 26 | item.accessory = true; 27 | 28 | // 物品的面板防御数值,装备了以后就会增加 29 | item.defense = 16; 30 | 31 | item.rare = 8; 32 | item.value = Item.sellPrice(0, 5, 0, 0); 33 | 34 | // 这个属性代表这是专家模式专有物品,稀有度颜色会是彩虹的! 35 | item.expert = true; 36 | } 37 | public override void UpdateAccessory(Player player, bool hideVisual) { 38 | player.lifeRegen += 10; 39 | player.jumpSpeedBoost = 5f; 40 | player.jumpBoost = true; 41 | // 连跳 42 | player.doubleJumpBlizzard = true; 43 | player.doubleJumpCloud = true; 44 | player.doubleJumpSail = true; 45 | player.doubleJumpFart = true; 46 | player.doubleJumpSandstorm = true; 47 | player.doubleJumpUnicorn = true; 48 | 49 | if (!player.controlJump && !player.controlDown) { 50 | player.gravDir = 0f; 51 | player.velocity.Y = 0; 52 | player.gravity = 0; 53 | player.noFallDmg = true; 54 | } 55 | if (player.controlDown) { 56 | player.gravity = Player.defaultGravity; 57 | player.gravDir = 1; 58 | player.noFallDmg = true; 59 | } 60 | } 61 | public override void UseStyle(Player player) { 62 | base.UseStyle(player); 63 | } 64 | 65 | public override void AddRecipes() { 66 | base.AddRecipes(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Items/Accessories/PurpleStone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Accessories/PurpleStone.png -------------------------------------------------------------------------------- /Items/Armors/ExampleBreastplate.cs: -------------------------------------------------------------------------------- 1 | using Terraria; 2 | using Terraria.ID; 3 | using Terraria.Localization; 4 | using Terraria.ModLoader; 5 | 6 | // 确保这个文件一定要放在Items/Armors/文件夹里,与命名空间匹配 7 | // 这个套装的贴图来自ExampleMOD 8 | namespace TemplateMod2.Items.Armors { 9 | // 注意这里,这是C#里面的一个神奇的语法 10 | // 作用是给一个类附加一个属性 11 | // 比如这里就是给这个类附加一个装备样式为头盔的属性,这样TML就会把它识别成头盔 12 | [AutoloadEquip(EquipType.Body)] 13 | public class ExampleBreastplate : ModItem { 14 | // 设置物品描述的地方 15 | public override void SetStaticDefaults() { 16 | base.SetStaticDefaults(); 17 | // SetDefaults也可以不写 18 | DisplayName.AddTranslation(GameCulture.Chinese, "模板胸甲"); 19 | Tooltip.AddTranslation(GameCulture.Chinese, "被魔改了的胸甲" 20 | + "\n免疫灼烧debuff" 21 | + "\n+50生命上限" 22 | + "\n增加回血能力"); 23 | } 24 | 25 | public override void SetDefaults() { 26 | item.width = 18; 27 | item.height = 18; 28 | item.value = Item.sellPrice(0, 1, 0, 0); 29 | item.rare = ItemRarityID.Orange; 30 | // 装备的防御值 31 | item.defense = 75; 32 | } 33 | 34 | public override void UpdateEquip(Player player) { 35 | // 免疫灼伤debuff 36 | player.buffImmune[BuffID.OnFire] = true; 37 | 38 | // 增加生命上限50 39 | player.statLifeMax2 += 50; 40 | 41 | // 增加2点生命恢复,虽然看起来不多,其实在游戏里还挺可观的 42 | player.lifeRegen += 2; 43 | } 44 | 45 | public override void AddRecipes() { 46 | ModRecipe recipe = new ModRecipe(mod); 47 | recipe.SetResult(this); 48 | recipe.AddRecipe(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Items/Armors/ExampleBreastplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Armors/ExampleBreastplate.png -------------------------------------------------------------------------------- /Items/Armors/ExampleBreastplate_Arms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Armors/ExampleBreastplate_Arms.png -------------------------------------------------------------------------------- /Items/Armors/ExampleBreastplate_Body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Armors/ExampleBreastplate_Body.png -------------------------------------------------------------------------------- /Items/Armors/ExampleBreastplate_FemaleBody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Armors/ExampleBreastplate_FemaleBody.png -------------------------------------------------------------------------------- /Items/Armors/ExampleHelmet.cs: -------------------------------------------------------------------------------- 1 | using Terraria; 2 | using Terraria.ID; 3 | using Terraria.Localization; 4 | using Terraria.ModLoader; 5 | 6 | // 确保这个文件一定要放在Items/Armors/文件夹里,与命名空间匹配 7 | // 这个套装的贴图来自ExampleMOD 8 | namespace TemplateMod2.Items.Armors { 9 | // 注意这里,这是C#里面的一个神奇的语法 10 | // 作用是给一个类附加一个属性 11 | // 比如这里就是给这个类附加一个装备样式为头盔的属性,这样TML就会把它识别成头盔 12 | [AutoloadEquip(EquipType.Head)] 13 | public class ExampleHelmet : ModItem { 14 | // 设置物品描述的地方 15 | public override void SetStaticDefaults() { 16 | DisplayName.AddTranslation(GameCulture.Chinese, "模板头盔"); 17 | Tooltip.AddTranslation(GameCulture.Chinese, "这是一个被魔改了的头盔" + 18 | "\n身体周围会发红光"); 19 | } 20 | 21 | public override void SetDefaults() { 22 | item.width = 18; 23 | item.height = 18; 24 | item.value = Item.sellPrice(0, 1, 0, 0); 25 | item.rare = ItemRarityID.Orange; 26 | 27 | // 防御+12 28 | item.defense = 50; 29 | } 30 | 31 | // 重点来了,IsArmorSet这个重写函数用来判断身上的物品能不能组成一件完整套装 32 | // 比如这里我需要让头盔,胸甲,护腿全部都是模板装备的才能算是套装 33 | public override bool IsArmorSet(Item head, Item body, Item legs) { 34 | return body.type == mod.ItemType("ExampleBreastplate") && legs.type == mod.ItemType("ExampleLeggings"); 35 | } 36 | // 如果在上面的函数中玩家被判定穿上了模板套装,那么就会在这里执行其效果 37 | public override void UpdateArmorSet(Player player) { 38 | // 套装描述,就是鼠标移上去最底下显示的套装效果 39 | player.setBonus = "进一步增加回血速度,吸取红心范围增大" + 40 | "\n增加10%伤害减免"; 41 | player.endurance += 0.6f; 42 | player.lifeRegen += 2; 43 | player.lifeMagnet = true; 44 | // 加点特技 45 | player.armorEffectDrawShadow = true; 46 | } 47 | 48 | 49 | public override void AddRecipes() { 50 | ModRecipe recipe = new ModRecipe(mod); 51 | recipe.AddIngredient(ItemID.CrimtaneBar, 99); 52 | recipe.AddTile(TileID.Anvils); 53 | recipe.SetResult(this); 54 | recipe.AddRecipe(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Items/Armors/ExampleHelmet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Armors/ExampleHelmet.png -------------------------------------------------------------------------------- /Items/Armors/ExampleHelmet_Head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Armors/ExampleHelmet_Head.png -------------------------------------------------------------------------------- /Items/Armors/ExampleLeggings.cs: -------------------------------------------------------------------------------- 1 | using Terraria; 2 | using Terraria.ID; 3 | using Terraria.Localization; 4 | using Terraria.ModLoader; 5 | 6 | // 确保这个文件一定要放在Items/Armors/文件夹里,与命名空间匹配 7 | // 这个套装的贴图来自ExampleMOD 8 | namespace TemplateMod2.Items.Armors { 9 | // 注意这里,这是C#里面的一个神奇的语法 10 | // 作用是给一个类附加一个属性 11 | // 比如这里就是给这个类附加一个装备样式为护腿的属性,这样TML就会把它识别成护腿 12 | [AutoloadEquip(EquipType.Legs)] 13 | public class ExampleLeggings : ModItem { 14 | // 设置物品描述的地方 15 | public override void SetStaticDefaults() { 16 | DisplayName.AddTranslation(GameCulture.Chinese, "模板护腿"); 17 | Tooltip.AddTranslation(GameCulture.Chinese, "这是一个被魔改了的护腿" 18 | + "\n玩家在移动的时候增加5%全部伤害"); 19 | } 20 | 21 | public override void SetDefaults() { 22 | item.width = 18; 23 | item.height = 18; 24 | item.value = Item.sellPrice(0, 1, 0, 0); 25 | item.rare = ItemRarityID.Orange; 26 | 27 | // 防御+12 28 | item.defense = 15; 29 | } 30 | 31 | public override void UpdateEquip(Player player) { 32 | // 如果玩家的速度的值大于一定值,也就是玩家在移动 33 | if (player.velocity.Length() > 0.05f) { 34 | // 就增加全部伤害 35 | player.allDamage += 0.05f; 36 | } 37 | } 38 | 39 | public override void AddRecipes() { 40 | ModRecipe recipe = new ModRecipe(mod); 41 | recipe.AddIngredient(ItemID.CrimtaneBar, 99); 42 | recipe.AddTile(TileID.Anvils); 43 | recipe.SetResult(this); 44 | recipe.AddRecipe(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Items/Armors/ExampleLeggings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Armors/ExampleLeggings.png -------------------------------------------------------------------------------- /Items/Armors/ExampleLeggings_Legs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/Armors/ExampleLeggings_Legs.png -------------------------------------------------------------------------------- /Items/PoisonousPotion.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using TemplateMod2.Buffs; 4 | using Terraria; 5 | using Terraria.ID; 6 | using Terraria.Localization; 7 | using Terraria.ModLoader; 8 | 9 | // 注意这里命名空间变了,多了个.Items 10 | // 因为这个文件在Items文件夹,而读取图片的时候是根据命名空间读取的,如果写错了可能图片就读不到了 11 | namespace TemplateMod2.Items { 12 | 13 | // 保证类名跟文件名一致,这样也方便查找 14 | public class PoisonousPotion : ModItem { 15 | 16 | // 设置物品名字,描述的地方,这个函数需要记住 17 | public override void SetStaticDefaults() { 18 | 19 | // 这个是物品名字,也就是忽略游戏语言的情况下显示的文字 20 | DisplayName.SetDefault("Skirt Sword"); 21 | // 推荐通过AddTranslation的方式添加其在切换到中文的时候显示中文名字 22 | DisplayName.AddTranslation(GameCulture.Chinese, "模板剑"); 23 | 24 | // 物品的描述,加入换行符 '\n' 可以多行显示哦 25 | Tooltip.SetDefault("What is this blade made of?\n" + 26 | "Ohh, Iron..."); 27 | // 同理,我们加一个中文的翻译(???我们不本来就是中国人? 28 | Tooltip.AddTranslation(GameCulture.Chinese, "它是由什么做的?\n" + 29 | "哦铁啊,那没事了"); 30 | } 31 | 32 | 33 | public override void SetDefaults() { 34 | // 这部分就不说了 35 | item.width = 14; 36 | item.height = 24; 37 | item.useAnimation = 17; 38 | item.useTime = 17; 39 | item.maxStack = 30; 40 | item.rare = 5; 41 | item.value = Item.sellPrice(0, 0, 50, 0); 42 | 43 | 44 | // 物品的使用方式,还记得2是什么吗 45 | item.useStyle = 2; 46 | // 喝药的声音 47 | item.UseSound = SoundID.Item3; 48 | 49 | // 决定这个物品使用以后会不会减少,true就是使用后物品会少一个,默认为false 50 | item.consumable = true; 51 | // 决定使用动画出现后,玩家转身会不会影响动画的方向,true就是会,默认为false 52 | item.useTurn = true; 53 | // 告诉TR内部系统,这个物品是一个生命药水物品,用于TR系统的特殊目的(比如一键喝药水),默认为false 54 | item.potion = false; 55 | // 这个药水能给玩家加多少血,跟potion一起使用喝完药就会有抗药性debuff 56 | item.healLife = 50; 57 | // 加buff的方法1:设置物品的buffType为buff的ID 58 | // 这里我设置了着火debuff(2333 59 | // item.buffType = BuffID.Poisoned; 60 | // 用于在物品描述上显示buff持续时间 61 | //item.buffTime = 60000; 62 | } 63 | 64 | // 当物品使用的时候触发,返回值貌似是什么都不会有影响 65 | public override bool UseItem(Player player) { 66 | // 给玩家加上中毒buff,持续 60000 / 60 = 1000秒 67 | // 第一个填buff的ID,第二个填持续时间 68 | player.AddBuff(ModContent.BuffType(), 300); 69 | return false; 70 | } 71 | 72 | // 物品合成表的设置部分 73 | public override void AddRecipes() { 74 | ModRecipe recipe = new ModRecipe(mod); 75 | recipe.AddIngredient(ItemID.GoldBar, 5); 76 | recipe.AddIngredient(ItemID.IronBar, 5); 77 | recipe.AddIngredient(ItemID.Torch, 25); 78 | recipe.AddTile(TileID.WorkBenches); 79 | recipe.AddTile(TileID.Anvils); 80 | recipe.SetResult(this, 99); 81 | recipe.AddRecipe(); 82 | 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Items/PoisonousPotion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/PoisonousPotion.png -------------------------------------------------------------------------------- /Items/SkirtSword.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using TemplateMod2.Buffs; 4 | using TemplateMod2.Utils; 5 | using Terraria; 6 | using Terraria.ID; 7 | using Terraria.Localization; 8 | using Terraria.ModLoader; 9 | 10 | // 注意这里命名空间变了,多了个.Items 11 | // 因为这个文件在Items文件夹,而读取图片的时候是根据命名空间读取的,如果写错了可能图片就读不到了 12 | namespace TemplateMod2.Items { 13 | 14 | // 保证类名跟文件名一致,这样也方便查找 15 | public class SkirtSword : ModItem { 16 | 17 | // 设置物品名字,描述的地方,这个函数需要记住 18 | public override void SetStaticDefaults() { 19 | 20 | // 这个是物品名字,也就是忽略游戏语言的情况下显示的文字 21 | DisplayName.SetDefault("Skirt Sword"); 22 | // 推荐通过AddTranslation的方式添加其在切换到中文的时候显示中文名字 23 | DisplayName.AddTranslation(GameCulture.Chinese, "模板剑"); 24 | 25 | // 物品的描述,加入换行符 '\n' 可以多行显示哦 26 | Tooltip.SetDefault("What is this blade made of?\n" + 27 | "Ohh, Iron..."); 28 | // 同理,我们加一个中文的翻译(???我们不本来就是中国人? 29 | Tooltip.AddTranslation(GameCulture.Chinese, "它是由什么做的?\n" + 30 | "哦铁啊,那没事了"); 31 | } 32 | 33 | public override void SetDefaults() { 34 | // 伤害!想都不要想,后面这个值随便改吧,但是不要超过2147483647 35 | // 不然…… 你试试就知道了 36 | item.damage = 10; 37 | 38 | // 决定了这个武器的伤害属性, 39 | // melee 代表近战 40 | // ranged 代表远程 41 | // magic 代表膜法,不,魔法 42 | // summon 代表召唤 43 | // thrown 代表投掷 44 | item.melee = true; 45 | 46 | // 物品的碰撞体积大小,可以与贴图无关,但是建议设为跟贴图一样的大小 47 | // 不然鬼知道会不会发生奇怪的事情 48 | item.width = 40; 49 | item.height = 40; 50 | 51 | // 攻击速度和攻击动画持续时间! 52 | // 这个数值越低越快,因为TR游戏速度每秒是60帧,这里的20就是 53 | // 20.0 / 60.0 = 0.333 秒挥动一次!也就是一秒三次 54 | // 一般来说我们要把这两个值设成一样,但也有例外的时候,我们以后会讲 55 | item.useTime = 4; 56 | item.useAnimation = 4; 57 | 58 | // 使用方式,这个值决定了武器使用时到底是按什么样的动画播放 59 | // 1 代表挥动,也就是剑类武器! 60 | // 2 代表像药水一样喝下去,emmmm这个放在剑上会不会很奇怪(吞 61 | // 3 代表像同志短剑一样刺x 出去 62 | // 4 唔,这个一般不是用在武器上的,想象一下生命水晶使用的时候的动作 63 | // 5 手持,枪、弓、法杖类武器的动作,用途最广 64 | item.useStyle = 1; 65 | 66 | // 击退,你懂的,但是这个击退有个上限就是20,超过20击退效果跟20没什么区别 67 | // 后面的 'f' 表示这是个浮点数:8.25,但是这个'f'不可省略 68 | item.knockBack = 8.25f; 69 | 70 | // 物品的价格,这里用sellPrice,也就是卖出物品的价格作为基准 71 | // 这件物品卖出时会获得 0白金 1金 60银 0铜 这么多的钱 (就这? 72 | item.value = Item.sellPrice(0, 1, 60, 0); 73 | 74 | // 物品的稀有度,由-1到13越来越高,具体参考维基百科 75 | // https://terraria.gamepedia.com/Rarity 或者裙中世界的补充栏目 76 | item.rare = 1; 77 | 78 | // 设置这个物品使用时发出的声音,以后会讲到怎么调出其他声音 79 | // 在这里我用的是普通的挥剑声音 80 | item.UseSound = SoundID.Item1; 81 | 82 | // 决定了这个武器鼠标按住不放能不能一直攻击, true代表可以, false代表不行 83 | // (鼠标别按废了 84 | item.autoReuse = true; 85 | 86 | // 射出泰拉剑气 87 | item.shoot = ProjectileID.TerraBeam; 88 | item.shootSpeed = 7f; 89 | } 90 | 91 | public override void MeleeEffects(Player player, Rectangle hitbox) { 92 | 93 | } 94 | 95 | public override void OnHitNPC(Player player, NPC target, int damage, float knockBack, bool crit) { 96 | // 给怪物加上我们之前做的Buff,持续10秒 97 | target.AddBuff(ModContent.BuffType(), 600); 98 | } 99 | 100 | public override void ModifyHitNPC(Player player, NPC target, ref int damage, ref float knockBack, ref bool crit) { 101 | base.ModifyHitNPC(player, target, ref damage, ref knockBack, ref crit); 102 | } 103 | 104 | //public override bool Shoot(Player player, ref Vector2 position, ref float speedX, ref float speedY, 105 | // ref int type, ref int damage, ref float knockBack) { 106 | // type = Main.rand.Next(Main.projectileTexture.Length); 107 | // return true; 108 | //} 109 | 110 | 111 | 112 | 113 | 114 | // 物品合成表的设置部分 115 | public override void AddRecipes() { 116 | ModRecipe recipe = new ModRecipe(mod); 117 | // 合成材料,需要10个泥土块 118 | recipe.AddIngredient(ItemID.DirtBlock, 10); 119 | // 以及在工作台旁边 120 | recipe.AddTile(TileID.WorkBenches); 121 | // 生成1个这种物品 122 | recipe.SetResult(this); 123 | // 这样可以生成50个 124 | // recipe.SetResult(this, 50); 125 | 126 | // 把这个合成表装进tr的系统里 127 | recipe.AddRecipe(); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Items/SkirtSword.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/SkirtSword.png -------------------------------------------------------------------------------- /Items/SkirtSword2.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using TemplateMod2.NPCs; 4 | using TemplateMod2.Projectiles; 5 | using Terraria; 6 | using Terraria.ID; 7 | using Terraria.Localization; 8 | using Terraria.ModLoader; 9 | 10 | // 注意这里命名空间变了,多了个.Items 11 | // 因为这个文件在Items文件夹,而读取图片的时候是根据命名空间读取的,如果写错了可能图片就读不到了 12 | namespace TemplateMod2.Items { 13 | //public class Item2 { 14 | // // 体积 15 | // public double volume; 16 | // // 密度 17 | // public double density; 18 | // // 单位价格 19 | // public double price; 20 | // // 物品ID 21 | // private int id; 22 | 23 | // public Item2(double volume, double density, double price, int id) { 24 | // this.volume = volume; 25 | // this.density = density; 26 | // this.price = price; 27 | // this.id = id; 28 | // } 29 | 30 | // public virtual void Use() { 31 | // Main.NewText("物品被使用了!"); 32 | // } 33 | // protected virtual double Price { 34 | // get { 35 | // return volume * density * price; 36 | // } 37 | // } 38 | // public void Sell() { 39 | // Main.NewText($"物品被卖出了{GetPrice()}元钱"); 40 | // } 41 | 42 | // public int ID { 43 | // get { return id; } 44 | // } 45 | //} 46 | 47 | 48 | // 保证类名跟文件名一致,这样也方便查找 49 | public class SkirtSword2 : ModItem { 50 | 51 | // 设置物品名字,描述的地方,这个函数需要记住 52 | public override void SetStaticDefaults() { 53 | 54 | // 这个是物品名字,也就是忽略游戏语言的情况下显示的文字 55 | DisplayName.SetDefault("Skirt Sword"); 56 | // 推荐通过AddTranslation的方式添加其在切换到中文的时候显示中文名字 57 | DisplayName.AddTranslation(GameCulture.Chinese, "版模剑"); 58 | 59 | // 物品的描述,加入换行符 '\n' 可以多行显示哦 60 | Tooltip.SetDefault("What is this blade made of?\n" + 61 | "Ohh, Iron..."); 62 | // 同理,我们加一个中文的翻译(???我们不本来就是中国人? 63 | Tooltip.AddTranslation(GameCulture.Chinese, "它是由什么做的?\n" + 64 | "哦铁啊,那没事了"); 65 | } 66 | 67 | 68 | public override void SetDefaults() { 69 | // 伤害!想都不要想,后面这个值随便改吧,但是不要超过2147483647 70 | // 不然…… 你试试就知道了 71 | item.damage = 50; 72 | 73 | // 决定了这个武器的伤害属性, 74 | // melee 代表近战 75 | // ranged 代表远程 76 | // magic 代表膜法,不,魔法 77 | // summon 代表召唤 78 | // thrown 代表投掷 79 | item.melee = true; 80 | 81 | // 物品的碰撞体积大小,可以与贴图无关,但是建议设为跟贴图一样的大小 82 | // 不然鬼知道会不会发生奇怪的事情 83 | item.width = 40; 84 | item.height = 40; 85 | 86 | // 攻击速度和攻击动画持续时间! 87 | // 这个数值越低越快,因为TR游戏速度每秒是60帧,这里的20就是 88 | // 20.0 / 60.0 = 0.333 秒挥动一次!也就是一秒三次 89 | // 一般来说我们要把这两个值设成一样,但也有例外的时候,我们以后会讲 90 | item.useTime = 14; 91 | item.useAnimation = 14; 92 | item.shoot = ModContent.ProjectileType(); 93 | item.shootSpeed = 6f; 94 | 95 | // 使用方式,这个值决定了武器使用时到底是按什么样的动画播放 96 | // 1 代表挥动,也就是剑类武器! 97 | // 2 代表像药水一样喝下去,emmmm这个放在剑上会不会很奇怪(吞 98 | // 3 代表像同志短剑一样刺x 出去 99 | // 4 唔,这个一般不是用在武器上的,想象一下生命水晶使用的时候的动作 100 | // 5 手持,枪、弓、法杖类武器的动作,用途最广 101 | item.useStyle = 1; 102 | 103 | // 击退,你懂的,但是这个击退有个上限就是20,超过20击退效果跟20没什么区别 104 | // 后面的 'f' 表示这是个浮点数:8.25,但是这个'f'不可省略 105 | item.knockBack = 0f; 106 | 107 | // 物品的价格,这里用sellPrice,也就是卖出物品的价格作为基准 108 | // 这件物品卖出时会获得 0白金 1金 60银 0铜 这么多的钱 (就这? 109 | item.value = Item.sellPrice(0, 1, 60, 0); 110 | 111 | // 物品的稀有度,由-1到13越来越高,具体参考维基百科 112 | //https://terraria.gamepedia.com/Rarity 或者裙中世界的补充栏目 113 | item.rare = 1; 114 | 115 | // 设置这个物品使用时发出的声音,以后会讲到怎么调出其他声音 116 | // 在这里我用的是普通的挥剑声音 117 | item.UseSound = SoundID.Item1; 118 | 119 | // 决定了这个武器鼠标按住不放能不能一直攻击, true代表可以, false代表不行 120 | // (鼠标别按废了 121 | item.autoReuse = false; 122 | 123 | 124 | } 125 | private float crossProduct(Vector2 v1, Vector2 v2) { 126 | return v1.X * v2.Y - v1.Y * v2.X; 127 | } 128 | 129 | // 参数分别是,射出位置,射出速度,目标的位置和重力系数 130 | private Tuple GetHitPoint(Vector2 pos, Vector2 shootVec, Vector2 targetPos, float gravity) { 131 | int t = 1; 132 | while (true) { 133 | pos += shootVec; 134 | // 如果速度向下,就是下落状态,并且下落到与目标相同(或更低)的高度 135 | // 越过了这个高度下落弹幕就没办法再击中敌人了,直接返回X坐标和所用时间 136 | if (shootVec.Y > 0 && pos.Y > targetPos.Y) 137 | return new Tuple(pos.X, t); 138 | // 模拟重力作用 139 | shootVec.Y += gravity; 140 | t++; 141 | } 142 | } 143 | 144 | // 获取到达固定点target所需的发射向量以及时间 145 | private Tuple GetShootVec(Vector2 pos, Vector2 target, float speed, float gravity) { 146 | // 二分法的范围包括了左右两边的弧度,要注意一下 147 | float L = -MathHelper.PiOver2, R = -MathHelper.PiOver4; 148 | Tuple ans = new Tuple(Vector2.Zero, 0); 149 | // 精度足够我们就停止 150 | while (R - L > 0.001f) { 151 | float mid = (L + R) / 2; 152 | Tuple tmp = GetHitPoint(pos, mid.ToRotationVector2() * speed, target, gravity); 153 | // 如果X坐标在目标左边说明我们要降低角度,反之增加 154 | if (tmp.Item1 < target.X) { 155 | L = mid; 156 | } else { 157 | R = mid; 158 | } 159 | ans = new Tuple(mid.ToRotationVector2() * speed, tmp.Item2); 160 | } 161 | return ans; 162 | } 163 | // 获取预判向量 164 | private Vector2 GetPredictVec(Vector2 pos, NPC npc, float speed, float gravity) { 165 | // 一开始我们假设往npc现在所处位置发射弹幕 166 | Vector2 target = npc.Center - pos; 167 | for (int i = 0; i < 20; i++) { 168 | // 获取弹幕的飞行时间和射出速度 169 | Tuple info = GetShootVec(pos, target, speed, gravity); 170 | // 过了这么多时间以后npc到哪里了? 171 | Vector2 npcPos = npc.Center + info.Item2 * npc.velocity; 172 | // 我们射到的位置是否距离npc最终位置足够近? 173 | if (Vector2.Distance(target, npcPos) < 0.1f) { 174 | Main.NewText($"足够近 {info.Item2} {info.Item1}"); 175 | // 足够近,我们直接返回这个发射向量 176 | return info.Item1; 177 | } 178 | // 不够近,我们把目标位置改动一下,进行下一次尝试 179 | target = npcPos; 180 | } 181 | // 怎么样都不够近,算了放弃吧 182 | return Vector2.Zero; 183 | } 184 | 185 | bool flag = false; 186 | public override bool Shoot(Player player, ref Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack) { 187 | //Main.dayTime = false; 188 | //Main.time = 0; 189 | NPC target = null; 190 | foreach (var npc in Main.npc) { 191 | if (npc.active && npc.type == ModContent.NPCType()) { 192 | target = npc; 193 | break; 194 | } 195 | } 196 | //if (target != null) { 197 | // if (!flag) { 198 | // WormHead head = (WormHead)target.modNPC; 199 | // WormBodyNPC wmb = (WormBodyNPC)target.modNPC; 200 | // for (int i = 0; i < 45; i++) { 201 | // WormBodyNPC nxt = (WormBodyNPC)Main.npc[wmb.Tail].modNPC; 202 | // if (i % 6 == 5) { 203 | // var nhead = WormHead.SpawnHead(wmb.npc); 204 | // nhead.velocity = Main.rand.NextVector2CircularEdge(1, 1) * 20f; 205 | // wmb.Head = nhead.whoAmI; 206 | // } 207 | // wmb = nxt; 208 | // } 209 | // } else { 210 | // foreach (var npc in Main.npc) { 211 | // if (npc.active && npc.type == ModContent.NPCType()) { 212 | // var worm = (WormBodyNPC)npc.modNPC; 213 | // if (worm.Tail == 0) { 214 | // npc.active = false; 215 | // } 216 | // } 217 | // if (npc.active && npc.modNPC is WormBodyNPC) { 218 | // var worm = (WormBodyNPC)npc.modNPC; 219 | // if (worm.Tail != 0) { 220 | // var pv = (WormBodyNPC)Main.npc[worm.Tail].modNPC; 221 | // pv.Head = worm.npc.whoAmI; 222 | // } 223 | // } 224 | // } 225 | // } 226 | // flag ^= true; 227 | //} 228 | return true; 229 | //float maxDis = 1000f; 230 | //NPC target = null; 231 | //foreach (var npc in Main.npc) { 232 | // if (npc.active && !npc.friendly && npc.value > 0 && !npc.dontTakeDamage) { 233 | // float dis = Vector2.Distance(npc.Center, position); 234 | // if (dis < maxDis) { 235 | // maxDis = dis; 236 | // target = npc; 237 | // } 238 | // } 239 | //} 240 | //if (target != null) 241 | // Projectile.NewProjectile(position, GetPredictVec(position, target, 16f, 0.3f), type, 100, 5f, player.whoAmI); 242 | //return false; 243 | //float maxDis = 1000f; 244 | //NPC target = null; 245 | //foreach (var npc in Main.npc) { 246 | // if (npc.active && !npc.friendly && npc.value > 0 && !npc.dontTakeDamage) { 247 | // float dis = Vector2.Distance(npc.Center, position); 248 | // if (dis < maxDis) { 249 | // maxDis = dis; 250 | // target = npc; 251 | // } 252 | // } 253 | //} 254 | //if (target != null) { 255 | // Vector2 targetPos = target.Center; 256 | // Vector2 plrToNPC = targetPos - position; 257 | // float speed = 20f; 258 | // float tmp = crossProduct(plrToNPC, target.velocity); 259 | // float offset = plrToNPC.ToRotation(); 260 | // float G = tmp / speed / plrToNPC.Length(); 261 | // if (G > 1 || G < -1) { 262 | // Main.NewText("无法预判QAQ"); 263 | // return true; 264 | // } 265 | // float realr = (float)(offset + Math.Asin(G)); 266 | // Projectile.NewProjectile(position, realr.ToRotationVector2() * speed / 2, type, 100, 5f, player.whoAmI); 267 | // return false; 268 | //} else { 269 | // return true; 270 | //} 271 | } 272 | 273 | 274 | // 物品合成表的设置部分 275 | public override void AddRecipes() { 276 | ModRecipe recipe = new ModRecipe(mod); 277 | recipe.AddIngredient(ItemID.GoldBar, 5); 278 | recipe.AddIngredient(ItemID.IronBar, 5); 279 | recipe.AddIngredient(ItemID.Torch, 25); 280 | recipe.AddTile(TileID.WorkBenches); 281 | recipe.AddTile(TileID.Anvils); 282 | recipe.SetResult(this, 99); 283 | recipe.AddRecipe(); 284 | 285 | } 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /Items/SkirtSword2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/SkirtSword2.png -------------------------------------------------------------------------------- /Items/TemplateGun.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * 这是一个基本枪械类武器的例子 3 | */ 4 | 5 | using Microsoft.Xna.Framework; 6 | using System; 7 | using System.Collections.Generic; 8 | using Terraria; 9 | using Terraria.ID; 10 | using Terraria.Localization; 11 | using Terraria.ModLoader; 12 | 13 | // 注意这里命名空间变了,多了个.Items 14 | // 因为这个文件在Items文件夹,而读取图片的时候是根据命名空间读取的,如果写错了可能图片就读不到了 15 | // 跟那把剑一样,后面我就不说了 16 | namespace TemplateMod2.Items { 17 | // 保证类名跟文件名一致,这样也方便查找 18 | public class TemplateGun : ModItem { 19 | 20 | 21 | // 设置物品名字,描述的地方 22 | public override void SetStaticDefaults() { 23 | base.SetStaticDefaults(); 24 | 25 | // 这个是物品名字,也就是忽略游戏语言的情况下显示的文字 26 | DisplayName.SetDefault("Gun"); 27 | // 推荐通过AddTranslation的方式添加其在切换到中文的时候显示中文名字 28 | DisplayName.AddTranslation(GameCulture.Chinese, "手枪"); 29 | 30 | // 物品的描述,加入换行符 '\n' 可以多行显示哦 31 | Tooltip.SetDefault("Nothing more..."); 32 | // 同理,我们加一个中文的翻译(???我们不本来就是中国人? 33 | Tooltip.AddTranslation(GameCulture.Chinese, "没什么特别的"); 34 | } 35 | 36 | // 最最最重要的物品基本属性部分 37 | public override void SetDefaults() { 38 | // 伤害!知道该做什么了吧,后面这个值随便改吧,但是不要超过2147483647 39 | // 不然…… 你试试就知道了 40 | item.damage = 10; 41 | 42 | // 击退,你懂的,但是这个击退有个上限就是20.0f,超过20击退效果跟20没什么区别 43 | // 后面的 'f' 表示这是个小数,0.25 44 | item.knockBack = 0.25f; 45 | 46 | // 物品的基础暴击几率,比正常物品少了 4% 呢 47 | item.crit = -4; 48 | 49 | // 物品的稀有度,由-1到13越来越高,具体参考维基百科或者我的博客 50 | item.rare = 2; 51 | 52 | // 攻击速度和攻击动画持续时间! 53 | // 这个数值越低越快,因为TR游戏速度每秒是60帧,这里的10就是 54 | // 10.0 / 60.0 = 0.1666... 秒使用1次!也就是一秒6次 55 | // 一般来说我们要把这两个值设成一样,但也有例外的时候,我们以后会讲 56 | item.useTime = 90; 57 | item.useAnimation = 90; 58 | 59 | // 使用方式,这个值决定了武器使用时到底是按什么样的动画播放 60 | // 1 代表挥动,也就是剑类武器! 61 | // 2 代表像药水一样喝下去,emmmm这个放在剑上会不会很奇怪(吞 62 | // 3 代表像同志短剑一样刺x 出去 63 | // 4 唔,这个一般不是用在武器上的,想象一下生命水晶使用的时候的动作 64 | // 5 手持,枪、弓、法杖类武器的动作,用途最广,这里就用它 65 | item.useStyle = 5; 66 | 67 | // 决定了这个武器鼠标按住不放能不能一直攻击, true代表可以 68 | // (我就是要按废你的鼠标! 69 | item.autoReuse = true; 70 | 71 | // 决定了这个武器的伤害属性, 72 | // melee 代表近战 73 | // ranged 代表远程 74 | // magic 代表膜法,不,魔法 75 | // summon 代表召唤 76 | // thrown 代表投掷 77 | item.melee = true; 78 | 79 | // 物品的价格,这里用sellPrice,也就是卖出物品的价格作为基准 80 | item.value = Item.sellPrice(0, 1, 0, 0); 81 | 82 | // 设置这个物品使用时发出的声音,以后会讲到怎么调出其他声音 83 | // 在这里我用的是开枪的声音 84 | item.UseSound = SoundID.Item36; 85 | 86 | // 物品的碰撞体积大小,可以与贴图无关,但是建议设为跟贴图一样的大小 87 | // 不然鬼知道会不会发生奇怪的事情(无所谓的) 88 | item.width = 60; 89 | item.height = 60; 90 | 91 | // 让它变小一点 92 | item.scale = 0.85f; 93 | 94 | // 最大堆叠数量,唔,对于一般武器来说,即使你堆了99个,使用的时候还是只有一个的效果 95 | item.maxStack = 1; 96 | 97 | //------------------------------------------------------------------------- 98 | // 接下来就是枪械武器独特的属性 99 | 100 | // noMelee代表这个武器使用的时候贴图会不会造成伤害 101 | // 如果你希望开枪的时候你的手枪还能敲在敌人头上就把它设为false 102 | // 反正我不希望:(,就当枪本身没有伤害吧 103 | item.noMelee = false; 104 | 105 | // 决定这个物品使用时射出点什么和射出的速度的量 106 | // 这里我让枪射出子弹,并且以 (7像素 / 帧) 的速度射出去 107 | item.shoot = ProjectileID.TerraBeam; 108 | item.shootSpeed = 7f; 109 | 110 | // 选择这个枪射出(的时候消耗什么作为弹药,这里选择子弹 111 | // 你也可以删(或者注释)掉这一句,这样枪就什么都不消耗了 112 | //【重要】如果设置了消耗什么弹药,那么之前shoot设置的值就会被弹药物品的属性所覆盖 113 | // 也就是说,你到底射出的是什么就由弹药决定了! 114 | item.useAmmo = AmmoID.Dart; 115 | 116 | // 好了,到这里差不多就是一个普通的枪需要填写的属性了 117 | // 至于更高级的枪怎么制作,嘿嘿,往后看吧。 118 | Item.staff[item.type] = true; 119 | } 120 | 121 | 122 | // 控制这把枪使用时候的重写函数 123 | public override bool Shoot(Player player, ref Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack) { 124 | //var proj = Projectile.NewProjectileDirect(position, new Vector2(speedX, speedY), type, damage, knockBack); 125 | //proj.noDropItem = true; 126 | return true; 127 | } 128 | 129 | public override Vector2? HoldoutOffset() { 130 | return new Vector2(-10, -5); 131 | } 132 | 133 | public override Vector2? HoldoutOrigin() { 134 | return new Vector2(-50, -50); 135 | } 136 | 137 | public override bool HoldItemFrame(Player player) { 138 | // 选中武器的时候设置为第3帧 139 | player.bodyFrame.Y = player.bodyFrame.Height * 2; 140 | return true; 141 | } 142 | public override void HoldStyle(Player player) { 143 | base.HoldStyle(player); 144 | } 145 | 146 | 147 | // 物品合成表的设置部分 148 | // 我没有写,这部分由你写 149 | public override void AddRecipes() { 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Items/TemplateGun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Items/TemplateGun.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 CXUtk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NPCs/StateMachine/NPCState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Terraria; 7 | using Terraria.ModLoader; 8 | 9 | namespace TemplateMod2.NPCs.StateMachine { 10 | public abstract class NPCState { 11 | protected NPC npc { get; set; } 12 | public NPCState(SMNPC mnpc) { 13 | npc = mnpc.npc; 14 | } 15 | public abstract void AI(SMNPC mnpc); 16 | } 17 | 18 | public class EmptyState : NPCState { 19 | public EmptyState(SMNPC mnpc) : base(mnpc) { } 20 | public override void AI(SMNPC mnpc) { 21 | 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /NPCs/StateMachine/SMNPC.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Terraria; 7 | using Terraria.ModLoader; 8 | 9 | namespace TemplateMod2.NPCs.StateMachine { 10 | /// 11 | /// 基于状态机的ModProjectile类,一定要先在Initialize里注册弹幕的状态才能使用哦 12 | /// 13 | public abstract class SMNPC : ModNPC { 14 | public NPCState currentState => projStates[State - 1]; 15 | private List projStates = new List(); 16 | private Dictionary stateDict = new Dictionary(); 17 | private int State { 18 | get { return (int)npc.ai[0]; } 19 | set { npc.ai[0] = (int)value; } 20 | } 21 | public int Timer { 22 | get { return (int)npc.ai[1]; } 23 | set { npc.ai[1] = value; } 24 | } 25 | public Player TargetPlayer { 26 | get { return Main.player[npc.target]; } 27 | } 28 | /// 29 | /// 把当前状态变为指定的弹幕状态实例 30 | /// 31 | /// 注册过的类名 32 | public void SetState() where T : NPCState { 33 | var name = typeof(T).FullName; 34 | if (!stateDict.ContainsKey(name)) throw new ArgumentException("这个状态并不存在"); 35 | State = stateDict[name]; 36 | } 37 | /// 38 | /// 判断NPC是否处于某个状态 39 | /// 40 | /// 注册过的类名 41 | /// 42 | public bool AtState() where T : NPCState { 43 | var name = typeof(T).FullName; 44 | if (!stateDict.ContainsKey(name)) throw new ArgumentException("这个状态并不存在"); 45 | return State == stateDict[name]; 46 | } 47 | /// 48 | /// 注册状态 49 | /// 50 | /// 需要注册的 51 | /// 需要注册的类的实例 52 | protected void RegisterState() where T : NPCState { 53 | var name = typeof(T).FullName; 54 | if (stateDict.ContainsKey(name)) throw new ArgumentException("这个状态已经注册过了"); 55 | var state = (T)Activator.CreateInstance(typeof(T), new[] { this }); 56 | projStates.Add(state); 57 | stateDict.Add(name, projStates.Count); 58 | } 59 | 60 | /// 61 | /// 初始化函数,用于注册弹幕状态 62 | /// 63 | public abstract void Initialize(); 64 | /// 65 | /// 我把AI函数封住了,这样在子类无法重写AI函数,只能用before和after函数 66 | /// 67 | public sealed override void AI() { 68 | if (State == 0) { 69 | Initialize(); 70 | State = 1; 71 | } 72 | AIBefore(); 73 | currentState.AI(this); 74 | AIAfter(); 75 | } 76 | /// 77 | /// 在状态机执行之前要执行的代码 78 | /// 79 | public virtual void AIAfter() { } 80 | /// 81 | /// 在状态机执行之后要执行的代码 82 | /// 83 | public virtual void AIBefore() { } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /NPCs/SuperMage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using TemplateMod2.Projectiles; 8 | using Terraria; 9 | using Terraria.ID; 10 | using Terraria.ModLoader; 11 | 12 | namespace TemplateMod2.NPCs { 13 | public class SuperMage : ModNPC { 14 | public override void SetStaticDefaults() { 15 | DisplayName.SetDefault("测试小伙"); 16 | //该NPC的游戏内显示名 17 | Main.npcFrameCount[npc.type] = Main.npcFrameCount[NPCID.Wizard]; 18 | //NPC总共帧图数,一般为16+下面两种帧的帧数 19 | NPCID.Sets.ExtraFramesCount[npc.type] = NPCID.Sets.ExtraFramesCount[NPCID.Wizard]; 20 | //额外活动帧,一般为5 21 | NPCID.Sets.AttackFrameCount[npc.type] = NPCID.Sets.AttackFrameCount[NPCID.Wizard]; 22 | //攻击帧,这个帧数取决于你的NPC攻击类型,射手填5,战士和投掷填4,法师填2,当然,也可以多填,就是不知效果如何(这里直接引用商人的) 23 | NPCID.Sets.DangerDetectRange[npc.type] = 1000; 24 | //巡敌范围,以像素为单位,这个似乎是半径 25 | NPCID.Sets.AttackType[npc.type] = NPCID.Sets.AttackType[NPCID.Wizard]; 26 | //攻击类型,一般为0,想要模仿其他NPC就填他们的ID 27 | NPCID.Sets.AttackTime[npc.type] = 60; 28 | //单次攻击持续时间,越短,则该NPC攻击越快(可以用来模拟长时间施法的NPC) 29 | NPCID.Sets.AttackAverageChance[npc.type] = 5; 30 | //NPC遇敌的攻击优先度,该数值越大则NPC遇到敌怪时越会优先选择逃跑,反之则该NPC越好斗。 31 | //最小一般为1,你可以试试0或负数LOL~ 32 | NPCID.Sets.MagicAuraColor[npc.type] = Color.Cyan; 33 | //如果该NPCc属于法师类,你可以加上这个来改变NPC的魔法光环颜色,这里用紫色 34 | 35 | } 36 | 37 | public override void SetDefaults() { 38 | npc.townNPC = true; 39 | //必带项,没有为什么 40 | npc.friendly = true; 41 | //如果你想写敌对NPC也行 42 | npc.width = 22; 43 | //碰撞箱宽 44 | npc.height = 32; 45 | //碰撞箱高 46 | npc.aiStyle = 7; 47 | //必带项,如果你能自己写出城镇NPC的AI可以不带 48 | npc.damage = 10; 49 | //碰撞伤害,由于城镇NPC没有碰撞伤害所以可以忽略 50 | npc.defense = 75; 51 | //防御力 52 | npc.lifeMax = 2500; 53 | //生命值 54 | npc.HitSound = SoundID.NPCHit1; 55 | //受伤音效 56 | npc.DeathSound = SoundID.NPCDeath1; 57 | //死亡音效 58 | npc.knockBackResist = 0.3f; 59 | //抗击退性,数字越大抗性越低 60 | animationType = NPCID.Wizard; 61 | //如果你的NPC属于除投掷类NPC以外的其他攻击类型,请带上,值可以填对应NPC的ID 62 | } 63 | 64 | public override string TownNPCName() { 65 | switch (WorldGen.genRand.Next(3)) { 66 | case 0: 67 | return "杨永信"; 68 | case 1: 69 | return "杨永信"; 70 | default: 71 | return "杨永信"; 72 | } 73 | } 74 | 75 | public override void FindFrame(int frameHeight) { 76 | base.FindFrame(frameHeight); 77 | } 78 | 79 | 80 | //法师NPC专属:魔法光环的照明范围,最多起装饰作用(选带项) 81 | public override void TownNPCAttackMagic(ref float auraLightMultiplier) { 82 | auraLightMultiplier = 2f; 83 | } 84 | 85 | //NPC攻击一次后的间隔 86 | public override void TownNPCAttackCooldown(ref int cooldown, ref int randExtraCooldown) { 87 | cooldown = 4; 88 | randExtraCooldown = 4; 89 | //间隔的算法:实际间隔会大于或等于cooldown的值且总是小于cooldown+randExtraCooldown的总和(TR总整这些莫名其妙的玩意) 90 | } 91 | 92 | public override void TownNPCAttackStrength(ref int damage, ref float knockback) { 93 | damage = 50; 94 | knockback = 6f; 95 | } 96 | 97 | public override void TownNPCAttackProj(ref int projType, ref int attackDelay) { 98 | projType = ModContent.ProjectileType(); 99 | attackDelay = 30; 100 | } 101 | public override void TownNPCAttackProjSpeed(ref float multiplier, ref float gravityCorrection, ref float randomOffset) { 102 | multiplier = 10f; 103 | randomOffset = 2f; 104 | } 105 | 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /NPCs/SuperMage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/NPCs/SuperMage.png -------------------------------------------------------------------------------- /NPCs/WormBody.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using TemplateMod2.NPCs.StateMachine; 7 | using Terraria; 8 | using Terraria.ID; 9 | using Microsoft.Xna.Framework; 10 | 11 | namespace TemplateMod2.NPCs { 12 | public class WormBody : WormBodyNPC { 13 | public override void SetStaticDefaults() { 14 | DisplayName.SetDefault("虫虫-身体"); 15 | } 16 | public override void SetDefaults() { 17 | npc.npcSlots = 5f; 18 | npc.width = 44; 19 | npc.height = 44; 20 | npc.aiStyle = -1; 21 | npc.damage = 20; 22 | npc.defense = 300; 23 | npc.lifeMax = 1000000; 24 | npc.HitSound = SoundID.NPCHit1; 25 | npc.DeathSound = SoundID.NPCDeath10; 26 | npc.noGravity = true; 27 | npc.scale = 1.5f; 28 | npc.boss = true; 29 | npc.noTileCollide = true; 30 | for (int i = 0; i < npc.buffImmune.Length - 1; i++) { 31 | npc.buffImmune[i] = true; 32 | } 33 | npc.knockBackResist = 0f; 34 | npc.behindTiles = true; 35 | npc.value = Item.buyPrice(10, 0, 0, 0); 36 | npc.netAlways = true; 37 | npc.dontCountMe = true; 38 | } 39 | public override void AIBefore() { 40 | npc.TargetClosest(); 41 | var head = Main.npc[npc.realLife]; 42 | // 如果大哥gg了 43 | if (!head.active || head.life <= 0) { 44 | // 自己也必死无疑 45 | npc.life = 0; 46 | npc.HitEffect(0, 10.0); 47 | npc.checkDead(); 48 | npc.netUpdate = true; 49 | } 50 | // 否则我们就跟随前面的大哥走 51 | var front = Head.npc; 52 | Vector2 diff = front.Center - npc.Center; 53 | // 让npc移动到大哥后面height距离的位置 54 | diff.Normalize(); 55 | npc.Center = front.Center - diff * npc.height; 56 | // 面朝大哥 57 | npc.rotation = diff.ToRotation() + 1.57f; 58 | base.AIBefore(); 59 | } 60 | 61 | 62 | public override void Initialize() { 63 | RegisterState(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /NPCs/WormBody.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/NPCs/WormBody.png -------------------------------------------------------------------------------- /NPCs/WormFakeHead.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using TemplateMod2.NPCs.StateMachine; 8 | using Terraria; 9 | using Terraria.ID; 10 | using Terraria.ModLoader; 11 | 12 | namespace TemplateMod2.NPCs { 13 | public class WormFakeHead : WormBodyNPC { 14 | public override void SetStaticDefaults() { 15 | DisplayName.SetDefault("虫虫-头"); 16 | } 17 | public override void SetDefaults() { 18 | npc.npcSlots = 5f; 19 | npc.width = 44; 20 | npc.height = 44; 21 | npc.aiStyle = -1; 22 | npc.damage = 20; 23 | npc.defense = 70; 24 | npc.lifeMax = 1000000; 25 | npc.HitSound = SoundID.NPCHit1; 26 | npc.DeathSound = SoundID.NPCDeath10; 27 | npc.boss = true; 28 | npc.noGravity = true; 29 | npc.scale = 1.5f; 30 | npc.noTileCollide = true; 31 | for (int i = 0; i < npc.buffImmune.Length - 1; i++) { 32 | npc.buffImmune[i] = true; 33 | } 34 | npc.knockBackResist = 0f; 35 | npc.behindTiles = true; 36 | npc.value = Item.buyPrice(10, 0, 0, 0); 37 | npc.netAlways = true; 38 | npc.dontCountMe = true; 39 | } 40 | 41 | public override void AIBefore() { 42 | npc.rotation = npc.velocity.ToRotation() + 1.57f; 43 | base.AIBefore(); 44 | } 45 | 46 | private void MoveToPlayer() { 47 | Vector2 diff = Vector2.Normalize(TargetPlayer.Center - npc.Center); 48 | diff *= 50f; 49 | diff = (npc.velocity * 30 + diff) / 31f; 50 | float speedX = 0.3f; 51 | float speedY = 0.3f; 52 | npc.velocity.X += (npc.velocity.X < diff.X ? 1 : -1) * speedX; 53 | npc.velocity.Y += (npc.velocity.Y < diff.Y ? 1 : -1) * speedY; 54 | } 55 | 56 | private class AttackState : NPCState { 57 | public AttackState(SMNPC npc) : base(npc) { } 58 | public override void AI(SMNPC mnpc) { 59 | var wmnpc = mnpc as WormFakeHead; 60 | //唯一目标 61 | if (npc.target < 0 || npc.target == 255 || Main.player[npc.target].dead) { 62 | npc.TargetClosest(true); 63 | } 64 | wmnpc.MoveToPlayer(); 65 | } 66 | } 67 | 68 | private class SpawnState : NPCState { 69 | public SpawnState(SMNPC npc) : base(npc) { } 70 | public override void AI(SMNPC mnpc) { 71 | var worm = (WormBodyNPC)(mnpc); 72 | npc.realLife = worm.Head.npc.realLife; 73 | mnpc.SetState(); 74 | } 75 | } 76 | 77 | public class MergeState : NPCState { 78 | public MergeState(SMNPC npc) : base(npc) { } 79 | public override void AI(SMNPC mnpc) { 80 | var worm = (WormBodyNPC)(mnpc); 81 | npc.velocity = Vector2.Normalize(worm.Head.npc.Center - npc.Center) * 30f; 82 | if (npc.Hitbox.Intersects(worm.Head.npc.Hitbox)) { 83 | // 接上去 84 | worm.Tail.Head = worm.Head; 85 | // 把上一个尾巴咬掉 86 | worm.Head.Tail.npc.active = false; 87 | worm.Head.Tail = worm.Tail; 88 | // 自己也死掉 89 | npc.active = false; 90 | } 91 | } 92 | } 93 | 94 | public override void Initialize() { 95 | RegisterState(); 96 | RegisterState(); 97 | RegisterState(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /NPCs/WormFakeHead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/NPCs/WormFakeHead.png -------------------------------------------------------------------------------- /NPCs/WormHead.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using TemplateMod2.NPCs.StateMachine; 8 | using Terraria; 9 | using Terraria.ID; 10 | using Terraria.ModLoader; 11 | 12 | namespace TemplateMod2.NPCs { 13 | public abstract class WormBodyNPC : SMNPC { 14 | /// 15 | /// 它前面的NPC的ID 16 | /// 17 | public WormBodyNPC Head { 18 | get { 19 | return _getNPCByID((int)npc.ai[2]); 20 | } 21 | set { 22 | npc.ai[2] = value.npc.whoAmI; 23 | } 24 | } 25 | /// 26 | /// 它后面的NPC的ID 27 | /// 28 | public WormBodyNPC Tail { 29 | get { 30 | return _getNPCByID((int)npc.ai[3]); 31 | } 32 | set { 33 | npc.ai[3] = value.npc.whoAmI; 34 | } 35 | } 36 | 37 | private WormBodyNPC _getNPCByID(int id) { 38 | if (id < 0) return null; 39 | NPC n = Main.npc[id]; 40 | if (!n.active || n == null) return null; 41 | return (WormBodyNPC)n.modNPC; 42 | } 43 | } 44 | public class WormHead : WormBodyNPC { 45 | public override void SetStaticDefaults() { 46 | DisplayName.SetDefault("虫虫-头"); 47 | } 48 | public override void SetDefaults() { 49 | npc.npcSlots = 5f; 50 | npc.width = 44; 51 | npc.height = 44; 52 | npc.aiStyle = -1; 53 | npc.damage = 50; 54 | npc.defense = 100; 55 | npc.lifeMax = 1000000; 56 | npc.HitSound = SoundID.NPCHit1; 57 | npc.DeathSound = SoundID.NPCDeath10; 58 | npc.boss = true; 59 | npc.noGravity = true; 60 | npc.scale = 1.5f; 61 | npc.noTileCollide = true; 62 | for (int i = 0; i < npc.buffImmune.Length - 1; i++) { 63 | npc.buffImmune[i] = true; 64 | } 65 | npc.knockBackResist = 0f; 66 | npc.behindTiles = true; 67 | npc.value = Item.buyPrice(10, 0, 0, 0); 68 | npc.netAlways = true; 69 | } 70 | 71 | private static WormBodyNPC GetWorm(NPC npc) { 72 | return npc.modNPC as WormBodyNPC; 73 | } 74 | 75 | public override void AIBefore() { 76 | npc.rotation = npc.velocity.ToRotation() + 1.57f; 77 | base.AIBefore(); 78 | } 79 | 80 | private void MoveToPlayer() { 81 | Vector2 diff = Vector2.Normalize(TargetPlayer.Center - npc.Center); 82 | diff *= 35f; 83 | diff = (npc.velocity * 30 + diff) / 31f; 84 | float speedX = 0.5f; 85 | float speedY = 0.5f; 86 | npc.velocity.X += (npc.velocity.X < diff.X ? 1 : -1) * speedX; 87 | npc.velocity.Y += (npc.velocity.Y < diff.Y ? 1 : -1) * speedY; 88 | } 89 | 90 | public static WormBodyNPC SpawnWormPart(NPC npc, int type) { 91 | int id = NPC.NewNPC((int)npc.position.X, (int)npc.position.Y, type); 92 | NPC n = Main.npc[id]; 93 | n.whoAmI = id; 94 | n.realLife = npc.realLife; 95 | return n.modNPC as WormBodyNPC; 96 | } 97 | 98 | private class NormalAttack : NPCState { 99 | public NormalAttack(SMNPC npc) : base(npc) { } 100 | public override void AI(SMNPC mnpc) { 101 | var wmnpc = mnpc as WormHead; 102 | //唯一目标 103 | if (npc.target < 0 || npc.target == 255 || Main.player[npc.target].dead) { 104 | npc.TargetClosest(true); 105 | } 106 | if (mnpc.Timer >= 0 && mnpc.Timer < 80) { 107 | if (wmnpc.npc.velocity.Y < 20) 108 | wmnpc.npc.velocity.Y += 0.5f; 109 | } else { 110 | wmnpc.MoveToPlayer(); 111 | } 112 | mnpc.Timer++; 113 | if (mnpc.Timer == 400) { 114 | WormBodyNPC cur = mnpc as WormBodyNPC; 115 | for (int i = 0; i < 45; i++) { 116 | WormBodyNPC nxt = cur.Tail; 117 | if (i % 6 == 5) { 118 | var lastHead = cur.Head; 119 | var newHead = SpawnWormPart(cur.npc, ModContent.NPCType()); 120 | newHead.npc.velocity = Main.rand.NextVector2CircularEdge(1, 1) * 25f; 121 | newHead.Head = cur.Head; 122 | newHead.Tail = cur; 123 | cur.Head = newHead; 124 | 125 | var newTail = SpawnWormPart(cur.npc, ModContent.NPCType()); 126 | newTail.Head = lastHead; 127 | lastHead.Tail = newTail; 128 | } 129 | cur = nxt; 130 | } 131 | mnpc.Timer = 0; 132 | mnpc.SetState(); 133 | } 134 | } 135 | } 136 | 137 | private class SpawnState : NPCState { 138 | public SpawnState(SMNPC npc) : base(npc) { } 139 | public override void AI(SMNPC mnpc) { 140 | npc.realLife = npc.whoAmI; 141 | int length = 50; 142 | WormBodyNPC cur = (WormBodyNPC)mnpc; 143 | for (int i = 0; i < length; i++) { 144 | int type = (i == length - 1) ? ModContent.NPCType() : ModContent.NPCType(); 145 | var part = SpawnWormPart(npc, type); 146 | part.npc.realLife = npc.realLife; 147 | part.Head = cur; 148 | GetWorm(cur.npc).Tail = part; 149 | // 每个蠕虫身体维护双向链表,前驱和后继 150 | cur = part; 151 | } 152 | mnpc.SetState(); 153 | } 154 | } 155 | 156 | private class Split : NPCState { 157 | public Split(SMNPC npc) : base(npc) { } 158 | public override void AI(SMNPC mnpc) { 159 | 160 | } 161 | } 162 | 163 | private class Merge : NPCState { 164 | public Merge(SMNPC npc) : base(npc) { } 165 | public override void AI(SMNPC mnpc) { 166 | 167 | mnpc.SetState(); 168 | } 169 | } 170 | 171 | private class SplitAttack : NPCState { 172 | public SplitAttack(SMNPC npc) : base(npc) { } 173 | public override void AI(SMNPC mnpc) { 174 | var wmnpc = mnpc as WormHead; 175 | //唯一目标 176 | if (npc.target < 0 || npc.target == 255 || Main.player[npc.target].dead) { 177 | npc.TargetClosest(true); 178 | } 179 | if (mnpc.Timer >= 60 && mnpc.Timer < 80) { 180 | if (wmnpc.npc.velocity.Y < 20) 181 | wmnpc.npc.velocity.Y += 0.5f; 182 | } else { 183 | wmnpc.MoveToPlayer(); 184 | } 185 | mnpc.Timer++; 186 | if (mnpc.Timer == 360) { 187 | foreach (var n in Main.npc) { 188 | if (n.active && n.realLife == npc.whoAmI) { 189 | if (n.type == ModContent.NPCType()) { 190 | var wormHead = n.modNPC as WormBodyNPC; 191 | wormHead.SetState(); 192 | } 193 | } 194 | } 195 | } else if (mnpc.Timer > 360) { 196 | if (wmnpc.npc.velocity.Y < 20) 197 | wmnpc.npc.velocity.Y += 0.5f; 198 | foreach (var n in Main.npc) { 199 | if (n.active && n.realLife == npc.whoAmI && n.type == ModContent.NPCType()) { 200 | return; 201 | } 202 | } 203 | mnpc.Timer = 0; 204 | mnpc.SetState(); 205 | } 206 | } 207 | } 208 | 209 | public override void Initialize() { 210 | RegisterState(); 211 | RegisterState(); 212 | RegisterState(); 213 | RegisterState(); 214 | RegisterState(); 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /NPCs/WormHead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/NPCs/WormHead.png -------------------------------------------------------------------------------- /NPCs/WormTail.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using TemplateMod2.NPCs.StateMachine; 8 | using Terraria; 9 | using Terraria.ID; 10 | 11 | 12 | namespace TemplateMod2.NPCs { 13 | public class WormTail : WormBodyNPC { 14 | public override void SetStaticDefaults() { 15 | DisplayName.SetDefault("虫虫-尾巴"); 16 | } 17 | public override void SetDefaults() { 18 | npc.npcSlots = 5f; 19 | npc.width = 44; 20 | npc.height = 44; 21 | npc.aiStyle = -1; 22 | npc.damage = 100; 23 | npc.defense = 300; 24 | npc.lifeMax = 1000000; 25 | npc.HitSound = SoundID.NPCHit1; 26 | npc.DeathSound = SoundID.NPCDeath10; 27 | npc.noGravity = true; 28 | npc.scale = 1.5f; 29 | npc.boss = true; 30 | npc.noTileCollide = true; 31 | for (int i = 0; i < npc.buffImmune.Length - 1; i++) { 32 | npc.buffImmune[i] = true; 33 | } 34 | npc.knockBackResist = 0f; 35 | npc.behindTiles = true; 36 | npc.value = Item.buyPrice(10, 0, 0, 0); 37 | npc.netAlways = true; 38 | npc.dontCountMe = true; 39 | } 40 | public override void AIBefore() { 41 | npc.TargetClosest(); 42 | var head = Main.npc[npc.realLife]; 43 | // 如果大哥gg了 44 | if (!head.active || head.life <= 0) { 45 | // 自己也必死无疑 46 | npc.life = 0; 47 | npc.HitEffect(0, 10.0); 48 | npc.checkDead(); 49 | npc.netUpdate = true; 50 | } 51 | // 否则我们就跟随前面的大哥走 52 | var front = Head.npc; 53 | Vector2 diff = front.Center - npc.Center; 54 | // 让npc移动到大哥后面height距离的位置 55 | diff.Normalize(); 56 | npc.Center = front.Center - diff * npc.height; 57 | // 面朝大哥 58 | npc.rotation = diff.ToRotation() + 1.57f; 59 | base.AIBefore(); 60 | } 61 | public override void Initialize() { 62 | RegisterState(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /NPCs/WormTail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/NPCs/WormTail.png -------------------------------------------------------------------------------- /Projectiles/AvoidProj.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using System.Collections.Generic; 5 | using TemplateMod2.Projectiles; 6 | using TemplateMod2.Utils; 7 | using Terraria; 8 | using Terraria.ID; 9 | using Terraria.Localization; 10 | using Terraria.ModLoader; 11 | 12 | namespace TemplateMod2.Projectiles { 13 | public class AvoidProj : ModProjectile { 14 | private static int[,] dist = new int[102, 102]; 15 | private static int[,] prev = new int[102, 102]; 16 | private static int[] dx = { 1, -1, 0, 0, 1, -1, 1, -1 }; 17 | private static int[] dy = { 0, 0, 1, -1, 1, 1, -1, -1 }; 18 | private static List list = new List(); 19 | public override void SetStaticDefaults() { 20 | DisplayName.SetDefault("超级追踪弹幕"); 21 | } 22 | public override void SetDefaults() { 23 | projectile.width = 1; 24 | projectile.height = 1; 25 | projectile.aiStyle = -1; 26 | projectile.friendly = true; 27 | projectile.light = 0.1f; 28 | projectile.timeLeft = 60; 29 | projectile.ignoreWater = true; 30 | projectile.tileCollide = true; 31 | projectile.scale = 1f; 32 | projectile.alpha = 255; 33 | } 34 | 35 | bool valid(int x, int y) { 36 | return x >= 0 && x <= 100 && y >= 0 && y <= 100; 37 | } 38 | bool CanHitLine(Vector2 start, Vector2 end) { 39 | Vector2 unit = Vector2.Normalize(end - start); 40 | float r = unit.ToRotation(); 41 | float dis = (end - start).Length(); 42 | for (int i = 0; i < dis; i += 16) { 43 | Point pTile = (start + unit * i).ToTileCoordinates(); 44 | for (int j = -1; j <= 1; j++) { 45 | for (int k = -1; k <= 1; k++) { 46 | Point neg = new Point(pTile.X + j, pTile.Y + k); 47 | if (!valid(neg.X, neg.Y)) continue; 48 | Tile tile = Main.tile[neg.X, neg.Y]; 49 | if (tile.active() && Main.tileSolid[tile.type] && !Main.tileSolidTop[tile.type]) return false; 50 | } 51 | } 52 | } 53 | return true; 54 | } 55 | 56 | bool check(Point end, int x, int y) { 57 | if (x == end.X && y == end.Y) return true; 58 | Tile tile = Main.tile[x, y]; 59 | if (tile.active() && Main.tileSolid[tile.type] && !Main.tileSolidTop[tile.type]) { 60 | return false; 61 | } 62 | return true; 63 | //if (x == end.X && y == end.Y) return true; 64 | //for (int i = 0; i < 8; i++) { 65 | // if (x + dx[i] >= 0 && x + dx[i] < Main.maxTilesX && y + dy[i] >= 0 && y + dy[i] < Main.maxTilesY) { 66 | // Tile tile = Main.tile[x + dx[i], y + dy[i]]; 67 | // if (tile.active() && Main.tileSolid[tile.type] && !Main.tileSolidTop[tile.type]) { 68 | // return false; 69 | // } 70 | // } 71 | //} 72 | return true; 73 | } 74 | private struct Node : IComparable { 75 | public int x, y, s; 76 | public double d; 77 | public Node(int x, int y, int s, double d) { 78 | this.x = x; 79 | this.y = y; 80 | this.s = s; 81 | this.d = d; 82 | } 83 | public int CompareTo(object obj) { 84 | Node other = (Node)obj; 85 | return (s + d).CompareTo(other.s + other.d); 86 | } 87 | } 88 | private double cal(int x, int y, Point b) { 89 | return Math.Abs(x - b.X) + Math.Abs(y - b.Y); 90 | return Math.Sqrt((x - b.X) * (x - b.X) + (y - b.Y) * (y - b.Y)); 91 | } 92 | 93 | void search() { 94 | PriorityQueue Q = new PriorityQueue(10000); 95 | for (int i = 0; i < 100; i++) 96 | for (int j = 0; j < 100; j++) { 97 | dist[i, j] = 0x3f3f3f3f; 98 | prev[i, j] = 0; 99 | } 100 | Point cur = projectile.Center.ToTileCoordinates(); 101 | Point end = target.Center.ToTileCoordinates(); 102 | dist[50, 50] = 0; 103 | Q.Push(new Node(50, 50, 0, cal(cur.X, cur.Y, end))); 104 | while (!Q.Empty) { 105 | Node x = Q.Pop(); 106 | if (x.x - 50 + cur.X == end.X && x.y - 50 + cur.Y == end.Y) break; 107 | for (int i = 0; i < 4; i++) { 108 | int nx = x.x + dx[i], ny = x.y + dy[i]; 109 | if (!valid(nx, ny)) continue; 110 | if (!check(end, nx + cur.X - 50, ny + cur.Y - 50)) continue; 111 | if (dist[x.x, x.y] + 1 < dist[nx, ny]) { 112 | dist[nx, ny] = dist[x.x, x.y] + 1; 113 | prev[nx, ny] |= x.x; 114 | prev[nx, ny] |= x.y << 10; 115 | Q.Push(new Node(nx, ny, dist[nx, ny], cal(nx + cur.X - 50, ny + cur.Y - 50, end))); 116 | } 117 | } 118 | } 119 | list.Clear(); 120 | Point end1 = new Point(end.X - cur.X + 50, end.Y - cur.Y + 50); 121 | int x1 = prev[end1.X, end1.Y] & 1023; 122 | int y1 = ((prev[end1.X, end1.Y] >> 10) & 1023); 123 | //Main.NewText($"Complete! {end1} : {dist[end1.X, end1.Y]} -> {x1}, {y1}"); 124 | if (dist[end1.X, end1.Y] == 0x3f3f3f3f) 125 | return; 126 | while (true) { 127 | Vector2 pos = new Vector2((cur.X + end1.X - 50) * 16 + 8, (cur.Y + end1.Y - 50) * 16 + 8); 128 | list.Add(pos); 129 | int x = prev[end1.X, end1.Y] & 1023; 130 | int y = ((prev[end1.X, end1.Y] >> 10) & 1023); 131 | if (x == end1.X && y == end1.Y) break; 132 | if (x == 50 && y == 50) { 133 | break; 134 | } 135 | end1 = new Point(x, y); 136 | } 137 | Vector2 end2 = new Vector2((cur.X + end1.X - 50) * 16 + 8, (cur.Y + end1.Y - 50) * 16 + 8); 138 | projectile.velocity = Vector2.Normalize(end2 - projectile.Center) * 10f; 139 | if (!Collision.CanHitLine(projectile.Center, 0, 0, projectile.Center + Vector2.Normalize(end2 - projectile.Center) * 24f, 0, 0)) { 140 | for (float r = 0; r < MathHelper.TwoPi; r += MathHelper.Pi / 2f) { 141 | Vector2 unit = (projectile.velocity.ToRotation() + r).ToRotationVector2(); 142 | if (Collision.CanHitLine(projectile.Center, 0, 0, projectile.Center + unit * 24, 0, 0)) { 143 | projectile.velocity = (projectile.velocity * 2 + unit * 16) / 3f; 144 | return; 145 | } 146 | } 147 | } 148 | } 149 | private NPC target; 150 | public override void AI() { 151 | // 火焰粒子特效 152 | Dust dust = Dust.NewDustDirect(projectile.position, projectile.width, projectile.height 153 | , MyDustId.Fire, 0f, 0f, 100, default, 3f); 154 | // 粒子特效不受重力 155 | dust.noGravity = true; 156 | dust.velocity *= 0; 157 | dust.position = projectile.Center; 158 | float maxDis = 800f; 159 | target = null; 160 | //projectile.velocity *= 0f; 161 | // 选取最近npc,如果target是null说明没有临近的敌人 162 | foreach (var npc in Main.npc) { 163 | if (npc.active && !npc.friendly && npc.value > 0 && !npc.dontTakeDamage) { 164 | float dis = Vector2.Distance(npc.Center, projectile.Center); 165 | if (dis < maxDis) { 166 | maxDis = dis; 167 | target = npc; 168 | } 169 | } 170 | } 171 | if (target != null) { 172 | search(); 173 | } 174 | } 175 | public override void PostDraw(SpriteBatch spriteBatch, Color lightColor) { 176 | if (target != null) 177 | Drawing.DrawLine(spriteBatch, projectile.Center - Main.screenPosition, target.Center - Main.screenPosition, 4, 2f, Color.White); 178 | foreach (var pos in list) { 179 | spriteBatch.Draw(Main.magicPixel, pos - Main.screenPosition, 180 | new Rectangle(0, 0, 1, 1), Color.White, 0f, new Vector2(0.5f, 0.5f), new Vector2(8, 8), SpriteEffects.None, 0f); 181 | } 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Projectiles/AvoidProj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Projectiles/AvoidProj.png -------------------------------------------------------------------------------- /Projectiles/GravProj.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using TemplateMod2.Projectiles; 5 | using TemplateMod2.Utils; 6 | using Terraria; 7 | using Terraria.ID; 8 | using Terraria.Localization; 9 | using Terraria.ModLoader; 10 | 11 | namespace TemplateMod2.Projectiles { 12 | public class GravProj : ModProjectile { 13 | public override void SetStaticDefaults() { 14 | DisplayName.SetDefault("重力弹幕"); 15 | } 16 | public override void SetDefaults() { 17 | projectile.width = 16; 18 | projectile.height = 16; 19 | projectile.aiStyle = -1; 20 | projectile.friendly = true; 21 | projectile.light = 0.1f; 22 | projectile.timeLeft = 400; 23 | projectile.ignoreWater = true; 24 | projectile.tileCollide = true; 25 | projectile.scale = 1f; 26 | projectile.alpha = 255; 27 | projectile.extraUpdates = 2; 28 | } 29 | 30 | bool valid(int x, int y) { 31 | return !(x < 0 || x >= Main.maxTilesX || y < 0 || y >= Main.maxTilesY); 32 | } 33 | bool CanHitLine(Vector2 start, Vector2 end) { 34 | Vector2 unit = Vector2.Normalize(end - start); 35 | float dis = (end - start).Length(); 36 | for (int i = 0; i < dis; i += 16) { 37 | Point pTile = (start + unit * i).ToTileCoordinates(); 38 | for (int j = -1; j <= 1; j++) { 39 | for (int k = -1; k <= 1; k++) { 40 | Point neg = new Point(pTile.X + j, pTile.Y + k); 41 | if (!valid(neg.X, neg.Y)) continue; 42 | Tile tile = Main.tile[neg.X, neg.Y]; 43 | if (tile.active() && Main.tileSolid[tile.type] && !Main.tileSolidTop[tile.type]) return false; 44 | } 45 | } 46 | } 47 | return true; 48 | } 49 | 50 | bool check(NPC target, float rot, float r, out float closest) { 51 | Vector2 unit = (rot + r).ToRotationVector2(); 52 | closest = 1.0f / 0.0f; 53 | for (int i = 0; i < 100; i += 8) { 54 | Vector2 cur = projectile.Center + unit * i; 55 | closest = Math.Min(closest, Vector2.Distance(cur, target.Center)); 56 | } 57 | return CanHitLine(projectile.Center, projectile.Center + unit * 100); 58 | } 59 | private NPC target; 60 | public override void AI() { 61 | // 火焰粒子特效 62 | Dust dust = Dust.NewDustDirect(projectile.position, projectile.width, projectile.height 63 | , MyDustId.Fire, 0f, 0f, 100, default(Color), 3f); 64 | // 粒子特效不受重力 65 | dust.noGravity = true; 66 | dust.velocity *= 0; 67 | dust.position = projectile.Center; 68 | float maxDis = 1000f; 69 | target = null; 70 | // 选取最近npc,如果target是null说明没有临近的敌人 71 | foreach (var npc in Main.npc) { 72 | if (npc.active && !npc.friendly && npc.value > 0 && !npc.dontTakeDamage) { 73 | float dis = Vector2.Distance(npc.Center, projectile.Center); 74 | if (dis < maxDis) { 75 | maxDis = dis; 76 | target = npc; 77 | } 78 | } 79 | } 80 | //if (!Collision.CanHit(projectile.position, projectile.width, projectile.height, target.position, target.width, target.height)) { 81 | if (target != null) { 82 | Vector2 aim = target.Center - projectile.Center; 83 | float rot = aim.ToRotation(); 84 | if (!CanHitLine(projectile.Center, target.Center)) { 85 | float final = 0f, minn = float.PositiveInfinity; 86 | for (float r = -MathHelper.Pi / 2; r < MathHelper.Pi / 2; r += MathHelper.Pi / 10f) { 87 | float d; 88 | if (check(target, rot, r, out d)) { 89 | if (d < minn) { 90 | minn = d; 91 | final = r; 92 | } 93 | } 94 | } 95 | if (minn != float.PositiveInfinity) 96 | projectile.velocity = (rot + final).ToRotationVector2() * 10f; 97 | } else { 98 | projectile.velocity = Vector2.Normalize(aim) * 10f; 99 | } 100 | } 101 | } 102 | public override void PostDraw(SpriteBatch spriteBatch, Color lightColor) { 103 | if (target != null) 104 | Drawing.DrawLine(spriteBatch, projectile.Center - Main.screenPosition, target.Center - Main.screenPosition, 4, 2f, Color.White); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Projectiles/GravProj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Projectiles/GravProj.png -------------------------------------------------------------------------------- /Projectiles/LightTreePro.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using TemplateMod2.Utils; 7 | using Terraria; 8 | using Terraria.ID; 9 | using Terraria.ModLoader; 10 | 11 | namespace TemplateMod2.Projectiles { 12 | public class LightTreePro : ModProjectile { 13 | public override void SetStaticDefaults() { 14 | DisplayName.SetDefault("神秘弹幕"); 15 | } 16 | public override void SetDefaults() { 17 | projectile.width = 1; 18 | projectile.height = 1; 19 | projectile.aiStyle = -1; 20 | projectile.friendly = true; 21 | projectile.light = 0.1f; 22 | projectile.timeLeft = 30; 23 | projectile.ignoreWater = true; 24 | projectile.tileCollide = false; 25 | projectile.penetrate = -1; 26 | } 27 | 28 | public override bool ShouldUpdatePosition() { 29 | return false; 30 | } 31 | private LightTree tree; 32 | public override void AI() { 33 | if (projectile.ai[0] % 6 == 0) { 34 | tree = new LightTree(Main.rand); 35 | float maxDis = 1000f; 36 | NPC target = null; 37 | foreach (var npc in Main.npc) { 38 | if (npc.active && !npc.friendly && npc.type != NPCID.TargetDummy && !npc.dontTakeDamage) { 39 | float dis = Vector2.Distance(npc.Center, projectile.Center); 40 | if (dis < maxDis) { 41 | maxDis = dis; 42 | target = npc; 43 | } 44 | } 45 | } 46 | Vector2 pos = projectile.Center + projectile.velocity * 100f; 47 | if (target != null) 48 | pos = target.Center; 49 | tree.Generate(projectile.Center, projectile.velocity, pos); 50 | } 51 | projectile.ai[0]++; 52 | } 53 | public override bool? Colliding(Rectangle projHitbox, Rectangle targetHitbox) { 54 | return tree.Check(targetHitbox); 55 | } 56 | 57 | 58 | public override void OnHitNPC(NPC target, int damage, float knockback, bool crit) { 59 | base.OnHitNPC(target, damage, knockback, crit); 60 | target.immune[projectile.owner] = 0; 61 | for (int i = 0; i < 2; i++) { 62 | var dust = Dust.NewDustDirect(target.position, target.width, target.height, MyDustId.ElectricCyan, 0, 0, 100, Color.White, 0.3f); 63 | dust.noGravity = true; 64 | dust.velocity *= 1.5f; 65 | } 66 | } 67 | 68 | public override void PostDraw(SpriteBatch spriteBatch, Color lightColor) { 69 | tree.Draw(spriteBatch, projectile.Center - Main.screenPosition, projectile.velocity); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Projectiles/LightTreePro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Projectiles/LightTreePro.png -------------------------------------------------------------------------------- /Projectiles/ProjProj.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using TemplateMod2.Projectiles; 5 | using TemplateMod2.Projectiles.StateMachine; 6 | using TemplateMod2.Utils; 7 | using Terraria; 8 | using Terraria.ID; 9 | using Terraria.Localization; 10 | using Terraria.ModLoader; 11 | 12 | namespace TemplateMod2.Projectiles { 13 | public class ProjProj : SMProjectile { 14 | 15 | public override void SetStaticDefaults() { 16 | DisplayName.SetDefault("回力标"); 17 | } 18 | public override void SetDefaults() { 19 | projectile.width = 6; 20 | projectile.height = 6; 21 | projectile.aiStyle = -1; 22 | projectile.friendly = true; 23 | projectile.light = 0.1f; 24 | projectile.timeLeft = 600; 25 | projectile.ignoreWater = true; 26 | projectile.tileCollide = false; 27 | projectile.penetrate = -1; 28 | projectile.scale = 1.25f; 29 | } 30 | 31 | public override Color? GetAlpha(Color lightColor) { 32 | return Color.White; 33 | } 34 | 35 | public override void OnHitNPC(NPC target, int damage, float knockback, bool crit) { 36 | target.immune[projectile.owner] = 4; 37 | } 38 | 39 | public override bool PreDraw(SpriteBatch spriteBatch, Color lightColor) { 40 | var tex = Main.projectileTexture[projectile.type]; 41 | spriteBatch.Draw(tex, 42 | projectile.Center - Main.screenPosition, tex.Frame(), 43 | Color.White, projectile.rotation, 44 | tex.Size() * 0.5f, 45 | projectile.scale, SpriteEffects.None, 0f); 46 | // 返回false阻止原版的绘制 47 | return false; 48 | } 49 | 50 | 51 | private class ForwardState : ProjState { 52 | public override void AI(SMProjectile proj) { 53 | var projectile = proj.projectile; 54 | projectile.rotation += 0.05f * projectile.velocity.Length(); 55 | proj.Timer++; 56 | float factor = proj.Timer / 90f; 57 | factor *= factor; 58 | projectile.velocity = Vector2.Normalize(projectile.velocity) * 9f * (1.0f - factor); 59 | if (proj.Timer >= 90) proj.SetState(); 60 | } 61 | } 62 | 63 | private class ChaseState : ProjState { 64 | public override void AI(SMProjectile proj) { 65 | var projectile = proj.projectile; 66 | proj.Timer++; 67 | projectile.rotation += 0.05f * projectile.velocity.Length(); 68 | var target = ProjUtils.FindNearestEnemy(projectile.Center, 1000, (npc) => npc.value > 1 || npc.damage > 1); 69 | if (target != null) { 70 | var unit = Vector2.Normalize(target.Center - projectile.Center); 71 | projectile.velocity = (projectile.velocity * 15f + unit * 10f) / 16f; 72 | } 73 | if (proj.Timer >= 300) proj.SetState(); 74 | } 75 | } 76 | 77 | 78 | private class BackwardState : ProjState { 79 | public override void AI(SMProjectile proj) { 80 | var projectile = proj.projectile; 81 | projectile.rotation -= 0.05f * projectile.velocity.Length(); 82 | Player owner = Main.player[projectile.owner]; 83 | projectile.velocity = Vector2.Normalize(owner.Center - projectile.Center) * 9f; 84 | if (projectile.Hitbox.Intersects(owner.Hitbox)) { 85 | projectile.Kill(); 86 | } 87 | } 88 | } 89 | 90 | public override void AIBefore() { 91 | 92 | } 93 | 94 | public override void Initialize() { 95 | RegisterState(new ForwardState()); 96 | RegisterState(new ChaseState()); 97 | RegisterState(new BackwardState()); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Projectiles/ProjProj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Projectiles/ProjProj.png -------------------------------------------------------------------------------- /Projectiles/ProjUtils.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Terraria; 8 | 9 | namespace TemplateMod2.Projectiles { 10 | public static class ProjUtils { 11 | public static NPC FindNearestEnemy(Vector2 pos, float maxDis, Func cond = null) { 12 | NPC target = null; 13 | foreach (var npc in Main.npc) { 14 | if (npc.active && !npc.friendly && cond(npc)) { 15 | float dis = Vector2.Distance(pos, npc.Center); 16 | if (dis < maxDis) { 17 | maxDis = dis; 18 | target = npc; 19 | } 20 | } 21 | } 22 | return target; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Projectiles/Raze/MagicLine.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using System.Collections.Generic; 5 | using TemplateMod2.Projectiles; 6 | using TemplateMod2.Utils; 7 | using Terraria; 8 | using Terraria.ID; 9 | using Terraria.Localization; 10 | using Terraria.ModLoader; 11 | 12 | namespace TemplateMod2.Projectiles.Raze { 13 | public class MagicLine : ModProjectile { 14 | 15 | public override void SetStaticDefaults() { 16 | DisplayName.SetDefault("超级追踪弹幕"); 17 | } 18 | public override void SetDefaults() { 19 | projectile.width = 1; 20 | projectile.height = 1; 21 | projectile.aiStyle = -1; 22 | projectile.friendly = true; 23 | projectile.light = 0.1f; 24 | projectile.timeLeft = 60; 25 | projectile.ignoreWater = true; 26 | projectile.tileCollide = true; 27 | projectile.scale = 1f; 28 | projectile.alpha = 255; 29 | projectile.extraUpdates = 100; 30 | } 31 | 32 | public override void AI() { 33 | Dust dust = Dust.NewDustDirect(projectile.position, projectile.width, projectile.height 34 | , MyDustId.RedTrans, 0f, 0f, 100, default, 2f); 35 | dust.noGravity = true; 36 | dust.velocity *= 0; 37 | dust.position = projectile.Center; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Projectiles/Raze/MagicLine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Projectiles/Raze/MagicLine.png -------------------------------------------------------------------------------- /Projectiles/StateMachine/ProjState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TemplateMod2.Projectiles.StateMachine { 8 | public abstract class ProjState { 9 | public abstract void AI(SMProjectile proj); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Projectiles/StateMachine/SMProjectile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Terraria.ModLoader; 7 | 8 | namespace TemplateMod2.Projectiles.StateMachine { 9 | /// 10 | /// 基于状态机的ModProjectile类,一定要先在Initialize里注册弹幕的状态才能使用哦 11 | /// 12 | public abstract class SMProjectile : ModProjectile { 13 | public ProjState currentState => projStates[State - 1]; 14 | private List projStates = new List(); 15 | private Dictionary stateDict = new Dictionary(); 16 | private int State { 17 | get { return (int)projectile.ai[0]; } 18 | set { projectile.ai[0] = (int)value; } 19 | } 20 | public int Timer { 21 | get { return (int)projectile.ai[1]; } 22 | set { projectile.ai[1] = value; } 23 | } 24 | /// 25 | /// 把当前状态变为指定的弹幕状态实例 26 | /// 27 | /// 注册过的类名 28 | public void SetState() where T : ProjState { 29 | var name = typeof(T).FullName; 30 | if (!stateDict.ContainsKey(name)) throw new ArgumentException("这个状态并不存在"); 31 | State = stateDict[name]; 32 | } 33 | public bool AtState() where T : ProjState { 34 | var name = typeof(T).FullName; 35 | if (!stateDict.ContainsKey(name)) throw new ArgumentException("这个状态并不存在"); 36 | return State == stateDict[name]; 37 | } 38 | /// 39 | /// 注册状态 40 | /// 41 | /// 需要注册的 42 | /// 需要注册的类的实例 43 | protected void RegisterState(T state) where T : ProjState { 44 | var name = typeof(T).FullName; 45 | if (stateDict.ContainsKey(name)) throw new ArgumentException("这个状态已经注册过了"); 46 | projStates.Add(state); 47 | stateDict.Add(name, projStates.Count); 48 | } 49 | 50 | /// 51 | /// 初始化函数,用于注册弹幕状态 52 | /// 53 | public abstract void Initialize(); 54 | /// 55 | /// 我把AI函数封住了,这样在子类无法重写AI函数,只能用before和after函数 56 | /// 57 | public sealed override void AI() { 58 | if (State == 0) { 59 | Initialize(); 60 | State = 1; 61 | } 62 | AIBefore(); 63 | currentState.AI(this); 64 | AIAfter(); 65 | } 66 | /// 67 | /// 在状态机执行之前要执行的代码 68 | /// 69 | public virtual void AIAfter() { } 70 | /// 71 | /// 在状态机执行之后要执行的代码 72 | /// 73 | public virtual void AIBefore() { } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Projectiles/SuperGCD.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using TemplateMod2.Projectiles; 5 | using TemplateMod2.Utils; 6 | using Terraria; 7 | using Terraria.ID; 8 | using Terraria.Localization; 9 | using Terraria.ModLoader; 10 | 11 | namespace TemplateMod2.Projectiles { 12 | public class SuperGCD : ModProjectile { 13 | public override void SetStaticDefaults() { 14 | DisplayName.SetDefault("阴 阳 怪 塔"); 15 | } 16 | public override void SetDefaults() { 17 | projectile.width = 6; 18 | projectile.height = 6; 19 | projectile.aiStyle = -1; 20 | projectile.friendly = true; 21 | projectile.light = 0.1f; 22 | projectile.timeLeft = 600; 23 | projectile.ignoreWater = true; 24 | projectile.tileCollide = false; 25 | projectile.penetrate = -1; 26 | projectile.scale = 1f; 27 | projectile.alpha = 255; 28 | projectile.extraUpdates = 0; 29 | } 30 | 31 | public int Timer1 { 32 | get { 33 | return (int)projectile.ai[0]; 34 | } 35 | set { 36 | projectile.ai[0] = value; 37 | } 38 | } 39 | 40 | public int Timer2 { 41 | get { 42 | return (int)projectile.ai[1]; 43 | } 44 | set { 45 | projectile.ai[1] = value; 46 | } 47 | } 48 | public override void AI() { 49 | Main.dayTime = false; 50 | Main.time = 0; 51 | projectile.velocity *= 0f; 52 | for (float r = 6.28f; r > 0; r -= MathHelper.TwoPi / 10f) { 53 | float r2 = (float)Math.Cos(projectile.ai[0]); 54 | for (int i = -1; i <= 1; i += 2) { 55 | Vector2 pos = projectile.Center + 56 | new Vector2((float)Math.Cos(r + r2), (float)Math.Sin(r + r2)) * r * 10f * i; 57 | Dust dust = Dust.NewDustDirect(projectile.position, projectile.width, projectile.height 58 | , i < 0 ? MyDustId.RedTorch : MyDustId.DemonTorch, 0f, 0f, 100, default(Color), 3f); 59 | dust.noGravity = true; 60 | dust.velocity *= 0; 61 | dust.position = pos; 62 | } 63 | } 64 | Timer1++; 65 | float factor = Math.Min(1.0f, Timer1 / 300f); 66 | if (factor < 0.2f) return; 67 | Timer2++; 68 | int shootCD = (int)(10 + (1 - factor) * 20); 69 | if (Timer2 >= shootCD) { 70 | float maxDis = 1000f; 71 | NPC target = null; 72 | // 选取最近npc,如果target是null说明没有临近的敌人 73 | foreach (var npc in Main.npc) { 74 | if (npc.active && !npc.friendly && npc.value > 0 && !npc.dontTakeDamage) { 75 | float dis = Vector2.Distance(npc.Center, projectile.Center); 76 | if (dis < maxDis) { 77 | maxDis = dis; 78 | target = npc; 79 | } 80 | } 81 | } 82 | if (target != null) { 83 | projectile.ai[1] = 0; 84 | Projectile.NewProjectile(projectile.Center, Vector2.Normalize(target.Center - projectile.Center) * 13f, 85 | ModContent.ProjectileType(), 100, 5f, projectile.owner, 0, Main.rand.Next(2)); 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Projectiles/SuperGCD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Projectiles/SuperGCD.png -------------------------------------------------------------------------------- /Projectiles/YinYangTower.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using TemplateMod2.Projectiles; 5 | using TemplateMod2.Utils; 6 | using Terraria; 7 | using Terraria.ID; 8 | using Terraria.Localization; 9 | using Terraria.ModLoader; 10 | 11 | namespace TemplateMod2.Projectiles { 12 | public class YinYangTower : ModProjectile { 13 | public override void SetStaticDefaults() { 14 | DisplayName.SetDefault("阴 阳 怪 塔"); 15 | } 16 | public override void SetDefaults() { 17 | projectile.width = 6; 18 | projectile.height = 6; 19 | projectile.aiStyle = -1; 20 | projectile.friendly = true; 21 | projectile.light = 0.1f; 22 | projectile.timeLeft = 600; 23 | projectile.ignoreWater = true; 24 | projectile.tileCollide = false; 25 | projectile.penetrate = -1; 26 | projectile.scale = 1f; 27 | projectile.alpha = 255; 28 | projectile.extraUpdates = 0; 29 | } 30 | 31 | public int Timer1 { 32 | get { 33 | return (int)projectile.ai[0]; 34 | } 35 | set { 36 | projectile.ai[0] = value; 37 | } 38 | } 39 | 40 | public int Timer2 { 41 | get { 42 | return (int)projectile.ai[1]; 43 | } 44 | set { 45 | projectile.ai[1] = value; 46 | } 47 | } 48 | public override void AI() { 49 | Main.dayTime = false; 50 | Main.time = 0; 51 | projectile.velocity *= 0f; 52 | for (float r = 6.28f; r > 0; r -= MathHelper.TwoPi / 10f) { 53 | float r2 = (float)Math.Cos(projectile.ai[0]); 54 | for (int i = -1; i <= 1; i += 2) { 55 | Vector2 pos = projectile.Center + 56 | new Vector2((float)Math.Cos(r + r2), (float)Math.Sin(r + r2)) * r * 10f * i; 57 | Dust dust = Dust.NewDustDirect(projectile.position, projectile.width, projectile.height 58 | , i < 0 ? MyDustId.RedTorch : MyDustId.DemonTorch, 0f, 0f, 100, default(Color), 3f); 59 | dust.noGravity = true; 60 | dust.velocity *= 0; 61 | dust.position = pos; 62 | } 63 | } 64 | Timer1++; 65 | float factor = Math.Min(1.0f, Timer1 / 300f); 66 | if (factor < 0.2f) return; 67 | Timer2++; 68 | int shootCD = (int)(10 + (1 - factor) * 20); 69 | if (Timer2 >= shootCD) { 70 | float maxDis = 1000f; 71 | NPC target = null; 72 | // 选取最近npc,如果target是null说明没有临近的敌人 73 | foreach (var npc in Main.npc) { 74 | if (npc.active && !npc.friendly && npc.value > 0 && !npc.dontTakeDamage) { 75 | float dis = Vector2.Distance(npc.Center, projectile.Center); 76 | if (dis < maxDis) { 77 | maxDis = dis; 78 | target = npc; 79 | } 80 | } 81 | } 82 | if (target != null) { 83 | projectile.ai[1] = 0; 84 | Projectile.NewProjectile(projectile.Center, Vector2.Normalize(target.Center - projectile.Center) * 13f, 85 | ModContent.ProjectileType(), 100, 5f, projectile.owner, 0, Main.rand.Next(2)); 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Projectiles/YinYangTower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/Projectiles/YinYangTower.png -------------------------------------------------------------------------------- /Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Terraria": { 4 | "commandName": "Executable", 5 | "executablePath": "$(tMLPath)", 6 | "workingDirectory": "$(TerrariaSteamPath)" 7 | }, 8 | "TerrariaServer": { 9 | "commandName": "Executable", 10 | "executablePath": "$(tMLServerPath)", 11 | "workingDirectory": "$(TerrariaSteamPath)" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /Sky/TestScreenShader.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using ReLogic.Graphics; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using Terraria; 9 | using Terraria.DataStructures; 10 | using Terraria.GameContent.UI; 11 | using Terraria.Graphics.Effects; 12 | using Terraria.Graphics.Shaders; 13 | using Terraria.ID; 14 | using Terraria.Localization; 15 | using Terraria.ModLoader; 16 | using Terraria.UI; 17 | 18 | namespace TemplateMod2 { 19 | public class TestScreenShaderData : ScreenShaderData { 20 | public TestScreenShaderData(string passName) : base(passName) { 21 | } 22 | public TestScreenShaderData(Ref shader, string passName) : base(shader, passName) { 23 | } 24 | public override void Apply() { 25 | base.Apply(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sky/TestSky.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using ReLogic.Graphics; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using Terraria; 9 | using Terraria.DataStructures; 10 | using Terraria.GameContent.UI; 11 | using Terraria.Graphics.Effects; 12 | using Terraria.Graphics.Shaders; 13 | using Terraria.ID; 14 | using Terraria.Localization; 15 | using Terraria.ModLoader; 16 | using Terraria.UI; 17 | 18 | namespace TemplateMod2 { 19 | public class TestSky : CustomSky { 20 | private bool _isActive; 21 | public override void Activate(Vector2 position, params object[] args) { 22 | 23 | 24 | _isActive = true; 25 | } 26 | 27 | public override void Deactivate(params object[] args) { 28 | _isActive = false; 29 | } 30 | 31 | public override void Draw(SpriteBatch spriteBatch, float minDepth, float maxDepth) { 32 | } 33 | 34 | public override bool IsActive() { 35 | return _isActive; 36 | } 37 | 38 | public override void Reset() { 39 | _isActive = false; 40 | } 41 | 42 | public override void Update(GameTime gameTime) { 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /TemplateGlobaProjectile.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Terraria; 9 | using Terraria.Localization; 10 | using Terraria.ModLoader; 11 | 12 | namespace TemplateMod2 { 13 | public class TemplateGlobaProjectile : GlobalProjectile { 14 | public override void PostDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor) { 15 | if (projectile.velocity.Length() < 0.1) return; 16 | //spriteBatch.Draw(ModContent.GetTexture("TemplateMod2/Images/Arrow1"), projectile.Center - Main.screenPosition, 17 | // null, Color.White, projectile.velocity.ToRotation(), new Vector2(0, 11), new Vector2((float)Math.Sqrt(projectile.velocity.Length()), 0.5f), SpriteEffects.None, 0f); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /TemplateGlobalItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Terraria; 7 | using Terraria.Localization; 8 | using Terraria.ModLoader; 9 | 10 | namespace TemplateMod2 { 11 | public class TemplateGlobalItem : GlobalItem { 12 | //public override void UpdateArmorSet(Player player, string set) { 13 | // Main.NewText(set); 14 | // if (set.Equals(Language.GetTextValue("ArmorSetBonus.Wood"))) { 15 | // player.statDefense += 100; 16 | // } 17 | // base.UpdateArmorSet(player, set); 18 | //} 19 | 20 | //public override string IsArmorSet(Item head, Item body, Item legs) { 21 | // return "?"; 22 | //} 23 | 24 | public override void UpdateEquip(Item item, Player player) { 25 | //if ((player.head == 52 && player.body == 32 && player.legs == 31) || (player.head == 53 && player.body == 33 && player.legs == 32) || (player.head == 54 && player.body == 34 && player.legs == 33) || (player.head == 55 && player.body == 35 && player.legs == 34) || (player.head == 70 && player.body == 46 && player.legs == 42) || (player.head == 71 && player.body == 47 && player.legs == 43) || (player.head == 166 && player.body == 173 && player.legs == 108) || (player.head == 167 && player.body == 174 && player.legs == 109)) { 26 | // player.setBonus = "防御力增加1000倍"; 27 | // player.statDefense *= 1000; 28 | //} 29 | base.UpdateEquip(item, player); 30 | } 31 | 32 | public override void UpdateAccessory(Item item, Player player, bool hideVisual) { 33 | //Main.NewText("?"); 34 | base.UpdateAccessory(item, player, hideVisual); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TemplateGlobalNPC.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Terraria; 9 | using Terraria.Localization; 10 | using Terraria.ModLoader; 11 | 12 | namespace TemplateMod2 { 13 | public class TemplateGlobalNPC : GlobalNPC { 14 | public override void PostAI(NPC npc) { 15 | //npc.position += npc.velocity * 10; 16 | } 17 | public override void PostDraw(NPC npc, SpriteBatch spriteBatch, Color drawColor) { 18 | spriteBatch.End(); 19 | spriteBatch.Begin(); 20 | } 21 | public override bool PreDraw(NPC npc, SpriteBatch spriteBatch, Color drawColor) { 22 | spriteBatch.End(); 23 | spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied); 24 | TemplateMod2.npcEffect.Parameters["uTime"].SetValue((float)Main.time); 25 | TemplateMod2.npcEffect.Parameters["uImageSize"].SetValue(Main.npcTexture[npc.type].Size()); 26 | TemplateMod2.npcEffect.CurrentTechnique.Passes["Edge"].Apply(); 27 | return true; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /TemplateMod2.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using ReLogic.Graphics; 4 | using System.Reflection; 5 | using Terraria; 6 | using Terraria.Graphics.Effects; 7 | using Terraria.Graphics.Shaders; 8 | using Terraria.ModLoader; 9 | 10 | // 命名空间,注意它要与文件夹的名字相同 11 | namespace TemplateMod2 { 12 | 13 | // 主要Mod类 14 | public class TemplateMod2 : Mod { 15 | 16 | // 给一个实例指针,以后会非常有用的 17 | public static TemplateMod2 Instance; 18 | public static float Strength; 19 | public static float Progress; 20 | public static Effect npcEffect; 21 | 22 | // 构造函数 23 | public TemplateMod2() { 24 | } 25 | public override void Load() { 26 | Instance = this; 27 | // 注意设置正确的Pass名字 28 | Filters.Scene["TemplateMod:GBlur"] = new Filter( 29 | new TestScreenShaderData(new Ref(GetEffect("Effects/ShockWave")), "Test"), EffectPriority.Medium); 30 | Filters.Scene["TemplateMod:GBlur"].Load(); 31 | 32 | 33 | npcEffect = GetEffect("Effects/EDge"); 34 | 35 | 36 | base.Load(); 37 | } 38 | public override void Unload() { 39 | Instance = null; 40 | Filters.Scene["TemplateMod:GBlur"].Deactivate(); 41 | base.Unload(); 42 | } 43 | public override void PostDrawInterface(SpriteBatch spriteBatch) { 44 | } 45 | 46 | public override void PreUpdateEntities() { 47 | if (!Filters.Scene["TemplateMod:GBlur"].IsActive()) { 48 | // Filters.Scene.Activate("TemplateMod:GBlur"); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /TemplateMod2.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | TemplateMod2 6 | net45 7 | x86 8 | latest 9 | 10 | 11 | 3 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /TemplateMod2.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29102.190 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TemplateMod2", "TemplateMod2.csproj", "{4AF5F2E2-A760-488E-BBF6-630EFBF41A13}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {4AF5F2E2-A760-488E-BBF6-630EFBF41A13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {4AF5F2E2-A760-488E-BBF6-630EFBF41A13}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {4AF5F2E2-A760-488E-BBF6-630EFBF41A13}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {4AF5F2E2-A760-488E-BBF6-630EFBF41A13}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {2E98E4AF-EDD4-4579-8C92-BB191A23236A} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /TemplatePlayer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using Terraria; 8 | using Terraria.DataStructures; 9 | using Terraria.GameInput; 10 | using Terraria.Graphics.Effects; 11 | using Terraria.ModLoader; 12 | 13 | namespace TemplateMod2 { 14 | public class TemplatePlayer : ModPlayer { 15 | public override void PostUpdate() { 16 | //for (int i = 0; i < 20736000; i++) { 17 | // player.statLife += 1; 18 | //} 19 | } 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TemplateWorld.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using Terraria; 8 | using Terraria.DataStructures; 9 | using Terraria.GameInput; 10 | using Terraria.ModLoader; 11 | 12 | 13 | namespace TemplateMod2 { 14 | public class TemplateWorld : ModWorld { 15 | public override void TileCountsAvailable(int[] tileCounts) { 16 | base.TileCountsAvailable(tileCounts); 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Utils/Drawing.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Terraria; 9 | 10 | namespace TemplateMod2.Utils { 11 | public static class Drawing { 12 | public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, int step, float scale, Color c = default(Color)) { 13 | Vector2 unit = Vector2.Normalize(end - start); 14 | float r = unit.ToRotation(); 15 | float dis = (end - start).Length(); 16 | for (int i = 0; i < dis; i += step) { 17 | sb.Draw(Main.magicPixel, start + unit * i, 18 | new Rectangle(0, 0, 1, 1), c, r, new Vector2(0.5f, 0.5f), scale, SpriteEffects.None, 0f); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Utils/LightTree.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using Microsoft.Xna.Framework.Graphics; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Terraria; 9 | using Terraria.ID; 10 | using Terraria.Utilities; 11 | 12 | namespace TemplateMod2.Utils { 13 | public class LightTree { 14 | private class Node { 15 | public float rad, size, length; 16 | public List children; 17 | public Node(float rad, float size, float length) { 18 | this.rad = rad; 19 | this.size = size; 20 | this.length = length; 21 | this.children = new List(); 22 | } 23 | }; 24 | private Node root; 25 | private UnifiedRandom random; 26 | public LightTree(UnifiedRandom random) { 27 | cnt = 0; 28 | root = null; 29 | this.random = random; 30 | } 31 | private Vector2 target; 32 | private int cnt; 33 | private List keyPoints; 34 | 35 | 36 | public void Generate(Vector2 pos, Vector2 vel, Vector2 target) { 37 | // 根节点生成,朝向0,粗细1,长度随机50中选 38 | root = new Node(0, 1f, rand() * 50f); 39 | keyPoints = new List(); 40 | this.target = target; 41 | root = _build(root, pos, vel, true); 42 | // Main.NewText($"生成了一个{cnt}个节点的树状结构"); 43 | } 44 | private Node _build(Node node, Vector2 pos, Vector2 vel, bool root) { 45 | keyPoints.Add(pos); 46 | cnt++; 47 | if (node.size < 0.1f || node.length < 1 || Vector2.Distance(pos, target) < 10) return node; 48 | var r2 = (target - pos).ToRotation() - vel.ToRotation(); 49 | var r = r2 + rand(MathHelper.Pi / 4f); 50 | var unit = (vel.ToRotation() + r).ToRotationVector2(); 51 | Node rchild = new Node(r, node.size * 0.9f, node.length); 52 | // 闪电树主节点(树干) 53 | node.children.Add(_build(rchild, pos + unit * node.length, unit, root)); 54 | if (root) { 55 | if (rand() > 0.75f) { 56 | for (int i = 0; i < 1; i++) { 57 | r = rand(MathHelper.Pi / 3f); 58 | unit = (vel.ToRotation() + r).ToRotationVector2(); 59 | Node child = new Node(r, rand() * node.size * 0.6f, node.length * 0.6f); 60 | node.children.Add(_build(child, pos + unit * node.length, unit, false)); 61 | } 62 | } 63 | } 64 | return node; 65 | } 66 | //private Node _build2(Node node, Vector2 pos, Vector2 vel, bool isMain, Vector2 target) { 67 | // cnt++; 68 | // keyPoints.Add(pos); 69 | // // 终止条件:树枝太细了,或者太短了 70 | // if (node.size < 0.1f || node.length < 1) return node; 71 | // var r2 = (target - pos).ToRotation() - vel.ToRotation(); 72 | // var r = r2 + rand(MathHelper.Pi / 4f); 73 | // Vector2 unit = (vel.ToRotation() + r).ToRotationVector2(); 74 | // Node main = new Node(rand(r), node.size * 0.95f, node.length); 75 | // node.children.Add(_build(main, pos + unit * node.length, unit, isMain, target)); 76 | // // 只有较小的几率出分支 77 | // if (rand() > 0.9f) { 78 | // // 生成分支的时候长度变化不大,但是大小变化很大 79 | // r = rand(MathHelper.Pi / 3f); 80 | // unit = (vel.ToRotation() + r).ToRotationVector2(); 81 | // Node child = new Node(r, node.size * 0.6f, node.length); 82 | // node.children.Add(_build(child, pos + unit * node.length, unit, false, target)); 83 | // } 84 | // return node; 85 | //} 86 | 87 | 88 | //private Node _build(Node node, Vector2 pos, Vector2 vel, bool root) { 89 | // keyPoints.Add(pos); 90 | // cnt++; 91 | // if (node.size < 0.1f || node.length < 1) return node; 92 | // var r2 = (target - pos).ToRotation() - vel.ToRotation(); 93 | // var r = r2 + rand(MathHelper.Pi / 4f); 94 | // var unit = (vel.ToRotation() + r).ToRotationVector2(); 95 | // Node rchild = new Node(r, node.size * 0.9f, node.length); 96 | // // 闪电树主节点(树干) 97 | // node.children.Add(_build(rchild, pos + unit * node.length, unit, root)); 98 | // if (root) { 99 | // if (rand() > 0.8f) { 100 | // for (int i = 0; i < 1; i++) { 101 | // r = rand(MathHelper.Pi / 3f); 102 | // unit = (vel.ToRotation() + r).ToRotationVector2(); 103 | // Node child = new Node(r, rand() * node.size * 0.6f, node.length * 0.6f); 104 | // node.children.Add(_build(child, pos + unit * node.length, unit, false)); 105 | // } 106 | // } 107 | // } 108 | // return node; 109 | //} 110 | 111 | 112 | 113 | 114 | private float rand() { 115 | double u = -2 * Math.Log(random.NextDouble()); 116 | double v = 2 * Math.PI * random.NextDouble(); 117 | return (float)Math.Max(0, Math.Sqrt(u) * Math.Cos(v) * 0.3 + 0.5); 118 | } 119 | 120 | private float rand(float range) { 121 | return random.NextFloatDirection() * range; 122 | } 123 | 124 | //private Node _build(Node node, Vector2 pos, Vector2 vel, bool root) { 125 | // keyPoints.Add(pos); 126 | // cnt++; 127 | // if (node.size < 0.1f || node.length < 1) return node; 128 | // var r2 = (target - pos).ToRotation() - vel.ToRotation(); 129 | // var r = r2 + rand(MathHelper.Pi / 4f); 130 | // var unit = (vel.ToRotation() + r).ToRotationVector2(); 131 | // Node rchild = new Node(r, node.size * 0.9f, node.length); 132 | // 闪电树主节点(树干) 133 | // node.children.Add(_build(rchild, pos + unit * node.length, unit, root)); 134 | // if (root) { 135 | // if (rand() > 0.8f) { 136 | // for (int i = 0; i < 1; i++) { 137 | // r = rand(MathHelper.Pi / 3f); 138 | // unit = (vel.ToRotation() + r).ToRotationVector2(); 139 | // Node child = new Node(r, rand() * node.size * 0.6f, node.length * 0.6f); 140 | // node.children.Add(_build(child, pos + unit * node.length, unit, false)); 141 | // } 142 | // } 143 | // } 144 | // return node; 145 | //} 146 | 147 | public void Draw(SpriteBatch sb, Vector2 pos, Vector2 vel) { 148 | sb.End(); 149 | sb.Begin(SpriteSortMode.Deferred, BlendState.Additive); 150 | _draw(sb, pos, vel, root, Color.Cyan * 0.4f, 5f); 151 | _draw(sb, pos, vel, root, Color.White * 0.6f, 3f); 152 | sb.End(); 153 | sb.Begin(); 154 | } 155 | 156 | public void SpawnDust(Vector2 pos, Vector2 vel) { 157 | _dust(pos, vel, root); 158 | } 159 | 160 | private void _draw(SpriteBatch sb, Vector2 pos, Vector2 vel, Node node, Color c, float factor) { 161 | // 树枝实际的方向向量 162 | Vector2 unit = (vel.ToRotation() + node.rad).ToRotationVector2(); 163 | // 类似激光的线性绘制方法,绘制出树枝 164 | for (float i = 0; i <= node.length; i += 0.3f) 165 | sb.Draw(Main.magicPixel, pos + unit * i, new Rectangle(0, 0, 1, 1), c, 0, 166 | new Vector2(0.5f, 0.5f), Math.Max(node.size * factor, 0.3f), SpriteEffects.None, 0f); 167 | // 递归到子节点进行绘制 168 | foreach (var child in node.children) { 169 | // 传递给子节点真实的位置和方向向量 170 | _draw(sb, pos + unit * node.length, unit, child, c, factor); 171 | } 172 | } 173 | 174 | public void Tile(Vector2 pos, Vector2 vel) { 175 | _tile(pos, vel, root); 176 | } 177 | 178 | private void _tile(Vector2 pos, Vector2 vel, Node node) { 179 | // 树枝实际的方向向量 180 | Vector2 unit = (vel.ToRotation() + node.rad).ToRotationVector2(); 181 | // 类似激光的线性绘制方法,绘制出树枝 182 | for (float i = 0; i <= node.length * 4; i += 8f) { 183 | Point p = (pos + unit * i).ToTileCoordinates(); 184 | Main.tile[p.X, p.Y] = new Tile(); 185 | Main.tile[p.X, p.Y].type = TileID.WoodBlock; 186 | Main.tile[p.X, p.Y].active(true); 187 | WorldGen.SquareTileFrame(p.X, p.Y); 188 | } 189 | // 递归到子节点进行绘制 190 | foreach (var child in node.children) { 191 | // 传递给子节点真实的位置和方向向量 192 | _tile(pos + unit * node.length * 4, unit, child); 193 | } 194 | } 195 | 196 | private void _dust(Vector2 pos, Vector2 vel, Node node) { 197 | float r = vel.ToRotation(); 198 | Vector2 unit = (r + node.rad).ToRotationVector2(); 199 | for (float i = 0; i <= node.length; i += 4f) { 200 | var dust = Dust.NewDustDirect(pos + unit * i, 0, 0, 201 | MyDustId.DemonTorch, 0, 0, 100, Color.White, 1); 202 | dust.noGravity = true; 203 | dust.velocity *= 0; 204 | dust.position = pos + unit * i; 205 | } 206 | foreach (var child in node.children) { 207 | _dust(pos + unit * node.length, unit, child); 208 | } 209 | } 210 | 211 | public bool Check(Rectangle hitbox) { 212 | foreach (var pt in keyPoints) 213 | if (hitbox.Contains(pt.ToPoint())) return true; 214 | return false; 215 | } 216 | } 217 | } 218 | 219 | //using Microsoft.Xna.Framework; 220 | //using Microsoft.Xna.Framework.Graphics; 221 | //using System; 222 | //using System.Collections.Generic; 223 | //using System.Linq; 224 | //using System.Text; 225 | //using System.Threading.Tasks; 226 | //using Terraria; 227 | //using Terraria.Utilities; 228 | 229 | //namespace TemplateMod2.Utils { 230 | // public class LightTree { 231 | // private class Node { 232 | // public float rad, size, length; 233 | // public List children; 234 | // public Node(float rad, float size, float length) { 235 | // this.rad = rad; 236 | // this.size = size; 237 | // this.length = length; 238 | // this.children = new List(); 239 | // } 240 | // }; 241 | // private Node root; 242 | // private UnifiedRandom random; 243 | // public LightTree(UnifiedRandom random) { 244 | // cnt = 0; 245 | // root = null; 246 | // this.random = random; 247 | // } 248 | // private Vector2 target; 249 | // private int cnt; 250 | // private List keyPoints; 251 | 252 | 253 | // public void Generate(Vector2 pos, Vector2 vel, Vector2 target) { 254 | // keyPoints = new List(); 255 | // root = new Node(0, 1f, rand() * LEN); 256 | // this.target = target; 257 | // root = _build(root, pos, vel, true); 258 | // // Main.NewText($"生成了一个{cnt}个节点的树状结构"); 259 | // } 260 | // private float rand() { 261 | // double u = -2 * Math.Log(random.NextDouble()); 262 | // double v = 2 * Math.PI * random.NextDouble(); 263 | // return (float)Math.Max(0, Math.Sqrt(u) * Math.Cos(v) * 0.3 + 0.5); 264 | // } 265 | 266 | // private float rand(float range) { 267 | // return random.NextFloatDirection() * range; 268 | // } 269 | 270 | // private Node _build(Node node, Vector2 pos, Vector2 vel, bool root) { 271 | // keyPoints.Add(pos); 272 | // cnt++; 273 | // if (node.size < 0.1f || node.length < 1) return node; 274 | // var r2 = (target - pos).ToRotation() - vel.ToRotation(); 275 | // var r = r2 + rand(MathHelper.Pi / 4f); 276 | // var unit = (vel.ToRotation() + r).ToRotationVector2(); 277 | // Node rchild = new Node(r, node.size * 0.9f, node.length); 278 | // // 闪电树主节点(树干) 279 | // node.children.Add(_build(rchild, pos + unit * node.length, unit, root)); 280 | // if (root) { 281 | // if (rand() > 0.8f) { 282 | // for (int i = 0; i < 1; i++) { 283 | // r = rand(MathHelper.Pi / 3f); 284 | // unit = (vel.ToRotation() + r).ToRotationVector2(); 285 | // Node child = new Node(r, rand() * node.size * 0.6f, node.length * 0.6f); 286 | // node.children.Add(_build(child, pos + unit * node.length, unit, false)); 287 | // } 288 | // } 289 | // } 290 | // return node; 291 | // } 292 | 293 | // public void Draw(SpriteBatch sb, Vector2 pos, Vector2 vel) { 294 | // _draw(sb, pos, vel, root); 295 | // } 296 | 297 | // public void SpawnDust(Vector2 pos, Vector2 vel) { 298 | // _dust(pos, vel, root); 299 | // } 300 | 301 | // private void _draw(SpriteBatch sb, Vector2 pos, Vector2 vel, Node node) { 302 | // Vector2 unit = (vel.ToRotation() + node.rad).ToRotationVector2(); 303 | // for (float i = 0; i <= node.length; i += 0.04f) { 304 | // sb.Draw(Main.magicPixel, pos + unit * i, new Rectangle(0, 0, 1, 1), Color.White * 0.5f, 0, 305 | // new Vector2(0.5f, 0.5f), Math.Max(node.size * 7, 0.2f), SpriteEffects.None, 0f); 306 | // } 307 | // foreach (var child in node.children) { 308 | // _draw(sb, pos + unit * node.length, unit, child); 309 | // } 310 | // } 311 | 312 | // private void _dust(Vector2 pos, Vector2 vel, Node node) { 313 | // float r = vel.ToRotation(); 314 | // Vector2 unit = (r + node.rad).ToRotationVector2(); 315 | // for (float i = 0; i <= node.length; i += 4f) { 316 | // var dust = Dust.NewDustDirect(pos + unit * i, 0, 0, 317 | // MyDustId.DemonTorch, 0, 0, 100, Color.White, 1); 318 | // dust.noGravity = true; 319 | // dust.velocity *= 0; 320 | // dust.position = pos + unit * i; 321 | // } 322 | // foreach (var child in node.children) { 323 | // _dust(pos + unit * node.length, unit, child); 324 | // } 325 | // } 326 | 327 | // public bool Check(Rectangle hitbox) { 328 | // foreach (var pt in keyPoints) 329 | // if (hitbox.Contains(pt.ToPoint())) return true; 330 | // return false; 331 | // } 332 | // } 333 | //} 334 | -------------------------------------------------------------------------------- /Utils/MazeTree.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Xna.Framework; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Terraria; 8 | using Terraria.ID; 9 | using Terraria.Map; 10 | using Terraria.Utilities; 11 | 12 | namespace TemplateMod2.Utils { 13 | public class MazeTree { 14 | private class Node : IComparable { 15 | public int dir, x, y, p; 16 | public Node[] children = new Node[4]; 17 | public Node(int dir, int x, int y, int p) { 18 | this.dir = dir; 19 | this.x = x; 20 | this.y = y; 21 | this.p = p; 22 | } 23 | 24 | public int CompareTo(object obj) { 25 | var node = (Node)obj; 26 | return p.CompareTo(node.p); 27 | } 28 | }; 29 | 30 | // 左右上下 31 | private static int[] dx = { -1, 1, 0, 0 }; 32 | private static int[] dy = { 0, 0, -1, 1 }; 33 | private Node root; 34 | private UnifiedRandom random; 35 | private int maxX, maxY; 36 | private bool[,] vis; 37 | private int cnt; 38 | public MazeTree(UnifiedRandom random) { 39 | root = null; 40 | this.random = random; 41 | } 42 | 43 | private bool check(int x, int y) { 44 | return x >= 0 && x < maxX && y >= 0 && y < maxY && !vis[x, y]; 45 | } 46 | 47 | private void swap(ref int a, ref int b) { 48 | int t = a; 49 | a = b; 50 | b = t; 51 | } 52 | 53 | public void Build(int x, int y) { 54 | maxX = x; 55 | maxY = y; 56 | vis = new bool[x, y]; 57 | for (int i = 0; i < x; i++) { 58 | for (int j = 0; j < y; j++) { 59 | vis[i, j] = false; 60 | } 61 | } 62 | root = new Node(-1, 0, 0, 0); 63 | vis[0, 0] = true; 64 | PriorityQueue Q = new PriorityQueue(x * y); 65 | Q.Push(root); 66 | while (!Q.Empty) { 67 | var cur = Q.Top; 68 | Q.Pop(); 69 | for (int i = 0; i < 4; i++) { 70 | int nx = cur.x + dx[i], ny = cur.y + dy[i]; 71 | Node nd = new Node(i, nx, ny, -Math.Abs(nx - maxX) - Math.Abs(ny - maxY)); 72 | if (!check(nd.x, nd.y)) continue; 73 | vis[nd.x, nd.y] = true; 74 | cur.children[i] = nd; 75 | Q.Push(nd); 76 | } 77 | } 78 | } 79 | 80 | 81 | 82 | 83 | public void Tile(int x, int y) { 84 | _tile(root, x, y); 85 | } 86 | 87 | //private void _tile(Node node, int x, int y) { 88 | // // 把3x3单元格填满 89 | // for (int i = -1; i <= 1; i++) { 90 | // for (int j = -1; j <= 1; j++) { 91 | // Main.tile[x + i, y + j] = new Tile { 92 | // type = TileID.WoodBlock, 93 | // }; 94 | // Main.tile[x + i, y + j].active(true); 95 | // } 96 | // } 97 | // // 中间的清除掉 98 | // Main.tile[x, y] = new Tile(); 99 | // // 如果不在根节点 100 | // if (node.dir != -1) { 101 | // // 获取方向的反向 102 | // int d = node.dir ^ 1; 103 | // // 挖空 104 | // for (int i = 1; i < 3; i++) 105 | // Main.tile[x + dx[d] * i, y + dy[d] * i] = new Tile(); 106 | // } 107 | // // 顺着每个非空子节点继续构造迷宫 108 | // for (int i = 0; i < 4; i++) { 109 | // Node child = node.children[i]; 110 | // if (child == null) continue; 111 | // // 因为单元格变成3x3,所以这里的坐标也要扩大三倍,总体迷宫范围扩大9倍 112 | // _tile(child, x + dx[child.dir] * 3, y + dy[child.dir] * 3); 113 | // } 114 | //} 115 | 116 | private void _tile(Node node, int x, int y) { 117 | for (int i = -2; i <= 2; i++) { 118 | for (int j = -2; j <= 2; j++) { 119 | Main.tile[x + i, y + j] = new Tile { 120 | type = TileID.WoodBlock, 121 | }; 122 | Main.tile[x + i, y + j].active(true); 123 | } 124 | } 125 | if (node.dir != -1) { 126 | int d = node.dir ^ 1; 127 | for (int i = -1; i < 7; i++) { 128 | Main.tile[x + dx[d] * i, y + dy[d] * i] = new Tile(); 129 | if (d >= 2) { 130 | Main.tile[x + dx[d] * i + 1, y + dy[d] * i] = new Tile(); 131 | Main.tile[x + dx[d] * i - 1, y + dy[d] * i] = new Tile(); 132 | } else { 133 | Main.tile[x + dx[d] * i, y + dy[d] * i + 1] = new Tile(); 134 | Main.tile[x + dx[d] * i, y + dy[d] * i - 1] = new Tile(); 135 | } 136 | } 137 | } 138 | for (int i = 0; i < 4; i++) { 139 | Node child = node.children[i]; 140 | if (child == null) continue; 141 | _tile(child, x + dx[child.dir] * 5, y + dy[child.dir] * 5); 142 | } 143 | } 144 | 145 | //private void _tile(Node node, int x, int y) { 146 | // if (node.dir == -1) { 147 | // for (int i = -1; i <= 1; i++) { 148 | // for (int j = -1; j <= 1; j++) { 149 | // Main.tile[x + i, y + j] = new Tile(); 150 | // } 151 | // } 152 | // } else { 153 | // for (int i = -1; i <= 1; i++) { 154 | // for (int j = -1; j <= 1; j++) { 155 | // Main.tile[x + i, y + j] = new Tile { 156 | // type = TileID.WoodBlock, 157 | // }; 158 | // Main.tile[x + i, y + j].active(true); 159 | // } 160 | // } 161 | // if (node.dir < 2) { 162 | // int d = node.dir; 163 | // for (int i = -1 + d; i <= -1 + d + 1; i++) { 164 | // Main.tile[x + i, y] = new Tile(); 165 | // } 166 | // if (node.dirp >= 2) { 167 | // d = (node.dirp == 2) ? 1 : -1; 168 | // Main.tile[x, y + d] = new Tile(); 169 | // } else { 170 | // for (int i = -1; i <= 1; i++) { 171 | // Main.tile[x + i, y] = new Tile(); 172 | // } 173 | // } 174 | // } else { 175 | // int d = (node.dir == 2) ? 0 : 1; 176 | // for (int i = -1 + d; i <= -1 + d + 1; i++) { 177 | // Main.tile[x, y + i] = new Tile(); 178 | // } 179 | // if (node.dirp < 2) { 180 | // d = (node.dirp == 0) ? 1 : -1; 181 | // Main.tile[x + d, y] = new Tile(); 182 | // } else { 183 | // for (int i = -1; i <= 1; i++) { 184 | // Main.tile[x, y + i] = new Tile(); 185 | // } 186 | // } 187 | // } 188 | // } 189 | // for (int i = 0; i < 4; i++) { 190 | // Node child = node.children[i]; 191 | // if (child == null) continue; 192 | // _tile(child, x + dx[child.dir] * 3, y + dy[child.dir] * 3); 193 | // } 194 | //} 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Utils/MyDustID.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | /// 7 | /// 命名空间自己改,这个文件是四十九落星内部源码的一部分 8 | /// 9 | namespace TemplateMod2.Utils { 10 | /// 11 | /// DXTsT自制的粒子ID表 12 | /// 制作时间:2017/1/31 13 | /// 版权所有:DXTsT & 四十九落星制作组 14 | /// 15 | /// 说明:以下字段带有(!)标识符的说明此粒子效果会在黑暗中自发光 16 | /// 带有(.)标识符说明此粒子效果会高亮显示但是不会发光 17 | /// 其余Dust全部都不会发光! 18 | /// 19 | /// 感谢所有参与制作者:Rainbow Fluorescence,子冀,海琴烟 20 | /// 21 | public static class MyDustId { 22 | /// 23 | /// 土壤粒子,不发光,受重力影响。 24 | /// 25 | public const int BrownDirt = 0; 26 | /// 27 | /// 岩石粒子,不发光,受重力影响。 28 | /// 29 | public const int GreyStone = 1; 30 | /// 31 | /// 浅绿色粒子,不发光,受重力影响。 32 | /// 33 | public const int GreenGrass = 2; 34 | /// 35 | /// 绿色粒子,不发光,受重力影响。 36 | /// 37 | public const int ThinGreenGrass = 3; 38 | /// 39 | /// 灰色粒子,不发光,受重力影响。 40 | /// 41 | public const int GreyPebble = 4; 42 | /// 43 | /// 深红色粒子,不发光,受重力影响。 44 | /// 45 | public const int RedBlood = 5; 46 | /// 47 | ///(!)橘黄色火焰粒子,自发光,范围中等,受重力影响。 48 | /// 49 | public const int Fire = 6; 50 | /// 51 | /// 深土壤色粒子,不发光,受重力影响。 52 | /// 53 | public const int Wood = 7; 54 | /// 55 | /// 铁矿色粒子,不发光,受重力影响。 56 | /// 57 | public const int PurpleGems = 8; 58 | /// 59 | /// 铜矿色粒子,不发光,受重力影响。 60 | /// 61 | public const int OrangeGems = 9; 62 | /// 63 | /// 金矿色粒子,不发光,受重力影响。 64 | /// 65 | public const int YellowGems = 10; 66 | /// 67 | /// 银矿色粒子,不发光,受重力影响。 68 | /// 69 | public const int WhiteGems = 11; 70 | /// 71 | /// 精金矿色粒子,不发光,受重力影响。 72 | /// 73 | public const int RedGems = 12; 74 | /// 75 | /// 钴蓝矿色粒子,不发光,受重力影响。 76 | /// 77 | public const int CyanGems = 13; 78 | /// 79 | /// 魔晶矿色粒子,不发光,受重力影响。 80 | /// 81 | public const int CorruptionParticle = 14; 82 | /// 83 | ///(!)冰晶色粒子,自发光,范围大,不受重力影响且在重力下持续时间变长。 84 | /// 85 | public const int BlueMagic = 15; 86 | /// 87 | ///(.)浅蓝云色粒高亮,不受重力影响且在重力下持续时间变长。 88 | /// 89 | public const int WhiteClouds = 16; 90 | /// 91 | /// 蓝黑色粒子,不发光,受重力影响。 92 | /// 93 | public const int ThinGrey = 17; 94 | /// 95 | /// 叶绿色粒子,不发光,受重力影响。 96 | /// 97 | public const int SicklyGreen = 18; 98 | /// 99 | /// 金色粒子,不发光,受重力影响。 100 | /// 101 | public const int ThinYellow = 19; 102 | /// 103 | /// (!)纯白色粒子,自发光,范围中等,不受重力影响且在重力下持续时间变长。 104 | /// 105 | public const int WhiteLingering = 20; 106 | /// 107 | /// (!)亮粉色粒子,自发光,范围小,不受重力影响且在重力下持续时间变长。 108 | /// 109 | public const int PurpleLingering = 21; 110 | /// 111 | /// 深土壤色粒子,不发光,受重力影响。 112 | /// 113 | public const int Brown = 22; 114 | /// 115 | /// 微粉土壤色粒子,不发光,受重力影响。 116 | /// 117 | public const int Orange = 23; 118 | /// 119 | /// 微紫土壤色粒子,不发光,受重力影响。 120 | /// 121 | public const int ThinBrown = 24; 122 | /// 123 | /// 狱岩石色粒子,不发光,受重力影响。 124 | /// 125 | public const int Copper = 25; 126 | /// 127 | /// 枯草色粒子,不发光,受重力影响。 128 | /// 129 | public const int Iron = 26; 130 | /// 131 | /// (!)蓝紫粉色粒子,自发光,范围中等,不受重力影响且在重力下持续时间变长。 132 | /// 133 | public const int PurpleLight = 27; 134 | /// 135 | /// 深铜色粒子,不发光,受重力影响。 136 | /// 137 | public const int DullCopper = 28; 138 | /// 139 | /// (!)深蓝色粒子,自发光,受重力影响。 140 | /// 141 | public const int DarkBluePinkLight = 29; 142 | /// 143 | /// 银白色粒子,不发光,受重力影响。 144 | /// 145 | public const int Silver = 30; 146 | /// 147 | /// 白云色粒子,不发光,不受重力影响。 148 | /// 149 | public const int Smoke = 31; 150 | /// 151 | /// 深黄色粒子,不发光,受重力影响。 152 | /// 153 | public const int Sand = 32; 154 | /// 155 | /// 水蓝色粒子,不发光,受重力影响。 156 | /// 157 | public const int Water = 33; 158 | /// 159 | ///(!)金黄火焰色粒子,自发光,范围中等,在重力下不显现,在无重力下显现。 160 | /// 161 | public const int RedLight = 35; 162 | /// 163 | /// 浅黑色粒子,不发光,受重力影响。 164 | /// 165 | public const int MuddyPale = 36; 166 | /// 167 | /// 深蓝黑色粒子,不发光,受重力影响。 168 | /// 169 | public const int DarkGrey = 37; 170 | /// 171 | /// 深土壤色粒子,不发光,受重力影响。 172 | /// 173 | public const int MuddyBrown = 38; 174 | /// 175 | /// 绿色粒子,不发光,受重力影响。 176 | /// 177 | public const int JungleGrass = 39; 178 | /// 179 | /// 深叶绿色粒子,不发光,受重力影响。 180 | /// 181 | public const int ThinGrass = 40; 182 | /// 183 | /// (!)亮水蓝色粒子,自发光,范围大,在重力下停留扩散且时间较长,无重力时消散较快。 184 | /// 185 | public const int BlueCircle = 41; 186 | /// 187 | /// 深钴蓝色粒子,不发光,受重力影响。 188 | /// 189 | public const int ThinTeal = 42; 190 | /// 191 | /// (!)亮光色粒子,不稳定发光,光源不穿透,光照范围与大小成正比,不受重力影响。 192 | /// 193 | public const int WhiteLight = 43; 194 | /// 195 | /// (!)黄绿色粒子,发白光,范围很大,在重力下停留扩散且时间较长,无重力时消散较快。 196 | /// 197 | public const int GreenSpores = 44; 198 | /// 199 | /// (!)深水蓝色粒子,自发光,在重力下停留扩散且时间较长,无重力时消散较快。 200 | /// 201 | public const int LightBlueCircle = 45; 202 | /// 203 | /// 深绿色粒子,不发光,不受重力影响。 204 | /// 205 | public const int GreenMaterial = 46; 206 | /// 207 | /// 深蓝色粒子,不发光,受重力影响。 208 | /// 209 | public const int CyanGrass = 47; 210 | /// 211 | /// 蘑菇矿色粒子,不发光,受重力影响。 212 | /// 213 | public const int BlueMushroom = 48; 214 | /// 215 | /// 蓝偏黑色粒子,不发光,受重力影响。 216 | /// 217 | public const int BlueDrakParticle = 49; 218 | /// 219 | /// 深精金矿色粒子,不发光,受重力影响。 220 | /// 221 | public const int RedParticle = 50; 222 | /// 223 | /// 白土色粒子,不发光,受重力影响。 224 | /// 225 | public const int PearlStone = 51; 226 | /// 227 | /// 粉,水,不发光,浅,色,受,力,高度。明。 228 | /// 229 | public const int PinkWater = 52; 230 | /// 231 | /// 灰色材质,不发光,浅深灰色,受重力,不受重力消失快。 232 | /// 233 | public const int GreyMaterial = 53; 234 | /// 235 | /// 黑色材质,不发光,浅黑色,受重力,不受重力消失快。 236 | /// 237 | public const int BlackMaterial = 54; 238 | /// 239 | /// (!)亮金火焰色粒子,自发光,范围较大,受重力时旋转扩散。 240 | /// 241 | public const int OrangeFx = 55; 242 | /// 243 | /// (!)天蓝色粒子,自发光,范围中等,在重力下扩散,消散较快。 244 | /// 245 | public const int CyanFx = 56; 246 | /// 247 | /// (!)小型黄色神圣特效,自发光,发黄浅浅黄色、金色,受重力时旋转扩散。 248 | /// 249 | public const int YellowHallowFx = 57; 250 | /// 251 | /// (!)亮粉白色粒子,自发光,范围较大,不受重力影响。 252 | /// 253 | public const int PinkMagic = 58; 254 | /// 255 | /// (!)晶蓝色粒子,自发光,高蓝光,受重力影响。 256 | /// 257 | public const int BlueTorch = 59; 258 | /// 259 | /// (!)偏粉红色粒子,自发光,高红光,受重力影响。 260 | /// 261 | public const int RedTorch = 60; 262 | /// 263 | /// (!)亮绿色粒子,自发光,高绿光,受重力影响。 264 | /// 265 | public const int GreenTorch = 61; 266 | /// 267 | /// (!)紫色粒子,自发光,高紫光,受重力影响。 268 | /// 269 | public const int PurpleTorch = 62; 270 | /// 271 | /// (!)灰白色粒子,自发光,白光,受重力影响。 272 | /// 273 | public const int WhiteTorch = 63; 274 | /// 275 | /// (!)纯金色粒子,自发光,受重力影响。 276 | /// 277 | public const int YellowTorch = 64; 278 | /// 279 | /// (!)深紫色粒子,自发光,受重力影响。 280 | /// 281 | public const int DemonTorch = 65; 282 | /// 283 | /// (!)白色粒子,自发光,范围非常大,在重力下迅速变大并且旋转,无重力时消散较快。 284 | /// 285 | public const int WhiteTransparent = 66; 286 | /// 287 | /// (!)浅海蓝色粒子,自发光,范围中等,受重力影响。 288 | /// 289 | public const int CyanIce = 67; 290 | /// 291 | /// (!)暗蓝冰晶,自发光,受重力时发暗蓝光深蓝色,不受重力时高亮发蓝光亮青色。 292 | /// 293 | public const int DarkCyanIce = 68; 294 | /// 295 | /// 粉色粒子,不发光,受重力影响。 296 | /// 297 | public const int ThinPink = 69; 298 | /// 299 | /// (!)透明紫色粒子,自发光,受重力时发暗紫光深紫色,不受重力时高亮发粉光亮粉色。 300 | /// 301 | public const int TransparentPurple = 70; 302 | /// 303 | /// (.)透明粉色特效,高亮,发粉浅亮粉色,受重力时旋转扩散。 304 | /// 305 | public const int TransparentPinkFx = 71; 306 | /// 307 | /// (.)红粉色粒子,高亮,在重力下扩散并消散,无重力时消散较快。 308 | /// 309 | public const int SolidPinkFx = 72; 310 | /// 311 | /// (!)亮红粉色粒子,自发光,范围中等,在重力下扩散并消散,无重力时消散较快。 312 | /// 313 | public const int BrightPinkFx = 73; 314 | /// 315 | /// (!)亮绿色粒子,自发光,范围中等,在重力下扩散并消散,无重力时消散较快。 316 | /// 317 | public const int BrightGreenFx = 74; 318 | /// 319 | /// (!)诅咒火把,自发光,发黄绿浅黄绿色,受重力。 320 | /// 321 | public const int CursedFire = 75; 322 | /// 323 | /// 下雪,浅白色,受重力时旋转大范围扩散,存在时间长,遇到物块消失。 324 | /// 325 | public const int Snow = 76; 326 | /// 327 | /// 阴影木,不发光,浅深灰色,受重力,不受重力消失快。 328 | /// 329 | public const int ThinGrey1 = 77; 330 | /// 331 | /// 红木,不发光,浅红棕色,受重力,不受重力消失快。 332 | /// 333 | public const int ThinCopper = 78; 334 | /// 335 | /// 薄黄材质,不发光,浅黄色,受重力下落不旋转,不受重力消失快。 336 | /// 337 | public const int ThinYellow1 = 79; 338 | /// 339 | /// 冰块,不发光,浅蓝白色,受重力,不受重力消失快。 340 | /// 341 | public const int IceBlock = 80; 342 | /// 343 | /// 锡矿石,不发光,浅灰色,受重力,不受重力消失快。 344 | /// 345 | public const int Tin = 81; 346 | /// 347 | /// 铅矿石,不发光,浅蓝黑色,受重力,不受重力消失快。 348 | /// 349 | public const int Lead = 82; 350 | /// 351 | /// 铅矿石,不发光,浅蓝黑色,受重力,不受重力消失快。 352 | /// 353 | public const int Tungsten = 83; 354 | /// 355 | /// 铂矿石,不发光,浅蓝银色,受重力,不受重力消失快。 356 | /// 357 | public const int Platinum = 84; 358 | /// 359 | /// 沙褐材质,不发光,浅橙色,受重力,不受重力消失快。 360 | /// 361 | public const int ThinSandy = 85; 362 | /// 363 | /// (!)少女粉色粒子,自发光,范围较大,受重力影响。 364 | /// 365 | public const int PinkTrans = 86; 366 | /// 367 | /// (!)亮金黄色粒子,自发光,范围中等,受重力影响。 368 | /// 369 | public const int YellowTrans = 87; 370 | /// 371 | /// (!)白偏浅蓝色粒子,自发光,范围中等,受重力影响。 372 | /// 373 | public const int BlueTrans = 88; 374 | /// 375 | /// (!)白绿色粒子,自发光,范围中等,受重力影响。 376 | /// 377 | public const int GreenTrans = 89; 378 | /// 379 | /// (!)粉红色粒子,自发光,范围中等,受重力影响。 380 | /// 381 | public const int RedTrans = 90; 382 | /// 383 | /// (!)亮白色粒子,自发光,范围大,受重力影响。 384 | /// 385 | public const int WhiteTrans = 91; 386 | /// 387 | /// (!)蓝白色粒子,自发光,范围较大,受重力影响。 388 | /// 389 | public const int CyanTrans = 92; 390 | /// 391 | /// 松绿色粒子,不发光,受重力影响。 392 | /// 393 | public const int DarkGrass = 93; 394 | /// 395 | /// 深黄绿色粒子,不发光,受重力影响。 396 | /// 397 | public const int PaleDarkGrass = 94; 398 | /// 399 | /// 深红偏黑色粒子,不发光,受重力影响。 400 | /// 401 | public const int DarkRedGrass = 95; 402 | /// 403 | /// 深蓝色粒子,不发光,受重力影响。 404 | /// 405 | public const int BlackGreenGrass = 96; 406 | /// 407 | /// 深紫偏黑色粒子,不发光,受重力影响。 408 | /// 409 | public const int DarkRedGrass1 = 97; 410 | /// 411 | /// 紫色粒子,不发光,受重力影响。 412 | /// 413 | public const int PurpleWater = 98; 414 | /// 415 | /// 浅绿色粒子,不发光,受重力影响。 416 | /// 417 | public const int CyanWater = 99; 418 | /// 419 | /// 浅粉色粒子,不发光,受重力影响。 420 | /// 421 | public const int PinkWater1 = 100; 422 | /// 423 | /// 浅蓝色粒子,不发光,受重力影响。 424 | /// 425 | public const int CyanWater1 = 101; 426 | /// 427 | /// 浅黄土色粒子,不发光,受重力影响。 428 | /// 429 | public const int OrangeWater = 102; 430 | /// 431 | /// 深蓝偏白色粒子,不发光,受重力影响。 432 | /// 433 | public const int DarkBlueWater = 103; 434 | /// 435 | /// 深粉偏黑色粒子,不发光,受重力影响。 436 | /// 437 | public const int HotPinkWater = 104; 438 | /// 439 | /// 大红色粒子,不发光,受重力影响。 440 | /// 441 | public const int RedWater = 105; 442 | /// 443 | /// (.)红黄绿三色火焰色粒子,高亮,受重力影响。 444 | /// 445 | public const int RGBMaterial = 106; 446 | /// 447 | /// (!)亮白绿色粒子,自发光,范围小,在重力下能够悬停较长时间。 448 | /// 449 | public const int GreenFXPowder = 107; 450 | /// 451 | /// 浅灰浅蓝色粒子,不发光,受重力影响。 452 | /// 453 | public const int PurpleRound = 108; 454 | /// 455 | /// 黑色粒子,不发光,受重力影响。 456 | /// 457 | public const int BlackMaterial1 = 109; 458 | /// 459 | /// (.)亮浅蓝偏绿色粒子,高亮,在重力下快速扩散范围但不大。 460 | /// 461 | public const int GreenBubble = 110; 462 | /// 463 | /// (.)亮蓝色粒子,高亮,在重力下快速扩散范围但不大。 464 | /// 465 | public const int CyanBubble = 111; 466 | /// 467 | /// (.)亮粉色粒子,高亮,在重力下快速扩散范围但不大。 468 | /// 469 | public const int PinkBubble = 112; 470 | /// 471 | /// (.)亮深蓝偏白色粒子,高亮,在重力下快速扩散范围但不大。 472 | /// 473 | public const int BlueIce = 113; 474 | /// 475 | /// (.)亮粉偏红色粒子,高亮,在重力下快速扩散范围但不大。 476 | /// 477 | public const int PinkYellowBubble = 114; 478 | /// 479 | /// 锈红色粒子,不发光,受重力影响 480 | /// 481 | public const int RedGrass = 115; 482 | /// 483 | /// 深蓝色粒子,不发光,受重力影响。 484 | /// 485 | public const int BlueGreenGrass = 116; 486 | /// 487 | /// 较锈红色粒子,不发光,受重力影响。 488 | /// 489 | public const int RedGrass1 = 117; 490 | /// 491 | /// 紫蓝白色粒子,不发光,受重力影响。 492 | /// 493 | public const int PurpleGems1 = 118; 494 | /// 495 | /// 深粉红色粒子,不发光,受重力影响。 496 | /// 497 | public const int PinkGems = 119; 498 | /// 499 | /// 深棕白色粒子,不发光,受重力影响。 500 | /// 501 | public const int PalePinkGems = 120; 502 | /// 503 | /// 深灰黑色粒子,不发光,受重力影响。 504 | /// 505 | public const int ThinGrey2 = 121; 506 | /// 507 | /// 深机械色粒子,不发光,受重力影响。 508 | /// 509 | public const int ThinIron = 122; 510 | /// 511 | /// 深粉红色粒子,不发光,受重力影响。 512 | /// 513 | public const int HotPinkBubble = 123; 514 | /// 515 | /// 浅黄偏白色粒子,不发光,受重力影响。 516 | /// 517 | public const int YellowWhiteBubble = 124; 518 | /// 519 | /// 深红偏黑色粒子,不发光,受重力影响。 520 | /// 521 | public const int ThinRed = 125; 522 | /// 523 | /// 深灰偏绿色粒子,不发光,受重力影响。 524 | /// 525 | public const int ThinGrey3 = 126; 526 | /// 527 | /// (!)岩浆色粒子,自发光,范围小,受重力影响。 528 | /// 529 | public const int OrangeFire = 127; 530 | /// 531 | /// 叶绿矿色粒子,不发光,受重力影响。 532 | /// 533 | public const int GreenGems = 128; 534 | /// 535 | /// 深土黄色粒子,不发光,受重力影响。 536 | /// 537 | public const int ThinBrown1 = 129; 538 | /// 539 | /// (!)白粉色粒子,自发光,范围较大,在重力下会呈现粒子下坠特效,在无重力时会悬停扩散,较快消散。 540 | /// 541 | public const int TrailingPinkWhite = 130; 542 | /// 543 | /// (!)绿色粒子,自发光,重力下扩散范围更大。 544 | /// 545 | public const int TrailingGreen = 131; 546 | /// 547 | /// (!)蓝色粒子,自发光,重力下曳尾。 548 | /// 549 | public const int TrailingCyan = 132; 550 | /// 551 | /// (!)黄色粒子,自发光,重力下曳尾。 552 | /// 553 | public const int TrailingYellow = 133; 554 | /// 555 | /// (!)粉色粒子,自发光,重力下曳尾。 556 | /// 557 | public const int TrailingPink = 134; 558 | /// 559 | /// (!)冰火把,青色粒子, 自发光。 560 | /// 561 | public const int IceTorch = 135; 562 | /// 563 | /// 红色粒子,不发光,受重力影响。 564 | /// 565 | public const int Red = 136; 566 | /// 567 | /// 亮蓝色/青色,不发光受重力影响。 568 | /// 569 | public const int BrightCyan = 137; 570 | /// 571 | /// 亮橙色/棕色,不发光受重力影响。 572 | /// 573 | public const int BrightOrange = 138; 574 | /// 575 | /// 青色纸屑,不发光,受重力缓慢下落。 576 | /// 577 | public const int CyanConfetti = 139; 578 | /// 579 | /// 绿色纸屑,不发光,受重力缓慢下落。 580 | /// 581 | public const int GreenConfetti = 140; 582 | /// 583 | /// 粉色纸屑,不发光,受重力缓慢下落。 584 | /// 585 | public const int PinkConfetti = 141; 586 | /// 587 | /// 黄色纸屑,不发光,受重力缓慢下落。 588 | /// 589 | public const int YellowConfetti = 142; 590 | /// 591 | /// 浅灰色石块粒子,不发光,受重力影响。 592 | /// 593 | public const int LightGreyStone = 143; 594 | /// 595 | /// 铜红色铜砖粒子,不发光,受重力影响。 596 | /// 597 | public const int CopperStone = 144; 598 | /// 599 | /// 粉色石块粒子,不发光,受重力影响。 600 | /// 601 | public const int PinkStone = 145; 602 | /// 603 | /// 钛金砖粒子,不发光,受重力影响。 604 | /// 605 | public const int GreenBrown = 146; 606 | /// 607 | /// 橙色粒子,不发光,受重力影响。 608 | /// 609 | public const int OrangeFx2 = 147; 610 | /// 611 | /// 饱和红色粒子,不发光,受重力影响。 612 | /// 613 | public const int RedDesaturated = 148; 614 | /// 615 | /// 白色粒子,不发光,受重力影响。 616 | /// 617 | public const int White = 149; 618 | /// 619 | /// 黑色/黄色/蓝白粒子,不发光,受重力影响。 620 | /// 621 | public const int BlackYellowBluishwhite = 150; 622 | /// 623 | /// 薄白色材质,不发光,受重力影响。 624 | /// 625 | public const int ThinWhite = 151; 626 | /// 627 | /// 亮橙色粒子,不发光,有重力直接消失,无重力变小到一定程度下落消失。 628 | /// 629 | public const int OrangeBubble = 152; 630 | /// 631 | /// 亮橙色粒子,不发光,受重力影响。 632 | /// 633 | public const int OrangeBubbleMaterial = 153; 634 | /// 635 | /// 薄苍白蓝色粒子,不发光,受重力影响。 636 | /// 637 | public const int BlueThin = 154; 638 | /// 639 | /// 薄暗棕色粒子,不发光,受重力影响。 640 | /// 641 | public const int DarkBrown = 155; 642 | /// 643 | /// (!)亮蓝色/白色粒子,发苍白/蓝色光,高亮显示,受重力影响。 644 | /// 645 | public const int BlueWhiteBubble = 156; 646 | /// 647 | /// (.)薄绿色粒子,不发光,高亮,闪烁,受重力影响。 648 | /// 649 | public const int GreenFx = 157; 650 | /// 651 | /// (!)橙色火焰粒子,发橙色光,受重力影响。 652 | /// 653 | public const int OrangeFire1 = 158; 654 | /// 655 | /// (!)黄色/白色粒子,发黄色光,不受重力影响,无重力消失快。 656 | /// 657 | public const int YellowFx = 159; 658 | /// 659 | /// (!)青色粒子,发亮青色光,不受重力影响,消失快。 660 | /// 661 | public const int CyanShortFx = 160; 662 | /// 663 | /// 青色粒子,不发光,受重力影响。 664 | /// 665 | public const int CyanMaterial = 161; 666 | /// 667 | /// (!)橙色火焰粒子,发橙色光,不受重力影响,消失快。 668 | /// 669 | public const int OrangeShortFx = 162; 670 | /// 671 | /// (.)亮绿色粒子,不发光,高亮,受重力影响。 672 | /// 673 | public const int BrightGreen = 163; 674 | /// 675 | /// (!)粉色粒子,发桃红色光,不受重力影响,无重力消失快。 676 | /// 677 | public const int PinkFx = 164; 678 | /// 679 | /// 白色/蓝色粒子,不发光,受重力影响。 680 | /// 681 | public const int WhiteBlueBubble = 165; 682 | /// 683 | /// 薄亮粉色粒子,不发光,受重力影响。 684 | /// 685 | public const int PinkThinBright = 166; 686 | /// 687 | /// 薄绿色粒子,不发光,受重力影响。 688 | /// 689 | public const int ThinGreen = 167; 690 | /// 691 | /// (!)亮粉色粒子,发桃红色光,无重力变大。 692 | /// 693 | public const int PinkBrightBubble = 168; 694 | /// 695 | /// (!)发光黄色粒子, 发深黄色光,受重力影响,消失快。 696 | /// 697 | public const int YellowFx1 = 169; 698 | /// 699 | /// (!)薄橙色特效, 发微弱白色光,受重力影响。 700 | /// 701 | public const int Ichor = 170; 702 | /// 703 | /// 亮紫色粒子,不发光,受重力影响。 704 | /// 705 | public const int PurpleBubble = 171; 706 | /// 707 | /// (!)浅蓝色,发极弱蓝光,高亮,受重力影响。 708 | /// 709 | public const int BlueParticle = 172; 710 | /// 711 | /// (!)亮紫色粒子,发紫色光,不受重力影响,消失快。 712 | /// 713 | public const int PurpleShortFx = 173; 714 | /// 715 | /// (!)亮橙色粒子,发橙红色光,受重力影响。 716 | /// 717 | public const int OrangeFire2 = 174; 718 | /// 719 | /// (.)白色粒子,不发光,高亮,不受重力影响,消失快。 720 | /// 721 | public const int WhiteShortFx = 175; 722 | /// 723 | /// 浅蓝色粒子,不发光,受重力影响。 724 | /// 725 | public const int LightBlueParticle = 176; 726 | /// 727 | /// 浅粉色粒子,不发光,受重力影响。 728 | /// 729 | public const int LightPinkParticle = 177; 730 | /// 731 | /// 浅绿色粒子,不发光,受重力影响。 732 | /// 733 | public const int LightGreenParticle = 178; 734 | /// 735 | /// 浅紫色粒子,不发光,受重力影响。 736 | /// 737 | public const int LightPurpleParticle = 179; 738 | /// 739 | /// (.)发光浅青粒子,不发光,高亮,受重力影响。 740 | /// 741 | public const int LightCyanParticle = 180; 742 | /// 743 | /// (.)浅青色/粉色粒子,不发光,高亮,受重力影响。 744 | /// 745 | public const int CyanPinkBubble = 181; 746 | /// 747 | /// (.)浅红色粒子,不发光,高亮,受重力影响。 748 | /// 749 | public const int RedBubble = 182; 750 | /// 751 | /// (.)半透明红色粒子,不发光,高亮,受重力影响。 752 | /// 753 | public const int RedTransBubble = 183; 754 | /// 755 | /// 枯绿色/绿灰色粒子,不发光,不受重力影响。 756 | /// 757 | public const int GreenishGreyParticle = 184; 758 | /// 759 | /// (!)浅青色粒子,发青色光,受重力影响。 760 | /// 761 | public const int CyanCrystal = 185; 762 | /// 763 | /// 暗蓝色粒子,不发光,不受重力影响。 764 | /// 765 | public const int DarkBlueSmoke = 186; 766 | /// 767 | /// (!)浅青色粒子,发青色光,受重力影响,消失快。 768 | /// 769 | public const int LightCyanParticle1 = 187; 770 | /// 771 | /// 亮绿色粒子,不发光,不受重力影响。 772 | /// 773 | public const int GreenBubble1 = 188; 774 | /// 775 | /// 薄橙色粒子,不发光,受重力影响。 776 | /// 777 | public const int OrangeMaterial = 189; 778 | /// 779 | /// 薄金色粒子,不发光,受重力影响。 780 | /// 781 | public const int GoldMaterial = 190; 782 | /// 783 | /// 黑色粒子,不发光,受重力影响。 784 | /// 785 | public const int BlackFlakes = 191; 786 | /// 787 | /// 雪白色粒子,不发光,受重力影响。 788 | /// 789 | public const int SnowMaterial = 192; 790 | /// 791 | /// 绿色粒子,不发光,受重力影响。 792 | /// 793 | public const int GreenMaterial1 = 193; 794 | /// 795 | /// 薄棕色粒子,不发光,受重力影响。 796 | /// 797 | public const int BrownMaterial = 194; 798 | /// 799 | /// 薄黑色粒子,不发光,受重力影响。 800 | /// 801 | public const int BlackMaterial2 = 195; 802 | /// 803 | /// 薄绿色粒子,不发光,受重力影响。 804 | /// 805 | public const int ThinGreen1 = 196; 806 | /// 807 | /// (.)薄亮青色粒子,不发光,高亮,受重力影响。 808 | /// 809 | public const int BrightCyanMaterial = 197; 810 | /// 811 | /// 黑色/白色粒子,不发光,受重力影响。 812 | /// 813 | public const int BlackWhiteParticle = 198; 814 | /// 815 | /// 黑色/灰色粒子,不发光,受重力影响。 816 | /// 817 | public const int PurpleBlackGrey = 199; 818 | /// 819 | /// 橙色粒子,不发光,受重力影响。 820 | /// 821 | public const int PinkParticle = 200; 822 | /// 823 | /// 浅橙色粒子,不发光,受重力影响。 824 | /// 825 | public const int LightPinkParticle1 = 201; 826 | /// 827 | /// 浅青色粒子,不发光,受重力影响。 828 | /// 829 | public const int LightCyanParticle2 = 202; 830 | /// 831 | /// 灰色粒子,不发光,受重力影响。 832 | /// 833 | public const int GreyParticle = 203; 834 | /// 835 | /// (.)白色/黄色/橙黄色粒子,不发光,高亮,受重力影响。 836 | /// 837 | public const int WhiteParticle = 204; 838 | /// 839 | /// (!)薄粉色材质粒子,几乎不发粉色光,高亮,受重力影响。 840 | /// 841 | public const int ThinPinkMaterial = 205; 842 | /// 843 | /// (!)青色粒子,发很暗的蓝色光,不受重力影响,消失快。 844 | /// 845 | public const int CyanShortFx1 = 206;//从这开始 846 | /// 847 | /// 薄棕色材质,不发光,受重力。 848 | /// 849 | public const int BrownMaterial1 = 207; 850 | /// 851 | /// 橙色石块,不发光,受重力。 852 | /// 853 | public const int OrangeStone = 208; 854 | /// 855 | /// 苍青色石块,不发光,受重力。 856 | /// 857 | public const int PaleGreenStone = 209; 858 | /// 859 | /// 白色粒子,不发光,受重力。 860 | /// 861 | public const int OffWhite = 210; 862 | /// 863 | /// 亮蓝色粒子,不发光,受重力。 864 | /// 865 | public const int BrightBlueParticle = 211; 866 | /// 867 | /// 白色粒子,不发光,受重力。 868 | /// 869 | public const int WhiteParticle1 = 212; 870 | /// 871 | /// (!)黄色粒子,发很暗的黄光,高亮,受重力影响,极微小且消失快。 872 | /// 873 | public const int WhiteShortFx1 = 213; 874 | /// 875 | /// 薄苍棕色粒子,不发光,受重力。 876 | /// 877 | public const int Thin = 214; 878 | /// 879 | /// 薄棕色粒子,不发光,受重力。 880 | /// 881 | public const int ThinKhaki = 215; 882 | /// 883 | /// 苍粉色粒子,不发光,受重力。 884 | /// 885 | public const int Pale = 216; 886 | /// 887 | /// 青色粒子,不发光,受重力。 888 | /// 889 | public const int Cyan = 217; 890 | /// 891 | /// 红色粒子,不发光,受重力。 892 | /// 893 | public const int Hot = 218; 894 | /// 895 | /// (!)红色粒子,发橙色光,重力下曳尾。 896 | /// 897 | public const int TrailingRed1 = 219; 898 | /// 899 | /// (!)亮绿色粒子,发亮绿色光,重力下曳尾。 900 | /// 901 | public const int TrailingGreen1 = 220; 902 | /// 903 | /// (!)蓝色粒子,发蓝色光,重力下曳尾。 904 | /// 905 | public const int TrailingBlue = 221; 906 | /// 907 | /// (!)黄色粒子,发黄色光,重力下曳尾。 908 | /// 909 | public const int TrailingYellow1 = 222; 910 | /// 911 | /// (.)粉色粒子,不发光,高亮,重力下曳尾。 912 | /// 913 | public const int TrailingRed2 = 223; 914 | /// 915 | /// 薄蓝色材质,不发光,受重力。 916 | /// 917 | public const int ThinBlue = 224; 918 | /// 919 | /// 橙色材质,不发光,受重力。 920 | /// 921 | public const int OrangeMaterial1 = 225; 922 | /// 923 | /// (!)亮浅蓝色粒子,发亮浅蓝色光,高亮,受重力。 924 | /// 925 | public const int ElectricCyan = 226; 926 | /// 927 | /// 亮紫色粒子,不发光,受重力影响,高速旋转,无重力消散较快 928 | /// 929 | public const int PurpleParticle1 = 227; 930 | /// 931 | /// (!)亮黄色+白色粒子,发黄色光,受重力影响 932 | /// 933 | public const int YellowGoldenFire = 228; 934 | /// 935 | /// (!)发光月炎 火焰!!! 重力下高速旋转 936 | /// 937 | public const int CyanLunarFire = 229; 938 | /// 939 | /// (!)类似月炎,但是照明范围较大,重力下高速旋转 940 | /// 941 | public const int CyanLunarFire1 = 230; 942 | /// 943 | /// (!)橙红色粒子,发橙色光,重力下消散时间较长 944 | /// 945 | public const int OrangeFx3 = 231; 946 | /// 947 | /// 屎黄色粒子,不发光,受重力影响 948 | /// 949 | public const int YellowMaterial1 = 232; 950 | /// 951 | /// 黄色+屎黄色粒子,不发光,受重力影响 952 | /// 953 | public const int YellowMaterial2 = 233; 954 | /// 955 | /// (!)淡紫色粒子,发出紫色光,受重力影响 956 | /// 957 | public const int PurpleHighFx = 234; 958 | /// 959 | /// (.)血红色高亮粒子,半透明,不受重力影响 960 | /// 961 | public const int RedBlood1 = 234; 962 | /// 963 | /// 银白色粒子,不发光,受重力影响 964 | /// 965 | public const int SilverMaterial = 234; 966 | } 967 | } 968 | -------------------------------------------------------------------------------- /Utils/PriorityQueue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace TemplateMod2.Utils { 8 | /// 9 | /// 优先队列,使用小根堆实现,Pop,Push复杂度保证O(log n) 10 | /// 11 | /// 12 | public class PriorityQueue where T : IComparable { 13 | private T[] heap; 14 | private int tot; 15 | public PriorityQueue(int sz) { 16 | tot = 0; 17 | heap = new T[sz * 4]; 18 | } 19 | public bool Empty { get => tot == 0; } 20 | 21 | /// 22 | /// 将元素放入小根堆 23 | /// 24 | /// 25 | public void Push(T val) { 26 | heap[++tot] = val; 27 | swim(); 28 | } 29 | 30 | /// 31 | /// 获取堆顶值 32 | /// 33 | public T Top => heap[1]; 34 | 35 | /// 36 | /// 获取并弹出堆顶上的最小值 37 | /// 38 | /// 39 | public T Pop() { 40 | T ret = heap[1]; 41 | swap(1, tot--); 42 | sink(); 43 | return ret; 44 | } 45 | private void swap(int i, int j) { 46 | T tmp = heap[i]; 47 | heap[i] = heap[j]; 48 | heap[j] = tmp; 49 | } 50 | private void swim() { 51 | int k = tot; 52 | while (k > 1 && heap[k >> 1].CompareTo(heap[k]) > 0) { 53 | swap(k >> 1, k); 54 | k >>= 1; 55 | } 56 | } 57 | private void sink() { 58 | int k = 1; 59 | while ((k << 1) <= tot) { 60 | int j = k << 1; 61 | if (heap[k << 1].CompareTo(heap[k << 1 | 1]) > 0) 62 | j++; 63 | if (heap[k].CompareTo(heap[j]) <= 0) break; 64 | swap(k, j); 65 | k = j; 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /build.txt: -------------------------------------------------------------------------------- 1 | displayName = MOD模板第二版(教学用) 2 | author = 小裙子/DXTsT 3 | version = 1.1 4 | hideResources = false 5 | hideCode = false 6 | includeSource = true 7 | includePDB = true 8 | homepage = http://fs49.org/ 9 | buildIgnore = *.csproj, *.sln, obj\*, bin\*, *.suo, *.user, .git\*, .vs\*, Libraries\* -------------------------------------------------------------------------------- /description.txt: -------------------------------------------------------------------------------- 1 | TemplateMod2 是裙中世界Mod教程的附带Mod,Mod制作者们可以用这个Mod的源代码作为起点,不断完善属于自己的Mod。 2 | 该Mod的后续更改将会发在Github上,敬请留意。 -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CXUtk/TemplateMod2/bf61eca2af1ec972fa83c6731b53b209c9bd56a8/icon.png --------------------------------------------------------------------------------