From e6d8b4b8d8f8a9a882f4fd5d330f935e325bbf9c Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Mon, 9 Sep 2019 19:11:40 +0000 Subject: [PATCH] Exponential interpolation for linear damping With an additional approximation to allow for the same size jumps given different framerates. --- Cargo.lock | 7 +- .../element/skillbar/stamina_wheel-0.vox | Bin 0 -> 4236 bytes .../element/skillbar/stamina_wheel-1.vox | Bin 0 -> 4240 bytes .../element/skillbar/stamina_wheel-2.vox | Bin 0 -> 4236 bytes .../element/skillbar/stamina_wheel-3.vox | Bin 0 -> 4240 bytes .../element/skillbar/stamina_wheel-4.vox | Bin 0 -> 4236 bytes .../element/skillbar/stamina_wheel-5.vox | Bin 0 -> 4272 bytes .../element/skillbar/stamina_wheel-6.vox | Bin 0 -> 4288 bytes .../element/skillbar/stamina_wheel-7.vox | Bin 0 -> 4200 bytes .../element/skillbar/stamina_wheel-empty.vox | Bin 0 -> 58684 bytes assets/voxygen/shaders/include/sky.glsl | 2 +- assets/voxygen/shaders/postprocess-frag.glsl | 8 + client/src/lib.rs | 8 + common/Cargo.toml | 2 +- common/src/comp/character_state.rs | 4 +- common/src/comp/controller.rs | 43 +++++ common/src/comp/mod.rs | 2 +- common/src/comp/phys.rs | 2 + common/src/event.rs | 16 +- common/src/msg/ecs_packet.rs | 4 + common/src/state.rs | 94 ++++++++++- common/src/sys/agent.rs | 41 ++++- common/src/sys/cleanup.rs | 8 +- common/src/sys/controller.rs | 53 +++++- common/src/sys/mod.rs | 10 +- common/src/sys/movement.rs | 97 ++++++++++- common/src/sys/phys.rs | 109 ++++++++---- server/src/cmd.rs | 1 + server/src/lib.rs | 47 ++++++ voxygen/src/anim/character/attack.rs | 1 + voxygen/src/anim/character/block.rs | 1 + voxygen/src/anim/character/blockidle.rs | 1 + voxygen/src/anim/character/cidle.rs | 1 + voxygen/src/anim/character/climb.rs | 108 ++++++++++++ voxygen/src/anim/character/crun.rs | 1 + voxygen/src/anim/character/gliding.rs | 35 +++- voxygen/src/anim/character/idle.rs | 1 + voxygen/src/anim/character/jump.rs | 1 + voxygen/src/anim/character/mod.rs | 6 + voxygen/src/anim/character/roll.rs | 1 + voxygen/src/anim/character/run.rs | 52 ++++-- voxygen/src/anim/character/sit.rs | 125 ++++++++++++++ voxygen/src/anim/character/stand.rs | 19 +-- voxygen/src/anim/character/swim.rs | 130 +++++++++++++++ voxygen/src/anim/character/wield.rs | 1 + voxygen/src/anim/mod.rs | 1 + voxygen/src/anim/quadruped/idle.rs | 1 + voxygen/src/anim/quadruped/jump.rs | 1 + voxygen/src/anim/quadruped/run.rs | 1 + voxygen/src/anim/quadrupedmedium/idle.rs | 1 + voxygen/src/anim/quadrupedmedium/jump.rs | 1 + voxygen/src/anim/quadrupedmedium/run.rs | 1 + voxygen/src/hud/img_ids.rs | 9 + voxygen/src/hud/skillbar.rs | 33 +++- voxygen/src/menu/char_selection/scene.rs | 3 + voxygen/src/scene/figure/mod.rs | 156 +++++++++++++----- voxygen/src/session.rs | 57 ++++++- voxygen/src/settings.rs | 10 ++ voxygen/src/window.rs | 20 +++ 59 files changed, 1181 insertions(+), 156 deletions(-) create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-0.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-1.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-2.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-3.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-4.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-5.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-6.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-7.vox create mode 100644 assets/voxygen/element/skillbar/stamina_wheel-empty.vox create mode 100644 voxygen/src/anim/character/climb.rs create mode 100644 voxygen/src/anim/character/sit.rs create mode 100644 voxygen/src/anim/character/swim.rs diff --git a/Cargo.lock b/Cargo.lock index dfa9de1f42..f21a6f5bfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3197,11 +3197,10 @@ dependencies = [ [[package]] name = "sphynx" version = "0.1.0" -source = "git+https://gitlab.com/veloren/sphynx.git?rev=11cdc7422568aaabd376c87242a60f636e68b40d#11cdc7422568aaabd376c87242a60f636e68b40d" +source = "git+https://gitlab.com/veloren/sphynx.git?rev=ac4adf54d181339a789907acd27f61fe81daa455#ac4adf54d181339a789907acd27f61fe81daa455" dependencies = [ "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", - "shred 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", "sum_type 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3601,7 +3600,7 @@ dependencies = [ "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", "specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git)", - "sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=11cdc7422568aaabd376c87242a60f636e68b40d)", + "sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=ac4adf54d181339a789907acd27f61fe81daa455)", "vek 0.9.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4258,7 +4257,7 @@ dependencies = [ "checksum smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" "checksum specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)" = "de65613ada4338aa7ba71eca60eca24c60483433eec0077bc4f33cfc31f4bdf0" "checksum specs-idvs 0.1.0 (git+https://gitlab.com/veloren/specs-idvs.git)" = "" -"checksum sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=11cdc7422568aaabd376c87242a60f636e68b40d)" = "" +"checksum sphynx 0.1.0 (git+https://gitlab.com/veloren/sphynx.git?rev=ac4adf54d181339a789907acd27f61fe81daa455)" = "" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" diff --git a/assets/voxygen/element/skillbar/stamina_wheel-0.vox b/assets/voxygen/element/skillbar/stamina_wheel-0.vox new file mode 100644 index 0000000000000000000000000000000000000000..8822a5da40dfc8abce941475bbc7c41905be1a27 GIT binary patch literal 4236 zcmds)-HVmi6~_18@7MYMp7Zs7pL2%5D48~9(qs&%O{N{SR!y9mIgXAFMHqX)!#k@uhN9x_P{gyUF&)F+H0-7 z-WTR4M_aB)SuO6JPRjk2p5ty&m*EXjI*S{i~j#7b&W(5__3Od|d z(9yJ|vsp{0*zu`?PPTlVZnsoAtLc2JpwrVeJ=&`2@pe@wn zYiOG6YtGlv={nD4ol{dfZK-@#p4SjR=wE*&mGVP3HQ*W zo&t5pV5Ej1oJ~}V*sa+_w`ODZj8z{DW)68wICDZC6LOfSF6TtV?GiWP`~m$NvG$3d zkjs#>67mV@*#O*En>FF z-?KAB%|+@hfiksLKo!*BU1|=#y~!+~UPFF{+%y_Z&YJuc=P1rroU5p- zOfCUyoihvY1$0fGbNog2H+U`)BO+dj`V2K0YH@VLU6T)}f)K7t4;DZNw82Do0e#f; zQ@3Y5bbT-YIr|kp1*gy`v`zLjKpoUnK;Hst4cObD?gDq{(5D7{s9F6BXN-P}c{YFfWRp#WR;V^Y}*kDx;^ouOQ($#h1CQ$tJ*5%q*RBwj@9nAkCAC7hLTe#-eN=co8ma&yVi zB|n#Ehn%hLkgN4M_zbnU_$}wb8DhBb4!nWa)a#;gfQQDQ(VXF;vvCYMOG zyXYKX`)<%_?#@N$q4Ck!J8;oCz(Z%yS&fIrM`Q2DMdJVujR8J$;Gyx+1ZeF2x#%3= zp)}$fc;M#Bw$XIZ*cg3y z`)#wD0h%EiTWbf+0va1*fM$qhfM$p$L}P1SK+~m9ZU<3wm;xIWx~ySMxReu!^?Z-{SzZ-_6%4zY*WA$G*< zSw6&$uw!h?N7ym*9AZbg}`D<~)!1o@CfoJL3D3VOxI0_bS8Au}6I4GVC1N z>c`kQw$$-h2T^6$@-$ln%3 z{;^Bs%l+aOT-kkMx9s`p9$9^JRSx{(fULj2F30X2lan8xl=F|y%hk`WO8U}aSv)?K z-DlpAXD+@a&%b?3_TBnW*6u!#!@qkh)8BtCZ~Wm;vO_HohC}%z4CT`}mcP>Hzk`2{ z$MX5%!!lexF5$88308Ji zv{&2GU++t@nMi&ve^TSK_nw{KZ?>Ju$Iq`+O Tm;U+5zW?QcH~hcK?c4t!0@d?h literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-1.vox b/assets/voxygen/element/skillbar/stamina_wheel-1.vox new file mode 100644 index 0000000000000000000000000000000000000000..c756f9ebd8dd4e18792d22ffc1bfe320ed902c05 GIT binary patch literal 4240 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 IeE!@2A4n{Iw*UYD literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-3.vox b/assets/voxygen/element/skillbar/stamina_wheel-3.vox new file mode 100644 index 0000000000000000000000000000000000000000..d97dd3f9c4d08ac8cb0977ab472bce4e7f9d6085 GIT binary patch literal 4240 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 E4;?zXwg3PC literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-4.vox b/assets/voxygen/element/skillbar/stamina_wheel-4.vox new file mode 100644 index 0000000000000000000000000000000000000000..0f3899ac6fefb5ab355e4c4559148bff5250577d GIT binary patch literal 4236 zcmds)Tc}*+7017|XJ*fyz2~y`p8LMd?Hq(Crd5yCgOH?p+FY6@UQ*2|7%5ROvFK%jU_K5=~e!2T~CLC%qy^dwOFE#ta3!0TCB5lNkNDyw66a=FvPl`cAk9fvtVLIH^RDD)T`5ev zV#5{LzcK0H>)`9)>)`9)>#Tfr*7v8b^l!K_SQI1$+4VqrH*?atk(WBYl$gPdvJ4k_ zX-sp{o(HVSOLtx(r^J-Tmel8=8H{^kUw0+B>5->LZXUUnBn8Q&B=OBa2D6+*3z7Q5 zm*G5VjSToY+5Qsazk~g%S|W=Uz81a~ zz81dL$~RgxMdnShW=+XWo06Y2r9hwJMFYM8-+*txH{e6*jq4(_x>(aXd>y{N;``%J z24FbOOM8}=&ODUS*p}WRC*2z&E&9{s9^!e98L%Z=*pgninKK*CmS|>6cWO%qv?n6| zqAJA-;RmxSHRYr}^Wj1>8vAgzv?fLAFN&NQn*O*b*}N#}tSHIV zyhJyO5~D}cqI8MtOsd?2LwtpE@{({rF?w&3lP34sptr+uh5akcNlwD+A_4y4qQpIv zxQCMTZj`u-67yb?{@mprT<*c;9$e99TY;mzY?y?Lj#8l~dp1Ub{r@}Mwxh;xW@=&{4R zJL1yo0sYT`JnytHt1#0QX1c;mSD5JvGwn#k-3oJJF*7-4B|oj=tFC-8??&b|=DNoG z)|lTK^Xu@IeRv<xrX{Yaw#Hfoax|qhzrmM@Fmv!=q`IC z?B`OSMNJm9mXuqHlrJKSN8KKb;if~+CS$N=e)Pm!M4z|gk>b>zt3B6o`#h4sQ-!oG&S z+0Fuka`emvCHhqXW#Cbx(60dapa!b!>#$FieQeJ2K$-h?mKww?$+biL8u_e|&xl^MvFn`YgBtz@(xr~}s+WeDCl~Tf$TOw3F8LUtCi-jEOKnprv^yKal{^V^qBY&d-=2#I255p7s7oKakKM=aV-K(g*aPeV_7HoBJ;WYjkFZDBBkUzI`kz@H&|T?0 z^d5Q-zK`BV@1qaU2j~OzA^H$~$Q>tE3=)un3`BgZVvv9oWFTVi7$hJC8HhL|1_?+( z20FxcSMhy(ePRa40Wp1I`c_XWRv!$&5cH&qUB#|qSNWz^;c9R-xEfpyt_J7B`EWj* z59h-LZ~vpTTh%gp(jtC)KjNU>GRJ&uV>Dj z(X(gI>T9pPrfYY>ErtLsV7zcV)Qp>p3&c)eNKP% zQ==dL!sw^(8vX2jqyPHI=)XTUs()*${^NGl&-QBG$?F}D-Jy5?`0e4p-r zd%qsNa8w`v;BkHW^3(d#$1iE`!9%+K@V4G@;#s}x^vinRD{twZx8BpO^Y81S-(At| z-+!Xd{^3u$LoM$|k$xD*`cazdU+MGT!9PbM{p8Rgjdnk*@zMAeHg-1j!B-#DoyW-W z1#@ZyUv>gm&`zb@qe&KGWkTHmZ|XMabt1}6j(_OFb(mUU}Q@u0rcMh literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-5.vox b/assets/voxygen/element/skillbar/stamina_wheel-5.vox new file mode 100644 index 0000000000000000000000000000000000000000..25954e274c312eecbe1470b9044c46c19a5da0da GIT binary patch literal 4272 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 literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/skillbar/stamina_wheel-6.vox b/assets/voxygen/element/skillbar/stamina_wheel-6.vox new file mode 100644 index 0000000000000000000000000000000000000000..52cabb5fdfa30059286c667c6bf26e99d3668ce1 GIT binary patch literal 4288 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ⅅ8M7y5Pb@{=OS4$Ypn^?x z8i~D(#92hxM)WU|{AZCAcrNl>LSLSarLzpN3$Y8a3$Y97aVUiwp%kYz87%_(9MI>0 zKKI00^d!e>&wAoad*belrMv8k0Bg}j*G1Pw*F{&8;k+ZV=!nIdn{~vVcEmxR&f3V^ z$lJ)<$lJT}WTz!EZ;3T)A#WjXA+K|0b#f7D(WjVxMV$R`=}NL3OBK{O@8;*JG(Q_l z^9xsEbkR*0p1Bf$!Q2&p=1L#*;6Vx>5;zpkM&jdpIIojaU6P#!u^Pn6No(Ff-atN* zKK{B>Ptv7F%#rk1D+>`Kz4VOI$cpl6=@v-YqN77y7u^&liVN1BQCE&S@-yZ10 z$<|^lbz&tuIruK{eIU`az&R8+hk=AU1QeG8;-du0pu&3<)aE((o`df;{I%h`O&lBk+VI_m?>2n5;kymrZTN1(cL%;Z*gJ67 zfx8a&4t#guy93`H`0l`W2fjP--G%QiJ#pc?%O00KE_`?4y9?i4`0m1Y4<38)*u&PN ze;$1I;JXLkJ^1dycb_xQW0%J+k6j+SJU!0C_dI;}C8poGWr02y=yL^5SKxF7PFLV` z1y1`C?Ns4=6@2@G~jy! zzBiCJkT-VaF}X$L7Lr>)ZUbgdIxE0=f$sv}E%X>Rj}<-YYNX& z=`p`z_D67Gz`XL2bvd^-=rD^q)Rs_N%-u_D(6z8{u`eL+7Czee3bpeF&n$q_%GcOiYu z(StI*DASWNJt|}0l<^nk-8-o0ulPo7qH~*_b_J(+7T*BaaC&C*(09hl%MDC&X_TzcKOstRN%S zKK^5J84xQbpCGHsBpaYJ_;gJzGx#6LAj`48OAaFmkk>&2tZX`&P^(Kkp;nieLajM! zZQ`>@{th+gskuPCMNp#FGN^zmdYhU(@~`n+2Myp-bCX;=>b1zvA~(TC$XUqW5YG_H z5Z_Rj2lD9htJ<(HVqYfbAt-?=xmQ4lzasTn)MQahPS6YafC>oEb&1;nZO{S}=>qyF z^ix_{54%3_!I1q1nL%f;G1xZPQwKHBpa)&!QCG~~IyG{>k|Wf-yY|khZh!nb>J@hkWHipXL9s3kE{Y0iu6t3U=7Y#ycgh* z1=j=~mf&3jA4T$V;joQeo!$vN#s_td(6z|5kNpJQ1V3YX(c`_vo;s+ZZ?9I+BZn?E zrqn$l?~s}zYV*m*2sP2)5Z{J8i{vw)rZ)R6@{F+W;KL%v4nEo-B*!s6T095XkMUJs z<+EE;gL)fq!Eny>n{|Z00(M5K=&GEvfjdI}dH7=JS(MqFuVWJKLCV>3UqQ@sf-FTh z#3n}_Hn}*)!Oq4mhg}Xk8#@O(7dtO|a@5xYN4l0?_m)^yLFJ-T=AUR_^b z*Nu%0-M4R_ZfPe%2+%mfVuG{r{_wLoB8+YrY`@W;69(q8Z zJh)$R*ih_WaZOy9>|iuYYRvgI^l` zt<;F|~a_~qmJ z*!z#^Q`er-7e9JYlLwFJ+QVCV$LVMEu5&-v`(AoO_r39sZeDs%kGy+bw|@VzKJ$k^ z={B{zHyG##L7*Q-k^YrF{~i2uJl2no9MQqvhc!4J+`{_yx<2^wgSvf!9G@q*UzKj@ zR(7l3-k#B3(P&yX8di;#N+qpSD!aO``m+}Hwb^W{@B3e+{zloMJrlh*9_SA?@6*i( zzWt5%UuW>b3oq!obLYO!gRma~pyY|sC`y$DtIF3ECt=JJMwv)K=6332XH?d1Ik*$#|TheGoa$=j% zgpwxU1_H$it)aM-1X2Sr1vi1Pq@+-m7Rna(wNRF_FJ*_z_uP>cZ~5fIpPXkOzjx31 z-E+_P-utd4&F5pkbNBtFXC24cwPpL=+?=<4!Ex^0esF7>G`Ln^-qQX40|&PcF9o8$ z<0Rc7uOExLS+AFSuUqm4+(FuTtl)*+qSr^e!!6S;a+l{Jubo&Ni=vO?kK210f6)us ze$qGZCDC``i?}(jjdnZjKKe({v{@@MW|L9QiheOP5uUqj z&LQq)a+K&RW0c4vwH3T>`ow4_XvfLdW_1!P5gWqfKInDwoFLYLzK?qXf1Gg>^huB> zfxjJJJF!mm-S#eZ7HNx>ys{f@q?}x%;Pf_z+yU-=+*90>+|!M+n{O1|Vk7628(C+t zk-^f=5PgOk16aQ^+$gz)M&21{^zqz_C9#5ABA#zV-Et$34LTVt+Zc9w$lcQzc6%B_ z^ciweSi0d;$1r0KQ^znh3_JadlOu0Gc?*o6Zp7R{?kVyYs3pT#1?tH*I^5w#hL}Un zFu5IU(9JY_`u9`Apo@lkJJyCT%or}LVa9P`4KuC_s}E}rIeVx-1alPT7`$;T0c#u9 zj&%?ZV2)6KC(m72Hx`Duhgu@=derApn~TP!W|#WetynMChxL;)M~;4S6c{UwrN~og zl<~*t-^FvB969pE;qzefVDa5tBjHjH){bR~^)qfC){7;vVYeS+9$n_sO*RJ5rLZ(s zroV&FA?BcQ(00?O3+u!>oCxz4fi*(kF1Vwtq0c;ZF;DH>JGr-U@8+JMo(OXm#vJA? z1cy)k9<_Tgy7YI+<&xK7-oh}q!|cH4!0Rw?0rTcCZ$5Jt$70M^JJyCJV04+UZmbLI z#5%|uB~J%=!i<-|+F0)>`@&&$Ke>T zbLsGoPz&|us51{+ntB`;CgwLsZihPK)RUo4Fa160%%krkk4KGt%u_FxqsAd}BzexF zA0k(m)MJ^t;q7KGIIJ`C&3%x3F?5dGL9ByycGx>E^+(w+4s(`o1njerQ)E3$%qj0H zR^YjauVnXQ9$f@o7+nZmfX+wfqYKc5(1n@f2=kQTj7wo@&d+{qfb+By>%zLR9xRFV zVtvHhu@3YJtc~+H&iNGM9E);(ML7E+oL^zixG?8ch;uT;ITrGIuq4)t^M2rBk$MW$T%hJW40#xG@MJm1Gn}g_ z&eb&5&)GVFB{@TTu|BLH>v98X45%;Q*{5b{`_w8vAD;(HfL~%EVjejH;y!VYxC?K9 z#>YZvJTxw21n4A>ht6eQ0(3qWLg%4#sUbk;qw~oU&) zIv)$6^Dvpi0G*G?yn1L|)+9jdV^hxvX7)E`%nGM%Exe=VKvs9y-y4 z(1g**`UGfvEQH3x!t8?(nlPFO8d<*posWgkd6;OzXd-B$tWAK%$3ker>?eswh)0RX zSib;Gh1;j$c!tA{wu>!HYjm3#2h_!ho{6+i){CV^~{s4c7y;{Ow#9zSg z;rHm??&b`t9%)PfN6aS{!UEpk zA>xC?%fw6ccZqw%e0v@Zph=+-z8*A5G`(p0(F~wTktc;FjYb%gXnN5|jude@+eDK_ zlR+b_y=eN-NRBj`44O2W44Nz&VeUiI&ph^{lX0?WGH9}Ba%lR{^rIO-Gk`|UQOT1< zlS7k7(~o8VO$tp4O&X2l$?;B?iKU38S$m16iD&So@n!I(@n!I3X=iC?XlH5X*n1Ms z($3M&)0TLScAov5rJbXlr(K{e`aJCd??;Zd=nJ%qv_)T_UF4n1(-wV^c8Rv=i?mC; z%LUq^m#>Wrv_)T{U8Y^6E&4KNN0GMZ!0XI&91Ve&kP$@c;|<|NOY z<-NImd+*N4HxZXzF3)D^l4rT*?%cbFuSe{4$tQRDzC&`XYK3fD9{JY8=8$L6UD8$; z!?Y@;N@--{+`ZfO@OQSoL@(E+-12!~(~YVV!5ZE>Wa)@6#t zqFJ$Gg;}|BrCGCPjTs#sHDhCAX49rkX6x3iX2*^lX7}#hX8->E=HS7Drdq9Xdo((MQeWk3VispFVA#e)?&1=FAy$_Uu`6?%X-^+;h*F^XJc- z3l}b!ix)4N%_Ya|SmBucH#+9QierwBI_CJ8V;XisJZ=-+s(Os)HqLV?@uhpL?_pp0mlr)9aHLZOs>N*iA2J*x3}Ba_5N6q zOPQXY9+OU|uNQtx-}aTm=H^1i+_iP1*?OyeMc%T{n+clDra5-(*qen_`g=xuOmZw~ zQrl9daG+qy6Xj*j^=H@H^({S6GQ%^&=7z-^%*sbrn$=IPHWIt#gxqFAu!pZz`A9f7h0McfHm+2ke<5KN7NM=i8T@& za`ueKAyjSFk53(u1F*7s#P(X4uN`i+M{NgbSLc^Ja^zH}>&;ekoo$XU+H@81Ri`TA zyQ#8zwACDGDL1xS54Tzy>_C$<$4^KdwnuelqG=nK)5z(qhep`-(8x0Dp^*)#help( zJv4F(>7kJWSr1K26-`_fO+pooyfb>`AStZn^e(k zRzmvqKflPE|B_siN7Xiso)rG`m&N z>`_H?k1CqIs%Y+2MYB&8&3;uh?^H!|pDLRBRnZ(!MRQOU&AU|5e3dGicdMd#Kow0@ z70rXHXvS6198yJdSQX8LDw>)qnn_hOQ>ti=sG^xxMRQaY%`sIpGpcB2Rng3;qN%H* znO8-_e;B0bUm0ntqFGQyv#5&ZxGI{5RM9-FispnWnv<$%PN|~#YE?9ksG|8ARW$EW zMf0dCn#WYpe61>)_o|}#I#o1}tDnwM44{D>-=A5}&3 ziYl5PQ$_RRs%U;f70pkoqIp#n%_mjS{FEx1*HqDbN)^pdtD^ZCRWzSgMf0<&Xnsx= z&Cjc%`2|%ppHW5gSyeQ@sEX#7RMC7+70oZJqWKk7G{35f=GRow{JJWd*HzK{hANuh zR7LY!s%U;&70vIcqWN7_G{2{c=J!?6{DCT(KU78YN2+N4SQX9ZRnh#3Dw;o4Me_w! zG=HXw=Fe5p{Dmr-zf?u@MO8F^rHbaSRnh#7Dw@AlMe}#6X#QRm%|EE3`A1bW|D=lM zpH}WE)NS~)y0$ZwLl(fa}Qa^Y<;2zs#6j9G!Jrxu`A`%bCZ&zI_>jkW%UNzIF_KX zc679~WYVoVJ~uT}lg^`+(rCqI8E@9cWzfp%$_DG0m|i%xJm>}@a!KYZ2R%MF{nmp@ z-8b0;*Gn;dXr`4yh8wZu&HCc<*dJNy80CKD*yD?{7NFhUY)E@ zRWHp|E6;V8$2zB$*{{3UHt|etZoxjxH|s~O-cH_M zsqtA^n6<0d${x6%N4Z33#j@4y${d=h*Jmxk?DUD&Qnr?C;qdrOYx76wR2^TKuN`jf z4O!pn;*n-;;YfYPPU(n@@pjyDU9nYd->GK(_}qjY?r?pk-W2$am6H8!hdxqowuZKe zZm?;tdM_njJv8-362YjH_$6bHEp@qKYztbOm}-IAk(WE!JP%J#EFNhMY-j1+OIbu^ zJIgHDhpFcDW%Vy-v8%W+KYn<6&Mw5r8)=1$S9-0L@Ji3mjGtUy==UxSB*Eo@TFGjQ vizK^j+U#mC7hzM_mVmv{Iy6;1d_`;Awro9iC8f_uiC?z!;o8iM2uA)FG0wJB literal 0 HcmV?d00001 diff --git a/assets/voxygen/shaders/include/sky.glsl b/assets/voxygen/shaders/include/sky.glsl index 1310f82f87..5e63466e70 100644 --- a/assets/voxygen/shaders/include/sky.glsl +++ b/assets/voxygen/shaders/include/sky.glsl @@ -155,7 +155,7 @@ float fog(vec3 f_pos, vec3 focus_pos, uint medium) { float max_fog = 1.0; if (medium == 1u) { - mist_radius = 32.0; + mist_radius = 96.0; min_fog = 0.0; } diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index f653f1d30c..eb0d162df4 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -163,6 +163,10 @@ vec3 hsv2rgb(vec3 c) { void main() { vec2 uv = (f_pos + 1.0) * 0.5; + if (medium.x == 1u) { + uv = clamp(uv + vec2(sin(uv.y * 16.0 + tick.x), sin(uv.x * 24.0 + tick.x)) * 0.005, 0, 1); + } + vec4 fxaa_color = fxaa_apply(src_color, uv * screen_res.xy, screen_res.xy); //vec4 fxaa_color = texture(src_color, uv); @@ -173,5 +177,9 @@ void main() { vec4 final_color = fxaa_color; //vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a); + if (medium.x == 1u) { + final_color *= vec4(0.2, 0.2, 0.8, 1.0); + } + tgt_color = vec4(final_color.rgb, 1); } diff --git a/client/src/lib.rs b/client/src/lib.rs index fb1aeb03ed..870cb96670 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -194,6 +194,14 @@ impl Client { } } + pub fn is_mounted(&self) -> bool { + self.state + .ecs() + .read_storage::() + .get(self.entity) + .is_some() + } + pub fn view_distance(&self) -> Option { self.view_distance } diff --git a/common/Cargo.toml b/common/Cargo.toml index 99cb28de05..32410710a1 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Joshua Barretto ", "Maciej Ćwięka , pub look_dir: Vec3, + pub sit: bool, pub jump: bool, pub roll: bool, pub glide: bool, + pub climb: bool, + pub climb_down: bool, + pub wall_leap: bool, pub respawn: bool, + pub events: Vec, +} + +impl Controller { + pub fn reset(&mut self) { + *self = Self::default(); + } + + pub fn clear_events(&mut self) { + self.events.clear(); + } + + pub fn push_event(&mut self, event: ControlEvent) { + self.events.push(event); + } } impl Component for Controller { type Storage = FlaggedStorage>; } + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum MountState { + Unmounted, + MountedBy(Uid), +} + +impl Component for MountState { + type Storage = FlaggedStorage>; +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Mounting(pub Uid); + +impl Component for Mounting { + type Storage = FlaggedStorage>; +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 450b67b271..b6e1d4dcbe 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -16,7 +16,7 @@ pub use admin::Admin; pub use agent::Agent; pub use body::{humanoid, object, quadruped, quadruped_medium, Body}; pub use character_state::{ActionState, CharacterState, MovementState}; -pub use controller::Controller; +pub use controller::{ControlEvent, Controller, MountState, Mounting}; pub use inputs::CanBuild; pub use inventory::{item, Inventory, InventoryUpdate, Item}; pub use last::Last; diff --git a/common/src/comp/phys.rs b/common/src/comp/phys.rs index f8787a633a..db6bd2447b 100644 --- a/common/src/comp/phys.rs +++ b/common/src/comp/phys.rs @@ -38,6 +38,8 @@ impl Component for Scale { #[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)] pub struct PhysicsState { pub on_ground: bool, + pub on_wall: Option>, + pub in_fluid: bool, } impl Component for PhysicsState { diff --git a/common/src/event.rs b/common/src/event.rs index e28e6e558b..4fb1bc4c09 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -6,8 +6,18 @@ use vek::*; pub enum LocalEvent { Jump(EcsEntity), - Boost { entity: EcsEntity, vel: Vec3 }, - LandOnGround { entity: EcsEntity, vel: Vec3 }, + WallLeap { + entity: EcsEntity, + wall_dir: Vec3, + }, + Boost { + entity: EcsEntity, + vel: Vec3, + }, + LandOnGround { + entity: EcsEntity, + vel: Vec3, + }, } pub enum ServerEvent { @@ -21,6 +31,8 @@ pub enum ServerEvent { }, Respawn(EcsEntity), Shoot(EcsEntity), + Mount(EcsEntity, EcsEntity), + Unmount(EcsEntity), } pub struct EventBus { diff --git a/common/src/msg/ecs_packet.rs b/common/src/msg/ecs_packet.rs index 26acad89f3..54c3591f07 100644 --- a/common/src/msg/ecs_packet.rs +++ b/common/src/msg/ecs_packet.rs @@ -27,6 +27,8 @@ sphynx::sum_type! { LightEmitter(comp::LightEmitter), Item(comp::Item), Scale(comp::Scale), + MountState(comp::MountState), + Mounting(comp::Mounting), } } // Automatically derive From for EcsCompPhantom @@ -44,6 +46,8 @@ sphynx::sum_type! { LightEmitter(PhantomData), Item(PhantomData), Scale(PhantomData), + MountState(PhantomData), + Mounting(PhantomData), } } impl sphynx::CompPacket for EcsCompPacket { diff --git a/common/src/state.rs b/common/src/state.rs index e87e6c91e3..9c5b2035c1 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -13,9 +13,10 @@ use hashbrown::{HashMap, HashSet}; use rayon::{ThreadPool, ThreadPoolBuilder}; use serde_derive::{Deserialize, Serialize}; use specs::{ + saveload::Marker, shred::{Fetch, FetchMut}, storage::{MaskedStorage as EcsMaskedStorage, Storage as EcsStorage}, - Component, DispatcherBuilder, Entity as EcsEntity, + Component, DispatcherBuilder, Entity as EcsEntity, Join, }; use sphynx; use std::{sync::Arc, time::Duration}; @@ -42,7 +43,7 @@ pub struct DeltaTime(pub f32); /// upper limit. If delta time exceeds this value, the game's physics will begin to produce time /// lag. Ideally, we'd avoid such a situation. const MAX_DELTA_TIME: f32 = 1.0; -const HUMANOID_JUMP_ACCEL: f32 = 18.0; +const HUMANOID_JUMP_ACCEL: f32 = 16.0; #[derive(Default)] pub struct BlockChange { @@ -119,6 +120,8 @@ impl State { ecs.register_synced::(); ecs.register_synced::(); ecs.register_synced::(); + ecs.register_synced::(); + ecs.register_synced::(); // Register components send from clients -> server ecs.register::(); @@ -145,7 +148,7 @@ impl State { ecs.register::(); // Register synced resources used by the ECS. - ecs.add_resource_synced(TimeOfDay(0.0)); + ecs.insert_synced(TimeOfDay(0.0)); // Register unsynced resources used by the ECS. ecs.add_resource(Time(0.0)); @@ -171,6 +174,11 @@ impl State { let _ = self.ecs.write_storage().insert(entity, comp); } + /// Delete a component attributed to a particular entity. + pub fn delete_component(&mut self, entity: EcsEntity) -> Option { + self.ecs.write_storage().remove(entity) + } + /// Read a component attributed to a particular entity. pub fn read_component_cloned(&self, entity: EcsEntity) -> Option { self.ecs.read_storage().get(entity).cloned() @@ -289,6 +297,68 @@ impl State { // Beyond a delta time of MAX_DELTA_TIME, start lagging to avoid skipping important physics events. self.ecs.write_resource::().0 = dt.as_secs_f32().min(MAX_DELTA_TIME); + // Mounted entities. We handle this here because we need access to the Uid registry and I + // forgot how to access that within a system. Anyhow, here goes. + for (entity, mount_state) in ( + &self.ecs.entities(), + &mut self.ecs.write_storage::(), + ) + .join() + { + match mount_state { + comp::MountState::Unmounted => {} + comp::MountState::MountedBy(mounter) => { + if let Some((controller, mounter)) = + self.ecs.entity_from_uid(mounter.id()).and_then(|mounter| { + self.ecs + .read_storage::() + .get(mounter) + .cloned() + .map(|x| (x, mounter)) + }) + { + let pos = self.ecs.read_storage::().get(entity).copied(); + let ori = self.ecs.read_storage::().get(entity).copied(); + let vel = self.ecs.read_storage::().get(entity).copied(); + if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) { + let _ = self + .ecs + .write_storage() + .insert(mounter, comp::Pos(pos.0 + Vec3::unit_z() * 1.0)); + let _ = self.ecs.write_storage().insert(mounter, ori); + let _ = self.ecs.write_storage().insert(mounter, vel); + } + let _ = self + .ecs + .write_storage::() + .insert(entity, controller); + } else { + *mount_state = comp::MountState::Unmounted; + } + } + } + } + + let mut to_unmount = Vec::new(); + for (entity, comp::Mounting(mountee)) in ( + &self.ecs.entities(), + &self.ecs.read_storage::(), + ) + .join() + { + if self + .ecs + .entity_from_uid(mountee.id()) + .filter(|mountee| self.ecs.is_alive(*mountee)) + .is_none() + { + to_unmount.push(entity); + } + } + for entity in to_unmount { + self.ecs.write_storage::().remove(entity); + } + // Run systems to update the world. // Create and run a dispatcher for ecs systems. let mut dispatch_builder = DispatcherBuilder::new().with_pool(self.thread_pool.clone()); @@ -316,6 +386,7 @@ impl State { let events = self.ecs.read_resource::>().recv_all(); for event in events { let mut velocities = self.ecs.write_storage::(); + let mut controllers = self.ecs.write_storage::(); match event { LocalEvent::LandOnGround { entity, vel } => { if let Some(stats) = self.ecs.write_storage::().get_mut(entity) { @@ -325,13 +396,26 @@ impl State { } } } - LocalEvent::Jump(entity) => { if let Some(vel) = velocities.get_mut(entity) { vel.0.z = HUMANOID_JUMP_ACCEL; } } - + LocalEvent::WallLeap { entity, wall_dir } => { + if let (Some(vel), Some(_controller)) = + (velocities.get_mut(entity), controllers.get_mut(entity)) + { + let hspeed = Vec2::::from(vel.0).magnitude(); + if hspeed > 0.001 && hspeed < 0.5 { + vel.0 += vel.0.normalized() + * Vec3::new(1.0, 1.0, 0.0) + * HUMANOID_JUMP_ACCEL + * 1.5 + - wall_dir * 0.03; + vel.0.z = HUMANOID_JUMP_ACCEL * 0.5; + } + } + } LocalEvent::Boost { entity, vel: extra_vel, diff --git a/common/src/sys/agent.rs b/common/src/sys/agent.rs index b98c6f077e..1d36365708 100644 --- a/common/src/sys/agent.rs +++ b/common/src/sys/agent.rs @@ -1,4 +1,6 @@ -use crate::comp::{Agent, CharacterState, Controller, MovementState::Glide, Pos, Stats}; +use crate::comp::{ + Agent, CharacterState, Controller, MountState, MovementState::Glide, Pos, Stats, +}; use rand::{seq::SliceRandom, thread_rng}; use specs::{Entities, Join, ReadStorage, System, WriteStorage}; use vek::*; @@ -13,15 +15,38 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, CharacterState>, WriteStorage<'a, Agent>, WriteStorage<'a, Controller>, + ReadStorage<'a, MountState>, ); fn run( &mut self, - (entities, positions, stats, character_states, mut agents, mut controllers): Self::SystemData, + (entities, positions, stats, character_states, mut agents, mut controllers, mount_states): Self::SystemData, ) { - for (entity, pos, agent, controller) in - (&entities, &positions, &mut agents, &mut controllers).join() + for (entity, pos, agent, controller, mount_state) in ( + &entities, + &positions, + &mut agents, + &mut controllers, + mount_states.maybe(), + ) + .join() { + // Skip mounted entities + if mount_state + .map(|ms| { + if let MountState::Unmounted = ms { + false + } else { + true + } + }) + .unwrap_or(false) + { + continue; + } + + controller.reset(); + match agent { Agent::Wanderer(bearing) => { *bearing += Vec2::new(rand::random::() - 0.5, rand::random::() - 0.5) @@ -29,7 +54,7 @@ impl<'a> System<'a> for Sys { - *bearing * 0.01 - pos.0 * 0.0002; - if bearing.magnitude_squared() != 0.0 { + if bearing.magnitude_squared() > 0.001 { controller.move_dir = bearing.normalized(); } } @@ -47,7 +72,7 @@ impl<'a> System<'a> for Sys { let dist: f32 = Vec2::from(tgt_pos - pos.0).magnitude(); controller.move_dir = if dist > 5.0 { Vec2::from(tgt_pos - pos.0).normalized() - } else if dist < 1.5 && dist > 0.0 { + } else if dist < 1.5 && dist > 0.001 { Vec2::from(pos.0 - tgt_pos).normalized() } else { Vec2::zero() @@ -82,7 +107,7 @@ impl<'a> System<'a> for Sys { let dist = Vec2::::from(target_pos.0 - pos.0).magnitude(); if target_stats.is_dead { choose_new = true; - } else if dist < MIN_ATTACK_DIST { + } else if dist < MIN_ATTACK_DIST && dist > 0.001 { // Fight (and slowly move closer) controller.move_dir = Vec2::::from(target_pos.0 - pos.0).normalized() * 0.01; @@ -109,7 +134,7 @@ impl<'a> System<'a> for Sys { * 0.1 - *bearing * 0.005; - controller.move_dir = if bearing.magnitude_squared() > 0.1 { + controller.move_dir = if bearing.magnitude_squared() > 0.001 { bearing.normalized() } else { Vec2::zero() diff --git a/common/src/sys/cleanup.rs b/common/src/sys/cleanup.rs index 5ef189ebb5..dd9f03867c 100644 --- a/common/src/sys/cleanup.rs +++ b/common/src/sys/cleanup.rs @@ -1,14 +1,12 @@ use crate::comp::Controller; -use specs::{Join, System, WriteStorage}; +use specs::{System, WriteStorage}; /// This system will allow NPCs to modify their controller pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = WriteStorage<'a, Controller>; - fn run(&mut self, mut controllers: Self::SystemData) { - for controller in (&mut controllers).join() { - *controller = Controller::default(); - } + fn run(&mut self, _controllers: Self::SystemData) { + // TODO: More stuff here } } diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index a130f2b458..23d5304dca 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -4,12 +4,16 @@ use super::{ }; use crate::{ comp::{ - item, ActionState::*, Body, CharacterState, Controller, Item, MovementState::*, - PhysicsState, Stats, Vel, + item, ActionState::*, Body, CharacterState, ControlEvent, Controller, Item, + MovementState::*, PhysicsState, Stats, Vel, }, event::{EventBus, LocalEvent, ServerEvent}, }; -use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage}; +use specs::{ + saveload::{Marker, MarkerAllocator}, + Entities, Join, Read, ReadStorage, System, WriteStorage, +}; +use sphynx::UidAllocator; use std::time::Duration; use vek::*; @@ -17,6 +21,7 @@ use vek::*; pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( + Read<'a, UidAllocator>, Entities<'a>, Read<'a, EventBus>, Read<'a, EventBus>, @@ -31,6 +36,7 @@ impl<'a> System<'a> for Sys { fn run( &mut self, ( + uid_allocator, entities, server_bus, local_bus, @@ -96,6 +102,20 @@ impl<'a> System<'a> for Sys { character.movement = Jump; } + // Sit + if controller.sit + && physics.on_ground + && character.action == Idle + && character.movement != Sit + && body.is_humanoid() + { + character.movement = Sit; + } else if character.movement == Sit + && (controller.move_dir.magnitude_squared() > 0.0 || !physics.on_ground) + { + character.movement = Run; + } + // Wield if controller.primary && character.action == Idle @@ -180,9 +200,34 @@ impl<'a> System<'a> for Sys { } // Jump - if controller.jump && physics.on_ground && vel.0.z <= 0.0 { + if controller.jump + && physics.on_ground + && vel.0.z <= 0.0 + && !character.movement.is_roll() + { local_emitter.emit(LocalEvent::Jump(entity)); } + + // Wall leap + if controller.wall_leap { + if let (Some(_wall_dir), Climb) = (physics.on_wall, character.movement) { + //local_emitter.emit(LocalEvent::WallLeap { entity, wall_dir }); + } + } + + // Process controller events + for event in std::mem::replace(&mut controller.events, Vec::new()) { + match event { + ControlEvent::Mount(mountee_uid) => { + if let Some(mountee_entity) = + uid_allocator.retrieve_entity_internal(mountee_uid.id()) + { + server_emitter.emit(ServerEvent::Mount(entity, mountee_entity)); + } + } + ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)), + } + } } } } diff --git a/common/src/sys/mod.rs b/common/src/sys/mod.rs index d177fde4b2..adeee4c759 100644 --- a/common/src/sys/mod.rs +++ b/common/src/sys/mod.rs @@ -21,9 +21,13 @@ const CLEANUP_SYS: &str = "cleanup_sys"; pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch_builder.add(agent::Sys, AGENT_SYS, &[]); dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS]); - dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS]); - dispatch_builder.add(movement::Sys, MOVEMENT_SYS, &[PHYS_SYS]); + dispatch_builder.add(movement::Sys, MOVEMENT_SYS, &[]); dispatch_builder.add(combat::Sys, COMBAT_SYS, &[CONTROLLER_SYS]); dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]); - dispatch_builder.add(cleanup::Sys, CLEANUP_SYS, &[STATS_SYS, MOVEMENT_SYS]); + dispatch_builder.add( + phys::Sys, + PHYS_SYS, + &[CONTROLLER_SYS, MOVEMENT_SYS, COMBAT_SYS, STATS_SYS], + ); + dispatch_builder.add(cleanup::Sys, CLEANUP_SYS, &[PHYS_SYS]); } diff --git a/common/src/sys/movement.rs b/common/src/sys/movement.rs index cd657704b9..393de342c8 100644 --- a/common/src/sys/movement.rs +++ b/common/src/sys/movement.rs @@ -1,12 +1,13 @@ +use super::phys::GRAVITY; use crate::{ comp::{ - ActionState::*, CharacterState, Controller, MovementState::*, Ori, PhysicsState, Pos, - Stats, Vel, + ActionState::*, CharacterState, Controller, Mounting, MovementState::*, Ori, PhysicsState, + Pos, Stats, Vel, }, state::DeltaTime, terrain::TerrainGrid, }; -use specs::{Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; +use specs::prelude::*; use std::time::Duration; use vek::*; @@ -16,13 +17,17 @@ const HUMANOID_ACCEL: f32 = 70.0; const HUMANOID_SPEED: f32 = 120.0; const HUMANOID_AIR_ACCEL: f32 = 10.0; const HUMANOID_AIR_SPEED: f32 = 100.0; +const HUMANOID_WATER_ACCEL: f32 = 70.0; +const HUMANOID_WATER_SPEED: f32 = 120.0; +const HUMANOID_CLIMB_ACCEL: f32 = 5.0; const ROLL_SPEED: f32 = 13.0; const GLIDE_ACCEL: f32 = 15.0; const GLIDE_SPEED: f32 = 45.0; const BLOCK_ACCEL: f32 = 30.0; const BLOCK_SPEED: f32 = 75.0; // Gravity is 9.81 * 4, so this makes gravity equal to .15 -const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95; +const GLIDE_ANTIGRAV: f32 = GRAVITY * 0.96; +const CLIMB_SPEED: f32 = 5.0; pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; @@ -30,6 +35,7 @@ pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; pub struct Sys; impl<'a> System<'a> for Sys { type SystemData = ( + Entities<'a>, ReadExpect<'a, TerrainGrid>, Read<'a, DeltaTime>, ReadStorage<'a, Stats>, @@ -39,11 +45,13 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, + ReadStorage<'a, Mounting>, ); fn run( &mut self, ( + entities, _terrain, dt, stats, @@ -53,10 +61,22 @@ impl<'a> System<'a> for Sys { mut positions, mut velocities, mut orientations, + mountings, ): Self::SystemData, ) { // Apply movement inputs - for (stats, controller, physics, mut character, mut _pos, mut vel, mut ori) in ( + for ( + _entity, + stats, + controller, + physics, + mut character, + mut _pos, + mut vel, + mut ori, + mounting, + ) in ( + &entities, &stats, &controllers, &physics_states, @@ -64,6 +84,7 @@ impl<'a> System<'a> for Sys { &mut positions, &mut velocities, &mut orientations, + mountings.maybe(), ) .join() { @@ -71,6 +92,11 @@ impl<'a> System<'a> for Sys { continue; } + if mounting.is_some() { + character.movement = Sit; + continue; + } + if character.movement.is_roll() { vel.0 = Vec3::new(0.0, 0.0, vel.0.z) + controller @@ -94,6 +120,9 @@ impl<'a> System<'a> for Sys { (true, Run) if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) => { HUMANOID_ACCEL } + (false, Climb) if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) => { + HUMANOID_CLIMB_ACCEL + } (false, Glide) if vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) => { GLIDE_ACCEL } @@ -102,6 +131,11 @@ impl<'a> System<'a> for Sys { { HUMANOID_AIR_ACCEL } + (false, Swim) + if vel.0.magnitude_squared() < HUMANOID_WATER_SPEED.powf(2.0) => + { + HUMANOID_WATER_ACCEL + } _ => 0.0, }; } @@ -112,6 +146,12 @@ impl<'a> System<'a> for Sys { || character.action.is_block() { Vec2::from(controller.look_dir).normalized() + } else if let (Climb, Some(wall_dir)) = (character.movement, physics.on_wall) { + if Vec2::::from(wall_dir).magnitude_squared() > 0.001 { + Vec2::from(wall_dir).normalized() + } else { + Vec2::from(vel.0) + } } else { Vec2::from(vel.0) }; @@ -129,12 +169,16 @@ impl<'a> System<'a> for Sys { // Glide if character.movement == Glide - && vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) + && Vec2::::from(vel.0).magnitude_squared() < GLIDE_SPEED.powf(2.0) && vel.0.z < 0.0 { character.action = Idle; - let lift = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2; - vel.0.z += dt.0 * lift * Vec2::::from(vel.0 * 0.15).magnitude().min(1.0); + let lift = GLIDE_ANTIGRAV + vel.0.z.abs().powf(2.0) * 0.15; + vel.0.z += dt.0 + * lift + * (Vec2::::from(vel.0).magnitude() * 0.075) + .min(1.0) + .max(0.2); } // Roll @@ -149,7 +193,36 @@ impl<'a> System<'a> for Sys { } } - if physics.on_ground && (character.movement == Jump || character.movement == Glide) { + // Climb + if let (true, Some(_wall_dir)) = ( + (controller.climb | controller.climb_down) && vel.0.z <= CLIMB_SPEED, + physics.on_wall, + ) { + if controller.climb_down && !controller.climb { + vel.0 -= dt.0 * vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 6.0); + } else if controller.climb && !controller.climb_down { + vel.0.z = (vel.0.z + dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED); + } else { + vel.0.z = vel.0.z + dt.0 * GRAVITY * 1.5; + vel.0 = Lerp::lerp( + vel.0, + Vec3::zero(), + 30.0 * dt.0 / (1.0 - vel.0.z.min(0.0) * 5.0), + ); + } + + character.movement = Climb; + character.action = Idle; + } else if let Climb = character.movement { + character.movement = Jump; + } + + if physics.on_ground + && (character.movement == Jump + || character.movement == Climb + || character.movement == Glide + || character.movement == Swim) + { character.movement = Stand; } @@ -160,6 +233,12 @@ impl<'a> System<'a> for Sys { { character.movement = Jump; } + + if !physics.on_ground && physics.in_fluid { + character.movement = Swim; + } else if let Swim = character.movement { + character.movement = Stand; + } } } } diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 141bf8f755..b7047acc38 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -1,17 +1,17 @@ use { crate::{ - comp::{Body, Ori, PhysicsState, Pos, Scale, Vel}, + comp::{Body, Mounting, Ori, PhysicsState, Pos, Scale, Vel}, event::{EventBus, LocalEvent}, state::DeltaTime, - terrain::TerrainGrid, + terrain::{Block, TerrainGrid}, vol::ReadVol, }, specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}, vek::*, }; -const GRAVITY: f32 = 9.81 * 4.0; - +pub const GRAVITY: f32 = 9.81 * 4.0; +const BOUYANCY: f32 = 0.0; // Friction values used for linear damping. They are unitless quantities. The // value of these quantities must be between zero and one. They represent the // amount an object will slow down within 1/60th of a second. Eg. if the frction @@ -19,6 +19,7 @@ const GRAVITY: f32 = 9.81 * 4.0; // be 0.99. after 1 second the speed will be 0.54, which is 0.99 ^ 60. const FRIC_GROUND: f32 = 0.125; const FRIC_AIR: f32 = 0.0125; +const FRIC_FLUID: f32 = 0.2; // Integrates forces, calculates the new velocity based off of the old velocity // dt = delta time @@ -29,11 +30,7 @@ fn integrate_forces(dt: f32, mut lv: Vec3, grav: f32, damp: f32) -> Vec3 System<'a> for Sys { WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, + ReadStorage<'a, Mounting>, ); fn run( @@ -68,18 +66,20 @@ impl<'a> System<'a> for Sys { mut positions, mut velocities, mut orientations, + mountings, ): Self::SystemData, ) { let mut event_emitter = event_bus.emitter(); // Apply movement inputs - for (entity, scale, _, mut pos, mut vel, mut _ori) in ( + for (entity, scale, _b, mut pos, mut vel, _ori, _) in ( &entities, scales.maybe(), &bodies, &mut positions, &mut velocities, &mut orientations, + !&mountings, ) .join() { @@ -102,12 +102,23 @@ impl<'a> System<'a> for Sys { let old_vel = *vel; // Integrate forces // Friction is assumed to be a constant dependent on location - let friction = if physics_state.on_ground { - FRIC_GROUND + let friction = FRIC_AIR + .max(if physics_state.on_ground { + FRIC_GROUND + } else { + 0.0 + }) + .max(if physics_state.in_fluid { + FRIC_FLUID + } else { + 0.0 + }); + let downward_force = if physics_state.in_fluid { + (1.0 - BOUYANCY) * GRAVITY } else { - FRIC_AIR + GRAVITY }; - vel.0 = integrate_forces(dt.0, vel.0, GRAVITY, friction); + vel.0 = integrate_forces(dt.0, vel.0, downward_force, friction); // Don't move if we're not in a loaded chunk let pos_delta = if terrain @@ -122,15 +133,11 @@ impl<'a> System<'a> for Sys { }; // Function for determining whether the player at a specific position collides with the ground - let collision_with = |pos: Vec3, near_iter| { + let collision_with = |pos: Vec3, hit: fn(&Block) -> bool, near_iter| { for (i, j, k) in near_iter { let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); - if terrain - .get(block_pos) - .map(|vox| vox.is_solid()) - .unwrap_or(false) - { + if terrain.get(block_pos).map(hit).unwrap_or(false) { let player_aabb = Aabb { min: pos + Vec3::new(-player_rad, -player_rad, 0.0), max: pos + Vec3::new(player_rad, player_rad, player_height), @@ -165,7 +172,9 @@ impl<'a> System<'a> for Sys { const MAX_ATTEMPTS: usize = 16; // While the player is colliding with the terrain... - while collision_with(pos.0, near_iter.clone()) && attempts < MAX_ATTEMPTS { + while collision_with(pos.0, |vox| vox.is_solid(), near_iter.clone()) + && attempts < MAX_ATTEMPTS + { // Calculate the player's AABB let player_aabb = Aabb { min: pos.0 + Vec3::new(-player_rad, -player_rad, 0.0), @@ -187,8 +196,6 @@ impl<'a> System<'a> for Sys { }, ) }) - // Determine whether the block's AABB collides with the player's AABB - .filter(|(_, block_aabb)| block_aabb.collides_with_aabb(player_aabb)) // Make sure the block is actually solid .filter(|(block_pos, _)| { terrain @@ -196,13 +203,13 @@ impl<'a> System<'a> for Sys { .map(|vox| vox.is_solid()) .unwrap_or(false) }) + // Determine whether the block's AABB collides with the player's AABB + .filter(|(_, block_aabb)| block_aabb.collides_with_aabb(player_aabb)) // Find the maximum of the minimum collision axes (this bit is weird, trust me that it works) - .max_by_key(|(_, block_aabb)| { - ((player_aabb - .collision_vector_with_aabb(*block_aabb) + .min_by_key(|(_, block_aabb)| { + ((block_aabb.center() - player_aabb.center() - Vec3::unit_z() * 0.5) .map(|e| e.abs()) - .product() - + block_aabb.min.z) + .sum() * 1_000_000.0) as i32 }) .expect("Collision detected, but no colliding blocks found!"); @@ -231,7 +238,7 @@ impl<'a> System<'a> for Sys { // When the resolution direction is non-vertical, we must be colliding with a wall // If the space above is free... - if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), near_iter.clone()) + if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), |vox| vox.is_solid(), near_iter.clone()) // ...and we're being pushed out horizontally... && resolve_dir.z == 0.0 // ...and the vertical resolution direction is sufficiently great... @@ -244,6 +251,7 @@ impl<'a> System<'a> for Sys { // ...and there is a collision with a block beneath our current hitbox... && collision_with( old_pos + resolve_dir - Vec3::unit_z() * 1.05, + |vox| vox.is_solid(), near_iter.clone(), ) { @@ -274,8 +282,11 @@ impl<'a> System<'a> for Sys { if on_ground { physics_state.on_ground = true; // If the space below us is free, then "snap" to the ground - } else if collision_with(pos.0 - Vec3::unit_z() * 1.05, near_iter.clone()) - && vel.0.z < 0.0 + } else if collision_with( + pos.0 - Vec3::unit_z() * 1.05, + |vox| vox.is_solid(), + near_iter.clone(), + ) && vel.0.z < 0.0 && vel.0.z > -1.5 && was_on_ground { @@ -283,13 +294,45 @@ impl<'a> System<'a> for Sys { physics_state.on_ground = true; } + let dirs = [ + Vec3::unit_x(), + Vec3::unit_y(), + -Vec3::unit_x(), + -Vec3::unit_y(), + ]; + + if let (wall_dir, true) = dirs.iter().fold((Vec3::zero(), false), |(a, hit), dir| { + if collision_with(pos.0 + *dir * 0.01, |vox| vox.is_solid(), near_iter.clone()) { + (a + dir, true) + } else { + (a, hit) + } + }) { + physics_state.on_wall = Some(wall_dir); + } else { + physics_state.on_wall = None; + } + + // Figure out if we're in water + physics_state.in_fluid = collision_with(pos.0, |vox| vox.is_fluid(), near_iter.clone()); + let _ = physics_states.insert(entity, physics_state); } // Apply pushback - for (pos, scale, vel, _) in (&positions, scales.maybe(), &mut velocities, &bodies).join() { + for (pos, scale, vel, _, _) in ( + &positions, + scales.maybe(), + &mut velocities, + &bodies, + !&mountings, + ) + .join() + { let scale = scale.map(|s| s.0).unwrap_or(1.0); - for (pos_other, scale_other, _) in (&positions, scales.maybe(), &bodies).join() { + for (pos_other, scale_other, _, _) in + (&positions, scales.maybe(), &bodies, !&mountings).join() + { let scale_other = scale_other.map(|s| s.0).unwrap_or(1.0); let diff = Vec2::::from(pos.0 - pos_other.0); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7eaf39a2aa..93087eeffa 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -414,6 +414,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C server .create_npc(pos, comp::Stats::new(get_npc_name(id), None), body) .with(comp::Vel(vel)) + .with(comp::MountState::Unmounted) .with(agent) .build(); } diff --git a/server/src/lib.rs b/server/src/lib.rs index 67e4e7eb3a..2a9e67c85f 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -363,6 +363,53 @@ impl Server { .insert(entity, comp::ForceUpdate); } } + ServerEvent::Mount(mounter, mountee) => { + if state + .ecs() + .read_storage::() + .get(mounter) + .is_none() + { + let not_mounting_yet = if let Some(comp::MountState::Unmounted) = state + .ecs() + .write_storage::() + .get_mut(mountee) + .cloned() + { + true + } else { + false + }; + + if not_mounting_yet { + if let (Some(mounter_uid), Some(mountee_uid)) = ( + state.ecs().uid_from_entity(mounter), + state.ecs().uid_from_entity(mountee), + ) { + state.write_component( + mountee, + comp::MountState::MountedBy(mounter_uid.into()), + ); + state.write_component(mounter, comp::Mounting(mountee_uid.into())); + } + } + } + } + ServerEvent::Unmount(mounter) => { + let mountee_entity = state + .ecs() + .write_storage::() + .get(mounter) + .and_then(|mountee| state.ecs().entity_from_uid(mountee.0.into())); + if let Some(mountee_entity) = mountee_entity { + state + .ecs_mut() + .write_storage::() + .get_mut(mountee_entity) + .map(|ms| *ms = comp::MountState::Unmounted); + } + state.delete_component::(mounter); + } } if let Some(entity) = todo_remove { diff --git a/voxygen/src/anim/character/attack.rs b/voxygen/src/anim/character/attack.rs index 2bfd5c19b2..017c7d0090 100644 --- a/voxygen/src/anim/character/attack.rs +++ b/voxygen/src/anim/character/attack.rs @@ -18,6 +18,7 @@ impl Animation for AttackAnimation { skeleton: &Self::Skeleton, _global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/block.rs b/voxygen/src/anim/character/block.rs index f3ff71c066..36b2bcb246 100644 --- a/voxygen/src/anim/character/block.rs +++ b/voxygen/src/anim/character/block.rs @@ -19,6 +19,7 @@ impl Animation for BlockAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/blockidle.rs b/voxygen/src/anim/character/blockidle.rs index a2e5c4f839..9ef1e51a7f 100644 --- a/voxygen/src/anim/character/blockidle.rs +++ b/voxygen/src/anim/character/blockidle.rs @@ -19,6 +19,7 @@ impl Animation for BlockIdleAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/cidle.rs b/voxygen/src/anim/character/cidle.rs index 26aadb2665..a72e0ff2d2 100644 --- a/voxygen/src/anim/character/cidle.rs +++ b/voxygen/src/anim/character/cidle.rs @@ -19,6 +19,7 @@ impl Animation for CidleAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/climb.rs b/voxygen/src/anim/character/climb.rs new file mode 100644 index 0000000000..2cd28df9e4 --- /dev/null +++ b/voxygen/src/anim/character/climb.rs @@ -0,0 +1,108 @@ +use super::{ + super::{Animation, SkeletonAttr}, + CharacterSkeleton, +}; +use vek::*; + +pub struct ClimbAnimation; + +impl Animation for ClimbAnimation { + type Skeleton = CharacterSkeleton; + type Dependency = (Vec3, Vec3, f64); + + fn update_skeleton( + skeleton: &Self::Skeleton, + (velocity, _orientation, _global_time): Self::Dependency, + anim_time: f64, + rate: &mut f32, + skeleton_attr: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + let speed = velocity.magnitude(); + *rate = speed; + + let constant = 1.0; + let wave = (anim_time as f32 * constant as f32 * 1.5).sin(); + let wave_cos = (anim_time as f32 * constant as f32 * 1.5).cos(); + + let wave_test = (((5.0) + / (0.6 + 4.0 * ((anim_time as f32 * constant as f32 * 1.5).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.5).sin()); + let wave_testc = (((5.0) + / (0.6 + 4.0 * ((anim_time as f32 * constant as f32 * 1.5).cos()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.5).cos()); + + next.head.offset = Vec3::new( + 0.0, + 1.0 + skeleton_attr.neck_forward, + skeleton_attr.neck_height + 13.5 + wave_cos * 1.3, + ); + next.head.ori = Quaternion::rotation_z(wave * 0.1) + * Quaternion::rotation_x(0.6) + * Quaternion::rotation_y(wave_test * 0.1); + next.head.scale = Vec3::one() * skeleton_attr.head_scale; + + next.chest.offset = Vec3::new(0.0, 1.0, 5.0 + wave_cos * 1.1); + next.chest.ori = Quaternion::rotation_z(wave_test * 0.25) + * Quaternion::rotation_x(-0.15) + * Quaternion::rotation_y(wave_test * -0.12); + next.chest.scale = Vec3::one(); + + next.belt.offset = Vec3::new(0.0, 1.0, 3.5 + wave_cos * 1.1); + next.belt.ori = Quaternion::rotation_z(wave_test * 0.25) * Quaternion::rotation_x(0.0); + next.belt.scale = Vec3::one(); + + next.shorts.offset = Vec3::new(0.0, 1.0, 1.0 + wave_cos * 1.1); + next.shorts.ori = Quaternion::rotation_z(wave_test * 0.25) + * Quaternion::rotation_x(0.1) + * Quaternion::rotation_y(wave_test * 0.10); + next.shorts.scale = Vec3::one(); + + next.l_hand.offset = Vec3::new(-8.5, 3.0 + wave_testc * 1.5, 6.0 - wave_test * 4.0); + next.l_hand.ori = Quaternion::rotation_x(0.2 + wave_testc * 0.5); + next.l_hand.scale = Vec3::one(); + + next.r_hand.offset = Vec3::new(8.5, 3.0 - wave_test * 1.5, 6.0 + wave_test * 4.0); + + next.r_hand.ori = Quaternion::rotation_x(0.2 - wave_testc * 0.5); + next.r_hand.scale = Vec3::one(); + + next.l_foot.offset = Vec3::new(-3.4, 1.0, 6.0 + wave_test * 2.5); + next.l_foot.ori = Quaternion::rotation_x(0.2 - wave_testc * 0.50); + next.l_foot.scale = Vec3::one(); + + next.r_foot.offset = Vec3::new(3.4, 1.0, 6.0 - wave_test * 2.5); + next.r_foot.ori = Quaternion::rotation_x(0.2 + wave_testc * 0.50); + next.r_foot.scale = Vec3::one(); + + next.weapon.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + next.weapon.ori = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_cos * 0.25); + next.weapon.scale = Vec3::one(); + + next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); + next.l_shoulder.ori = Quaternion::rotation_x(wave_cos * 0.15); + next.l_shoulder.scale = Vec3::one() * 1.1; + + next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.ori = Quaternion::rotation_x(wave * 0.15); + next.r_shoulder.scale = Vec3::one() * 1.1; + + next.draw.offset = Vec3::new(0.0, 5.0, 0.0); + next.draw.ori = Quaternion::rotation_y(0.0); + next.draw.scale = Vec3::one() * 0.0; + + next.torso.offset = Vec3::new(0.0, -0.2 + wave * -0.08, 0.4) * skeleton_attr.scaler; + next.torso.ori = Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0); + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + next + } +} diff --git a/voxygen/src/anim/character/crun.rs b/voxygen/src/anim/character/crun.rs index 5e69198d89..7f33c43187 100644 --- a/voxygen/src/anim/character/crun.rs +++ b/voxygen/src/anim/character/crun.rs @@ -17,6 +17,7 @@ impl Animation for WieldAnimation { skeleton: &Self::Skeleton, (velocity, global_time): Self::Dependency, anim_time: f64, + rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/gliding.rs b/voxygen/src/anim/character/gliding.rs index 2a66fbbc93..08cce65c5d 100644 --- a/voxygen/src/anim/character/gliding.rs +++ b/voxygen/src/anim/character/gliding.rs @@ -9,15 +9,19 @@ pub struct GlidingAnimation; impl Animation for GlidingAnimation { type Skeleton = CharacterSkeleton; - type Dependency = (f32, f64); + type Dependency = (Vec3, Vec3, Vec3, f64); fn update_skeleton( skeleton: &Self::Skeleton, - (velocity, global_time): Self::Dependency, + (velocity, orientation, last_ori, global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); + + let speed = Vec2::::from(velocity).magnitude(); + let wave_slow = (anim_time as f32 * 7.0).sin(); let wave_slow_cos = (anim_time as f32 * 7.0).cos(); let wave_stop = (anim_time as f32 * 1.5).min(PI / 2.0).sin(); @@ -37,6 +41,22 @@ impl Animation for GlidingAnimation { .sin() * 0.25, ); + + let ori = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); + + let tilt = if Vec2::new(ori, last_ori) + .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && ori.angle_between(last_ori).is_finite() + { + ori.angle_between(last_ori).min(0.15) + * last_ori.determine_side(Vec2::zero(), ori).signum() + } else { + 0.0 + } * 0.8; + next.head.offset = Vec3::new( 0.0 + skeleton_attr.neck_right, 0.0 + skeleton_attr.neck_forward, @@ -76,14 +96,14 @@ impl Animation for GlidingAnimation { next.l_foot.offset = Vec3::new(-3.4, 1.0, -2.0); next.l_foot.ori = Quaternion::rotation_x( - (wave_stop * -0.7 - wave_slow_cos * -0.21 + wave_very_slow * 0.19) * velocity * 0.04, + (wave_stop * -0.7 - wave_slow_cos * -0.21 + wave_very_slow * 0.19) * speed * 0.04, ); next.l_foot.scale = Vec3::one(); next.r_foot.offset = Vec3::new(3.4, 1.0, -2.0); next.r_foot.ori = Quaternion::rotation_x( - (wave_stop * -0.8 + wave_slow * -0.25 + wave_very_slow_alt * 0.13) * velocity * 0.04, + (wave_stop * -0.8 + wave_slow * -0.25 + wave_very_slow_alt * 0.13) * speed * 0.04, ); next.r_foot.scale = Vec3::one(); @@ -104,12 +124,13 @@ impl Animation for GlidingAnimation { next.r_shoulder.scale = Vec3::one() * 1.1; next.draw.offset = Vec3::new(0.0, -13.0 + wave_very_slow * 0.10, 6.0); - next.draw.ori = Quaternion::rotation_x(1.0)//0.95 - wave_very_slow * 0.08) - * Quaternion::rotation_y(wave_very_slow_cos * 0.04); + next.draw.ori = + Quaternion::rotation_x(1.0) * Quaternion::rotation_y(wave_very_slow_cos * 0.04); next.draw.scale = Vec3::one(); next.torso.offset = Vec3::new(0.0, 6.0, 15.0) / 11.0 * skeleton_attr.scaler; - next.torso.ori = Quaternion::rotation_x(-0.05 * velocity + wave_very_slow * 0.10); + next.torso.ori = Quaternion::rotation_x(-0.05 * speed.max(12.0) + wave_very_slow * 0.10) + * Quaternion::rotation_y(tilt * 16.0); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next diff --git a/voxygen/src/anim/character/idle.rs b/voxygen/src/anim/character/idle.rs index f5ab828a78..85806d6372 100644 --- a/voxygen/src/anim/character/idle.rs +++ b/voxygen/src/anim/character/idle.rs @@ -18,6 +18,7 @@ impl Animation for IdleAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/jump.rs b/voxygen/src/anim/character/jump.rs index 9360cc7568..02f716d417 100644 --- a/voxygen/src/anim/character/jump.rs +++ b/voxygen/src/anim/character/jump.rs @@ -15,6 +15,7 @@ impl Animation for JumpAnimation { skeleton: &Self::Skeleton, _global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/mod.rs b/voxygen/src/anim/character/mod.rs index c8ec0efbcf..3a8f50d91a 100644 --- a/voxygen/src/anim/character/mod.rs +++ b/voxygen/src/anim/character/mod.rs @@ -2,12 +2,15 @@ pub mod attack; pub mod block; pub mod blockidle; pub mod cidle; +pub mod climb; pub mod gliding; pub mod idle; pub mod jump; pub mod roll; pub mod run; +pub mod sit; pub mod stand; +pub mod swim; pub mod wield; // Reexports @@ -15,12 +18,15 @@ pub use self::attack::AttackAnimation; pub use self::block::BlockAnimation; pub use self::blockidle::BlockIdleAnimation; pub use self::cidle::CidleAnimation; +pub use self::climb::ClimbAnimation; pub use self::gliding::GlidingAnimation; pub use self::idle::IdleAnimation; pub use self::jump::JumpAnimation; pub use self::roll::RollAnimation; pub use self::run::RunAnimation; +pub use self::sit::SitAnimation; pub use self::stand::StandAnimation; +pub use self::swim::SwimAnimation; pub use self::wield::WieldAnimation; use super::{Bone, Skeleton}; diff --git a/voxygen/src/anim/character/roll.rs b/voxygen/src/anim/character/roll.rs index fd5989b1d6..ab397172eb 100644 --- a/voxygen/src/anim/character/roll.rs +++ b/voxygen/src/anim/character/roll.rs @@ -15,6 +15,7 @@ impl Animation for RollAnimation { skeleton: &Self::Skeleton, _global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/character/run.rs b/voxygen/src/anim/character/run.rs index 759ae0c396..2f2edddb63 100644 --- a/voxygen/src/anim/character/run.rs +++ b/voxygen/src/anim/character/run.rs @@ -10,21 +10,35 @@ pub struct RunAnimation; impl Animation for RunAnimation { type Skeleton = CharacterSkeleton; - type Dependency = (f32, f32, f64); + type Dependency = (Vec3, Vec3, Vec3, f64); fn update_skeleton( skeleton: &Self::Skeleton, - (velocity, orientation, global_time): Self::Dependency, + (velocity, orientation, last_ori, global_time): Self::Dependency, anim_time: f64, + rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); - let wave = (anim_time as f32 * velocity * 1.2).sin(); - let wave_cos = (anim_time as f32 * velocity * 1.2).cos(); + let speed = Vec2::::from(velocity).magnitude(); + *rate = speed; - let wave_diff = (anim_time as f32 * velocity * 0.6).sin(); - let wave_cos_dub = (anim_time as f32 * velocity * 2.4).cos(); + let constant = 1.0; + let wave = (((5.0) + / (1.1 + 3.9 * ((anim_time as f32 * constant as f32 * 1.2).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.2).sin()); + let wave_cos = (((5.0) + / (1.1 + 3.9 * ((anim_time as f32 * constant as f32 * 2.4).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.5).sin()); + let wave_cos_dub = (((5.0) + / (1.1 + 3.9 * ((anim_time as f32 * constant as f32 * 1.5).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * constant as f32 * 1.5).sin()); + + let wave_diff = (anim_time as f32 * 0.6).sin(); let wave_stop = (anim_time as f32 * 2.6).min(PI / 2.0).sin(); let head_look = Vec2::new( ((global_time + anim_time) as f32 / 2.0) @@ -39,18 +53,20 @@ impl Animation for RunAnimation { * 0.1, ); - let vel = Vec2::from(velocity); - let ori = (Vec2::from(orientation)).normalized(); + let ori = Vec2::from(orientation); + let last_ori = Vec2::from(last_ori); - let _tilt = if Vec2::new(ori, vel) - .map(|v| Vec2::::from(v).magnitude_squared()) - .reduce_partial_min() - > 0.001 + let tilt = if Vec2::new(ori, last_ori) + .map(|o| Vec2::::from(o).magnitude_squared()) + .map(|m| m > 0.001 && m.is_finite()) + .reduce_and() + && ori.angle_between(last_ori).is_finite() { - vel.normalized().dot(ori.normalized()).min(1.0).acos() + ori.angle_between(last_ori).min(0.5) + * last_ori.determine_side(Vec2::zero(), ori).signum() } else { 0.0 - }; + } * 1.3; next.head.offset = Vec3::new( 0.0, @@ -90,11 +106,11 @@ impl Animation for RunAnimation { next.r_hand.scale = Vec3::one(); next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 6.0 - wave_cos_dub * 0.7); - next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.5); + next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.2); next.l_foot.scale = Vec3::one(); next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 6.0 - wave_cos_dub * 0.7); - next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.5); + next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.2); next.r_foot.scale = Vec3::one(); next.weapon.offset = Vec3::new( @@ -120,8 +136,8 @@ impl Animation for RunAnimation { next.torso.offset = Vec3::new(0.0, -0.2 + wave * -0.08, 0.4) * skeleton_attr.scaler; next.torso.ori = - Quaternion::rotation_x(wave_stop * velocity * -0.06 + wave_diff * velocity * -0.005) - * Quaternion::rotation_y(0.0); + Quaternion::rotation_x(wave_stop * speed * -0.06 + wave_diff * speed * -0.005) + * Quaternion::rotation_y(tilt); next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next diff --git a/voxygen/src/anim/character/sit.rs b/voxygen/src/anim/character/sit.rs new file mode 100644 index 0000000000..be24d9801d --- /dev/null +++ b/voxygen/src/anim/character/sit.rs @@ -0,0 +1,125 @@ +use super::{ + super::{Animation, SkeletonAttr}, + CharacterSkeleton, +}; +use std::{f32::consts::PI, ops::Mul}; +use vek::*; + +pub struct SitAnimation; + +impl Animation for SitAnimation { + type Skeleton = CharacterSkeleton; + type Dependency = f64; + + fn update_skeleton( + skeleton: &Self::Skeleton, + global_time: f64, + anim_time: f64, + _rate: &mut f32, + skeleton_attr: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + let wave_slow = (anim_time as f32 * 1.0 + PI).sin(); + let wave_slow_cos = (anim_time as f32 * 1.0 + PI).cos(); + let wave_stop = (anim_time as f32 * 3.0).min(PI / 2.0).sin(); + let wave_slow_abs = ((anim_time as f32 * 0.5 + PI).sin()) + 1.0; + let wave_ultra_slow = (anim_time as f32 * 0.3 + PI).sin(); + let wave_ultra_slow_cos = (anim_time as f32 * 0.3 + PI).cos(); + + let head_look = Vec2::new( + ((global_time + anim_time) as f32 / 18.0) + .floor() + .mul(7331.0) + .sin() + * 0.25, + ((global_time + anim_time) as f32 / 18.0) + .floor() + .mul(1337.0) + .sin() + * 0.125, + ); + next.head.offset = Vec3::new( + 0.0 + skeleton_attr.neck_right, + wave_stop * -1.6 + skeleton_attr.neck_forward, + skeleton_attr.neck_height + 15.0 + wave_slow * 0.1 + wave_stop * -0.8, + ); + next.head.ori = + Quaternion::rotation_z(head_look.x + wave_ultra_slow * 0.2 - wave_slow * 0.1) + * Quaternion::rotation_x( + (wave_ultra_slow_cos * -0.2 + wave_slow * 0.1 + head_look.y).abs(), + ); + next.head.scale = Vec3::one() * skeleton_attr.head_scale; + + next.chest.offset = Vec3::new( + 0.0, + wave_stop * -0.4, + 7.0 + wave_slow * 0.1 + wave_stop * -0.8, + ); + next.chest.ori = Quaternion::rotation_x(wave_stop * 0.15); + next.chest.scale = Vec3::one() + wave_slow_abs * 0.05; + + next.belt.offset = Vec3::new(0.0, wave_stop * 1.2, 5.0); + next.belt.ori = Quaternion::rotation_x(wave_stop * 0.3); + next.belt.scale = (Vec3::one() + wave_slow_abs * 0.05) * 1.02; + + next.shorts.offset = Vec3::new(0.0, wave_stop * 2.5, 2.0 + wave_stop * 0.6); + next.shorts.ori = Quaternion::rotation_x(wave_stop * 0.6); + next.shorts.scale = Vec3::one(); + + next.l_hand.offset = Vec3::new( + -7.5, + 0.0 + wave_ultra_slow_cos * 0.15, + wave_ultra_slow * 0.7 + wave_stop * -2.0, + ); + + next.l_hand.ori = + Quaternion::rotation_x(0.0 + wave_slow_cos * -0.1 + wave_ultra_slow * 0.1); + next.l_hand.scale = Vec3::one() + wave_slow_abs * -0.05; + + next.r_hand.offset = Vec3::new( + 7.5, + 0.0 + wave_ultra_slow_cos * 0.15, + wave_ultra_slow * 0.7 + wave_stop * -2.0, + ); + next.r_hand.ori = + Quaternion::rotation_x(0.0 + wave_slow * -0.1 + wave_ultra_slow_cos * 0.1); + next.r_hand.scale = Vec3::one() + wave_slow_abs * -0.05; + + next.l_foot.offset = Vec3::new(-3.4, -0.1, 8.0); + next.l_foot.ori = + Quaternion::rotation_x(wave_slow * 0.1 + wave_stop * 1.2 + wave_ultra_slow * 0.1); + next.l_foot.scale = Vec3::one(); + + next.r_foot.offset = Vec3::new(3.4, -0.1, 8.0); + next.r_foot.ori = Quaternion::rotation_x( + wave_slow_cos * 0.1 + wave_stop * 1.2 + wave_ultra_slow_cos * 0.1, + ); + next.r_foot.scale = Vec3::one(); + + next.weapon.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); + next.weapon.scale = Vec3::one() + wave_slow_abs * -0.05; + + next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); + next.l_shoulder.ori = Quaternion::rotation_x(0.0); + next.l_shoulder.scale = (Vec3::one() + wave_slow_abs * -0.05) * 1.15; + + next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.ori = Quaternion::rotation_x(0.0); + next.r_shoulder.scale = (Vec3::one() + wave_slow_abs * -0.05) * 1.15; + + next.draw.offset = Vec3::new(0.0, 5.0, 0.0); + next.draw.ori = Quaternion::rotation_y(0.0); + next.draw.scale = Vec3::one() * 0.0; + + next.torso.offset = Vec3::new(0.0, -0.2, wave_stop * -0.16) * skeleton_attr.scaler; + next.torso.ori = Quaternion::rotation_x(0.0); + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + next + } +} diff --git a/voxygen/src/anim/character/stand.rs b/voxygen/src/anim/character/stand.rs index c7057b1f1e..00163be28c 100644 --- a/voxygen/src/anim/character/stand.rs +++ b/voxygen/src/anim/character/stand.rs @@ -5,9 +5,6 @@ use super::{ use std::{f32::consts::PI, ops::Mul}; use vek::*; -pub struct Input { - pub attack: bool, -} pub struct StandAnimation; impl Animation for StandAnimation { @@ -18,12 +15,14 @@ impl Animation for StandAnimation { skeleton: &Self::Skeleton, global_time: f64, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); let wave_ultra_slow = (anim_time as f32 * 1.0 + PI).sin(); let wave_ultra_slow_cos = (anim_time as f32 * 1.0 + PI).cos(); + let wave_ultra_slow_abs = ((anim_time as f32 * 0.5 + PI).sin()) + 1.0; let head_look = Vec2::new( ((global_time + anim_time) as f32 / 12.0) @@ -48,11 +47,11 @@ impl Animation for StandAnimation { next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_ultra_slow * 0.3); next.chest.ori = Quaternion::rotation_x(0.0); - next.chest.scale = Vec3::one(); + next.chest.scale = Vec3::one() + wave_ultra_slow_abs * 0.05; next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + wave_ultra_slow * 0.3); next.belt.ori = Quaternion::rotation_x(0.0); - next.belt.scale = Vec3::one(); + next.belt.scale = Vec3::one() + wave_ultra_slow_abs * 0.05; next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + wave_ultra_slow * 0.3); next.shorts.ori = Quaternion::rotation_x(0.0); @@ -70,10 +69,10 @@ impl Animation for StandAnimation { next.r_hand.offset = Vec3::new( 7.5, 0.0 + wave_ultra_slow_cos * 0.15, - 0.0 + wave_ultra_slow * 0.5, + 0.0 + wave_ultra_slow * 0.5 + wave_ultra_slow_abs * -0.05, ); next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06); - next.r_hand.scale = Vec3::one(); + next.r_hand.scale = Vec3::one() + wave_ultra_slow_abs * -0.05; next.l_foot.offset = Vec3::new(-3.4, -0.1, 8.0); next.l_foot.ori = Quaternion::identity(); @@ -89,15 +88,15 @@ impl Animation for StandAnimation { 15.0, ); next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57); - next.weapon.scale = Vec3::one(); + next.weapon.scale = Vec3::one() + wave_ultra_slow_abs * -0.05; next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); next.l_shoulder.ori = Quaternion::rotation_x(0.0); - next.l_shoulder.scale = Vec3::one() * 1.1; + next.l_shoulder.scale = (Vec3::one() + wave_ultra_slow_abs * -0.05) * 1.15; next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); next.r_shoulder.ori = Quaternion::rotation_x(0.0); - next.r_shoulder.scale = Vec3::one() * 1.1; + next.r_shoulder.scale = (Vec3::one() + wave_ultra_slow_abs * -0.05) * 1.15; next.draw.offset = Vec3::new(0.0, 5.0, 0.0); next.draw.ori = Quaternion::rotation_y(0.0); diff --git a/voxygen/src/anim/character/swim.rs b/voxygen/src/anim/character/swim.rs new file mode 100644 index 0000000000..74a995028a --- /dev/null +++ b/voxygen/src/anim/character/swim.rs @@ -0,0 +1,130 @@ +use super::{ + super::{Animation, SkeletonAttr}, + CharacterSkeleton, +}; +use std::f32::consts::PI; +use std::ops::Mul; +use vek::*; + +pub struct SwimAnimation; + +impl Animation for SwimAnimation { + type Skeleton = CharacterSkeleton; + type Dependency = (f32, f32, f64); + + fn update_skeleton( + skeleton: &Self::Skeleton, + (velocity, orientation, global_time): Self::Dependency, + anim_time: f64, + _rate: &mut f32, + skeleton_attr: &SkeletonAttr, + ) -> Self::Skeleton { + let mut next = (*skeleton).clone(); + + let wave = (anim_time as f32 * velocity * 1.2).sin(); + let wave_cos = (anim_time as f32 * velocity * 1.2).cos(); + + let wave_diff = (anim_time as f32 * velocity * 0.6).sin(); + let wave_cos_dub = (anim_time as f32 * velocity * 2.4).cos(); + let wave_stop = (anim_time as f32 * 2.6).min(PI / 2.0).sin(); + let head_look = Vec2::new( + ((global_time + anim_time) as f32 / 2.0) + .floor() + .mul(7331.0) + .sin() + * 0.2, + ((global_time + anim_time) as f32 / 2.0) + .floor() + .mul(1337.0) + .sin() + * 0.1, + ); + + let vel = Vec2::from(velocity); + let ori = (Vec2::from(orientation)).normalized(); + + let _tilt = if Vec2::new(ori, vel) + .map(|v| Vec2::::from(v).magnitude_squared()) + .reduce_partial_min() + > 0.001 + { + vel.normalized().dot(ori.normalized()).min(1.0).acos() + } else { + 0.0 + }; + + next.head.offset = Vec3::new( + 0.0, + -1.0 + skeleton_attr.neck_forward, + skeleton_attr.neck_height + 15.0 + wave_cos * 1.3, + ); + next.head.ori = Quaternion::rotation_z(head_look.x + wave * 0.1) + * Quaternion::rotation_x(head_look.y + 0.35); + next.head.scale = Vec3::one() * skeleton_attr.head_scale; + + next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_cos * 1.1); + next.chest.ori = Quaternion::rotation_z(wave * 0.2); + next.chest.scale = Vec3::one(); + + next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + wave_cos * 1.1); + next.belt.ori = Quaternion::rotation_z(wave * 0.35); + next.belt.scale = Vec3::one(); + + next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + wave_cos * 1.1); + next.shorts.ori = Quaternion::rotation_z(wave * 0.6); + next.shorts.scale = Vec3::one(); + + next.l_hand.offset = Vec3::new( + -7.5 + wave_cos_dub * 1.0, + 2.0 + wave_cos * 5.0, + 0.0 - wave * 1.5, + ); + next.l_hand.ori = Quaternion::rotation_x(wave_cos * 0.8); + next.l_hand.scale = Vec3::one(); + + next.r_hand.offset = Vec3::new( + 7.5 - wave_cos_dub * 1.0, + 2.0 - wave_cos * 5.0, + 0.0 + wave * 1.5, + ); + next.r_hand.ori = Quaternion::rotation_x(wave_cos * -0.8); + next.r_hand.scale = Vec3::one(); + + next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 6.0 - wave_cos_dub * 0.7); + next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.5); + next.l_foot.scale = Vec3::one(); + + next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 6.0 - wave_cos_dub * 0.7); + next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.5); + next.r_foot.scale = Vec3::one(); + + next.weapon.offset = Vec3::new( + -7.0 + skeleton_attr.weapon_x, + -5.0 + skeleton_attr.weapon_y, + 15.0, + ); + next.weapon.ori = + Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_cos * 0.25); + next.weapon.scale = Vec3::one(); + + next.l_shoulder.offset = Vec3::new(-5.0, 0.0, 4.7); + next.l_shoulder.ori = Quaternion::rotation_x(wave_cos * 0.15); + next.l_shoulder.scale = Vec3::one() * 1.1; + + next.r_shoulder.offset = Vec3::new(5.0, 0.0, 4.7); + next.r_shoulder.ori = Quaternion::rotation_x(wave * 0.15); + next.r_shoulder.scale = Vec3::one() * 1.1; + + next.draw.offset = Vec3::new(0.0, 5.0, 0.0); + next.draw.ori = Quaternion::rotation_y(0.0); + next.draw.scale = Vec3::one() * 0.0; + + next.torso.offset = Vec3::new(0.0, -0.2 + wave * -0.08, 0.4) * skeleton_attr.scaler; + next.torso.ori = + Quaternion::rotation_x(wave_stop * velocity * -0.06 + wave_diff * velocity * -0.005) + * Quaternion::rotation_y(0.0); + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + next + } +} diff --git a/voxygen/src/anim/character/wield.rs b/voxygen/src/anim/character/wield.rs index 56bce3625e..bac379c2ff 100644 --- a/voxygen/src/anim/character/wield.rs +++ b/voxygen/src/anim/character/wield.rs @@ -15,6 +15,7 @@ impl Animation for WieldAnimation { skeleton: &Self::Skeleton, (_velocity, _global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/mod.rs b/voxygen/src/anim/mod.rs index d3d27e0ad2..12287b1c15 100644 --- a/voxygen/src/anim/mod.rs +++ b/voxygen/src/anim/mod.rs @@ -178,6 +178,7 @@ pub trait Animation { skeleton: &Self::Skeleton, dependency: Self::Dependency, anim_time: f64, + rate: &mut f32, skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton; } diff --git a/voxygen/src/anim/quadruped/idle.rs b/voxygen/src/anim/quadruped/idle.rs index 8477a2c9c1..fc0f912acc 100644 --- a/voxygen/src/anim/quadruped/idle.rs +++ b/voxygen/src/anim/quadruped/idle.rs @@ -15,6 +15,7 @@ impl Animation for IdleAnimation { skeleton: &Self::Skeleton, global_time: Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadruped/jump.rs b/voxygen/src/anim/quadruped/jump.rs index 19d9075428..d0cf3fbe39 100644 --- a/voxygen/src/anim/quadruped/jump.rs +++ b/voxygen/src/anim/quadruped/jump.rs @@ -15,6 +15,7 @@ impl Animation for JumpAnimation { skeleton: &Self::Skeleton, (_velocity, _global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadruped/run.rs b/voxygen/src/anim/quadruped/run.rs index 327f2bf559..f43863a063 100644 --- a/voxygen/src/anim/quadruped/run.rs +++ b/voxygen/src/anim/quadruped/run.rs @@ -14,6 +14,7 @@ impl Animation for RunAnimation { skeleton: &Self::Skeleton, (_velocity, _global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadrupedmedium/idle.rs b/voxygen/src/anim/quadrupedmedium/idle.rs index 568d1c511d..63c1bcd97e 100644 --- a/voxygen/src/anim/quadrupedmedium/idle.rs +++ b/voxygen/src/anim/quadrupedmedium/idle.rs @@ -15,6 +15,7 @@ impl Animation for IdleAnimation { skeleton: &Self::Skeleton, global_time: Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadrupedmedium/jump.rs b/voxygen/src/anim/quadrupedmedium/jump.rs index 7708083852..06f93aa6f7 100644 --- a/voxygen/src/anim/quadrupedmedium/jump.rs +++ b/voxygen/src/anim/quadrupedmedium/jump.rs @@ -15,6 +15,7 @@ impl Animation for JumpAnimation { skeleton: &Self::Skeleton, _global_time: Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/anim/quadrupedmedium/run.rs b/voxygen/src/anim/quadrupedmedium/run.rs index 5fd7a5d8cd..5acc6d4cd8 100644 --- a/voxygen/src/anim/quadrupedmedium/run.rs +++ b/voxygen/src/anim/quadrupedmedium/run.rs @@ -15,6 +15,7 @@ impl Animation for RunAnimation { skeleton: &Self::Skeleton, (_velocity, global_time): Self::Dependency, anim_time: f64, + _rate: &mut f32, _skeleton_attr: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 0c7fb13710..e38477fb19 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -28,6 +28,15 @@ image_ids! { bar_content: "voxygen.element.skillbar.bar_content", level_up: "voxygen.element.misc_bg.level_up", level_down:"voxygen.element.misc_bg.level_down", + stamina_0:"voxygen.element.skillbar.stamina_wheel-empty", + stamina_1:"voxygen.element.skillbar.stamina_wheel-0", + stamina_2:"voxygen.element.skillbar.stamina_wheel-1", + stamina_3:"voxygen.element.skillbar.stamina_wheel-2", + stamina_4:"voxygen.element.skillbar.stamina_wheel-3", + stamina_5:"voxygen.element.skillbar.stamina_wheel-4", + stamina_6:"voxygen.element.skillbar.stamina_wheel-5", + stamina_7:"voxygen.element.skillbar.stamina_wheel-6", + stamina_8:"voxygen.element.skillbar.stamina_wheel-7", // Window Parts window_3: "voxygen.element.frames.window_3", diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index d492e76277..912a14ca58 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -77,7 +77,7 @@ widget_ids! { level_down, level_align, level_message, - + stamina_wheel, } } @@ -163,6 +163,37 @@ impl<'a> Widget for Skillbar<'a> { let shortcuts = self.global_state.settings.gameplay.shortcut_numbers; const BG_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 0.8); + + // Stamina Wheel + /* + let stamina_percentage = + self.stats.health.current() as f64 / self.stats.health.maximum() as f64 * 100.0; + if stamina_percentage < 100.0 { + Image::new(if stamina_percentage <= 0.1 { + self.imgs.stamina_0 + } else if stamina_percentage < 12.5 { + self.imgs.stamina_1 + } else if stamina_percentage < 25.0 { + self.imgs.stamina_2 + } else if stamina_percentage < 37.5 { + self.imgs.stamina_3 + } else if stamina_percentage < 50.0 { + self.imgs.stamina_4 + } else if stamina_percentage < 62.5 { + self.imgs.stamina_5 + } else if stamina_percentage < 75.0 { + self.imgs.stamina_6 + } else if stamina_percentage < 87.5 { + self.imgs.stamina_7 + } else { + self.imgs.stamina_8 + }) + .w_h(37.0 * 3.0, 37.0 * 3.0) + .mid_bottom_with_margin_on(ui.window, 150.0) + .set(state.ids.stamina_wheel, ui); + } + */ + // Level Up Message let current_level = self.stats.level.level(); diff --git a/voxygen/src/menu/char_selection/scene.rs b/voxygen/src/menu/char_selection/scene.rs index cdc6471d5e..9d57cda816 100644 --- a/voxygen/src/menu/char_selection/scene.rs +++ b/voxygen/src/menu/char_selection/scene.rs @@ -131,6 +131,7 @@ impl Scene { self.figure_state.skeleton_mut(), client.state().get_time(), client.state().get_time(), + &mut 0.0, &SkeletonAttr::from(&body), ); self.figure_state.skeleton_mut().interpolate( @@ -145,6 +146,8 @@ impl Scene { 1.0, Rgba::broadcast(1.0), 1.0 / 60.0, // TODO: Use actual deltatime here? + 1.0, + 1.0, ); } diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index b62bffc3ba..fb3d402899 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -23,7 +23,6 @@ use common::{ use hashbrown::HashMap; use log::debug; use specs::{Entity as EcsEntity, Join}; -use std::time::Instant; use vek::*; const DAMAGE_FADE_COEFFICIENT: f64 = 5.0; @@ -128,6 +127,9 @@ impl FigureMgr { ) .1; + let mut movement_animation_rate = 1.0; + let mut action_animation_rate = 1.0; + match body { Body::Humanoid(_) => { let state = self @@ -140,45 +142,67 @@ impl FigureMgr { }; if !character.is_same_movement(&last_character.0) { - state.last_movement_change = Instant::now(); + state.movement_time = 0.0; } if !character.is_same_action(&last_character.0) { - state.last_action_change = Instant::now(); + state.action_time = 0.0; } - let time_since_movement_change = - state.last_movement_change.elapsed().as_secs_f64(); - let time_since_action_change = state.last_action_change.elapsed().as_secs_f64(); - let target_base = match &character.movement { Stand => anim::character::StandAnimation::update_skeleton( &CharacterSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Run => anim::character::RunAnimation::update_skeleton( &CharacterSkeleton::new(), - (vel.0.magnitude(), ori.0.magnitude(), time), - time_since_movement_change, + (vel.0, ori.0, state.last_ori, time), + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Jump => anim::character::JumpAnimation::update_skeleton( &CharacterSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Roll { .. } => anim::character::RollAnimation::update_skeleton( &CharacterSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Glide => anim::character::GlidingAnimation::update_skeleton( &CharacterSkeleton::new(), - (vel.0.magnitude(), time), - time_since_movement_change, + (vel.0, ori.0, state.last_ori, time), + state.movement_time, + &mut movement_animation_rate, + skeleton_attr, + ), + Swim => anim::character::SwimAnimation::update_skeleton( + &CharacterSkeleton::new(), + (vel.0.magnitude(), ori.0.magnitude(), time), + state.movement_time, + &mut movement_animation_rate, + skeleton_attr, + ), + Climb => anim::character::ClimbAnimation::update_skeleton( + &CharacterSkeleton::new(), + (vel.0, ori.0, time), + state.movement_time, + &mut movement_animation_rate, + skeleton_attr, + ), + Sit => anim::character::SitAnimation::update_skeleton( + &CharacterSkeleton::new(), + time, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), }; @@ -187,40 +211,54 @@ impl FigureMgr { (Stand, Wield { .. }) => anim::character::CidleAnimation::update_skeleton( &target_base, time, - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ), (Stand, Block { .. }) => { anim::character::BlockIdleAnimation::update_skeleton( &target_base, time, - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ) } (_, Attack { .. }) => anim::character::AttackAnimation::update_skeleton( &target_base, time, - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ), (_, Wield { .. }) => anim::character::WieldAnimation::update_skeleton( &target_base, (vel.0.magnitude(), time), - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ), (_, Block { .. }) => anim::character::BlockAnimation::update_skeleton( &target_base, time, - time_since_action_change, + state.action_time, + &mut action_animation_rate, skeleton_attr, ), _ => target_base, }; state.skeleton.interpolate(&target_bones, dt); - state.update(renderer, pos.0, ori.0, scale, col, dt); + state.update( + renderer, + pos.0, + ori.0, + scale, + col, + dt, + movement_animation_rate, + action_animation_rate, + ); } Body::Quadruped(_) => { let state = self @@ -234,29 +272,29 @@ impl FigureMgr { }; if !character.is_same_movement(&last_character.0) { - state.last_movement_change = Instant::now(); + state.movement_time = 0.0; } - let time_since_movement_change = - state.last_movement_change.elapsed().as_secs_f64(); - let target_base = match character.movement { Stand => anim::quadruped::IdleAnimation::update_skeleton( &QuadrupedSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Run => anim::quadruped::RunAnimation::update_skeleton( &QuadrupedSkeleton::new(), (vel.0.magnitude(), time), - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Jump => anim::quadruped::JumpAnimation::update_skeleton( &QuadrupedSkeleton::new(), (vel.0.magnitude(), time), - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), @@ -265,7 +303,16 @@ impl FigureMgr { }; state.skeleton.interpolate(&target_base, dt); - state.update(renderer, pos.0, ori.0, scale, col, dt); + state.update( + renderer, + pos.0, + ori.0, + scale, + col, + dt, + movement_animation_rate, + action_animation_rate, + ); } Body::QuadrupedMedium(_) => { let state = self @@ -281,29 +328,29 @@ impl FigureMgr { }; if !character.is_same_movement(&last_character.0) { - state.last_movement_change = Instant::now(); + state.movement_time = 0.0; } - let time_since_movement_change = - state.last_movement_change.elapsed().as_secs_f64(); - let target_base = match character.movement { Stand => anim::quadrupedmedium::IdleAnimation::update_skeleton( &QuadrupedMediumSkeleton::new(), time, - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Run => anim::quadrupedmedium::RunAnimation::update_skeleton( &QuadrupedMediumSkeleton::new(), (vel.0.magnitude(), time), - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), Jump => anim::quadrupedmedium::JumpAnimation::update_skeleton( &QuadrupedMediumSkeleton::new(), (vel.0.magnitude(), time), - time_since_movement_change, + state.movement_time, + &mut movement_animation_rate, skeleton_attr, ), @@ -312,7 +359,16 @@ impl FigureMgr { }; state.skeleton.interpolate(&target_base, dt); - state.update(renderer, pos.0, ori.0, scale, col, dt); + state.update( + renderer, + pos.0, + ori.0, + scale, + col, + dt, + movement_animation_rate, + action_animation_rate, + ); } Body::Object(_) => { let state = self @@ -321,7 +377,16 @@ impl FigureMgr { .or_insert_with(|| FigureState::new(renderer, ObjectSkeleton::new())); state.skeleton = state.skeleton_mut().clone(); - state.update(renderer, pos.0, ori.0, scale, col, dt); + state.update( + renderer, + pos.0, + ori.0, + scale, + col, + dt, + movement_animation_rate, + action_animation_rate, + ); } } } @@ -426,11 +491,12 @@ impl FigureMgr { pub struct FigureState { bone_consts: Consts, locals: Consts, - last_movement_change: Instant, - last_action_change: Instant, + movement_time: f64, + action_time: f64, skeleton: S, pos: Vec3, ori: Vec3, + last_ori: Vec3, } impl FigureState { @@ -440,11 +506,12 @@ impl FigureState { .create_consts(&skeleton.compute_matrices()) .unwrap(), locals: renderer.create_consts(&[FigureLocals::default()]).unwrap(), - last_movement_change: Instant::now(), - last_action_change: Instant::now(), + movement_time: 0.0, + action_time: 0.0, skeleton, pos: Vec3::zero(), ori: Vec3::zero(), + last_ori: Vec3::zero(), } } @@ -456,7 +523,11 @@ impl FigureState { scale: f32, col: Rgba, dt: f32, + movement_rate: f32, + action_rate: f32, ) { + self.last_ori = Lerp::lerp(self.last_ori, ori, 15.0 * dt); + // Update interpolation values if self.pos.distance_squared(pos) < 64.0 * 64.0 { self.pos = Lerp::lerp(self.pos, pos, 15.0 * dt); @@ -466,6 +537,9 @@ impl FigureState { self.ori = ori; } + self.movement_time += (dt * movement_rate) as f64; + self.action_time += (dt * action_rate) as f64; + let mat = Mat4::::identity() * Mat4::translation_3d(self.pos) * Mat4::rotation_z(-ori.x.atan2(ori.y)) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 9710e831ab..029d5fe67d 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -10,8 +10,7 @@ use client::{self, Client}; use common::{ clock::Clock, comp, - comp::Pos, - comp::Vel, + comp::{Pos, Vel}, msg::ClientState, terrain::{Block, BlockKind}, vol::ReadVol, @@ -118,6 +117,9 @@ impl PlayState for SessionState { .compute_dependents(&self.client.borrow()); let cam_dir: Vec3 = Vec3::from(view_mat.inverted() * -Vec4::unit_z()); + // Reset controller events + self.controller.clear_events(); + // Handle window events. for event in global_state.window.fetch_events() { // Pass all events to the ui first. @@ -208,6 +210,9 @@ impl PlayState for SessionState { Event::InputUpdate(GameInput::Jump, state) => { self.controller.jump = state; } + Event::InputUpdate(GameInput::Sit, state) => { + self.controller.sit = state; + } Event::InputUpdate(GameInput::MoveForward, state) => self.key_state.up = state, Event::InputUpdate(GameInput::MoveBack, state) => self.key_state.down = state, Event::InputUpdate(GameInput::MoveLeft, state) => self.key_state.left = state, @@ -215,6 +220,54 @@ impl PlayState for SessionState { Event::InputUpdate(GameInput::Glide, state) => { self.controller.glide = state; } + Event::InputUpdate(GameInput::Climb, state) => self.controller.climb = state, + Event::InputUpdate(GameInput::ClimbDown, state) => { + self.controller.climb_down = state + } + Event::InputUpdate(GameInput::WallLeap, state) => { + self.controller.wall_leap = state + } + Event::InputUpdate(GameInput::Mount, true) => { + let client = self.client.borrow(); + if client.is_mounted() { + self.controller.push_event(comp::ControlEvent::Unmount); + } else { + let player_pos = client + .state() + .read_storage::() + .get(client.entity()) + .copied(); + if let Some(player_pos) = player_pos { + // Find closest mountable entity + let closest_mountable = ( + &client.state().ecs().entities(), + &client.state().ecs().read_storage::(), + &client.state().ecs().read_storage::(), + ) + .join() + .filter(|(_, _, ms)| { + if let comp::MountState::Unmounted = ms { + true + } else { + false + } + }) + .min_by_key(|(_, pos, _)| { + (player_pos.0.distance_squared(pos.0) * 1000.0) as i32 + }) + .map(|(entity, _, _)| entity); + + if let Some(mountee_uid) = + closest_mountable.and_then(|mountee_entity| { + client.state().ecs().uid_from_entity(mountee_entity) + }) + { + self.controller + .push_event(comp::ControlEvent::Mount(mountee_uid.into())); + } + } + } + } Event::InputUpdate(GameInput::Interact, state) => { let mut client = self.client.borrow_mut(); diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs index 66327d7fe4..df23337137 100644 --- a/voxygen/src/settings.rs +++ b/voxygen/src/settings.rs @@ -24,7 +24,12 @@ pub struct ControlSettings { pub move_back: KeyMouse, pub move_right: KeyMouse, pub jump: KeyMouse, + pub sit: KeyMouse, pub glide: KeyMouse, + pub climb: KeyMouse, + pub climb_down: KeyMouse, + pub wall_leap: KeyMouse, + pub mount: KeyMouse, pub map: KeyMouse, pub bag: KeyMouse, pub quest_log: KeyMouse, @@ -57,7 +62,12 @@ impl Default for ControlSettings { move_back: KeyMouse::Key(VirtualKeyCode::S), move_right: KeyMouse::Key(VirtualKeyCode::D), jump: KeyMouse::Key(VirtualKeyCode::Space), + sit: KeyMouse::Key(VirtualKeyCode::K), glide: KeyMouse::Key(VirtualKeyCode::LShift), + climb: KeyMouse::Key(VirtualKeyCode::Space), + climb_down: KeyMouse::Key(VirtualKeyCode::LShift), + wall_leap: KeyMouse::Mouse(MouseButton::Middle), + mount: KeyMouse::Key(VirtualKeyCode::F), map: KeyMouse::Key(VirtualKeyCode::M), bag: KeyMouse::Key(VirtualKeyCode::B), quest_log: KeyMouse::Key(VirtualKeyCode::L), diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 6676c98223..2bf555ca8f 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -19,7 +19,12 @@ pub enum GameInput { MoveLeft, MoveRight, Jump, + Sit, Glide, + Climb, + ClimbDown, + WallLeap, + Mount, Enter, Command, Escape, @@ -142,9 +147,24 @@ impl Window { map.entry(settings.controls.jump) .or_default() .push(GameInput::Jump); + map.entry(settings.controls.sit) + .or_default() + .push(GameInput::Sit); map.entry(settings.controls.glide) .or_default() .push(GameInput::Glide); + map.entry(settings.controls.climb) + .or_default() + .push(GameInput::Climb); + map.entry(settings.controls.climb_down) + .or_default() + .push(GameInput::ClimbDown); + map.entry(settings.controls.wall_leap) + .or_default() + .push(GameInput::WallLeap); + map.entry(settings.controls.mount) + .or_default() + .push(GameInput::Mount); map.entry(settings.controls.map) .or_default() .push(GameInput::Map);