commit 4ec1b9bdb71c44547e90bf9de58cef953a2bc885 Author: MrMelon Date: Sun Sep 12 00:25:10 2021 +0100 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aaea3dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +dist/ +package/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b54e66f --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +Copyright 2020 Sean + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5b82e43 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ + + +all: + make build-main + +build-main: + mkdir -p dist/ && go build -o dist/susapp . + +package: + ./scripts/package-susapp.sh + +deb: + make clean && \ + make all && \ + make package + +clean: + rm -rf dist/ && \ + rm -rf package/ + + +.PHONY: run +run: + make && \ + cd ./dist/ && \ + ./susapp diff --git a/assets/collision/airship.json b/assets/collision/airship.json new file mode 100644 index 0000000..be8788b --- /dev/null +++ b/assets/collision/airship.json @@ -0,0 +1 @@ +[[{"x":-16.03356,"y":-0.8336114},{"x":-16.42589,"y":-0.8337003},{"x":-16.42541,"y":0.9505637},{"x":-16.57034,"y":0.9829645},{"x":-16.81557,"y":1.143003},{"x":-16.95277,"y":1.327235},{"x":-17.52295,"y":1.328595},{"x":-17.7697,"y":1.2033},{"x":-19.55016,"y":1.209893},{"x":-19.76917,"y":1.341429},{"x":-20.50605,"y":1.334634},{"x":-21.49481,"y":0.9486741},{"x":-21.97912,"y":0.9304763},{"x":-22.04113,"y":0.8538507},{"x":-22.30273,"y":0.8202428},{"x":-22.61555,"y":0.7253085},{"x":-22.9622,"y":0.7579792},{"x":-23.17584,"y":0.6119347},{"x":-23.30625,"y":0.3478366},{"x":-23.789,"y":0.0429384},{"x":-24.16054,"y":-0.3017496},{"x":-24.50533,"y":-0.3955403},{"x":-24.74094,"y":-0.8068387},{"x":-24.80811,"y":-1.062746},{"x":-24.83992,"y":-1.312127},{"x":-24.80346,"y":-1.510181},{"x":-24.81113,"y":-1.915542},{"x":-24.86881,"y":-1.989878},{"x":-24.85874,"y":-2.201746},{"x":-24.83265,"y":-2.553008},{"x":-24.57797,"y":-2.659963},{"x":-24.22581,"y":-2.664566},{"x":-24.19022,"y":-2.795648},{"x":-24.39435,"y":-2.890892},{"x":-24.72721,"y":-2.980601},{"x":-24.60085,"y":-3.258168},{"x":-24.26596,"y":-3.551901},{"x":-23.75923,"y":-3.765429},{"x":-23.26645,"y":-3.895437},{"x":-22.51438,"y":-3.995637},{"x":-16.43034,"y":-3.992238},{"x":-16.44197,"y":-3.290086},{"x":-16.99006,"y":-3.25973},{"x":-17.10056,"y":-3.094273},{"x":-16.95137,"y":-2.99458},{"x":-16.42274,"y":-2.913776},{"x":-16.42371,"y":-1.629756},{"x":-16.06307,"y":-1.633793}],[{"x":-20.10272,"y":-1.295893},{"x":-20.11193,"y":-1.295943},{"x":-19.70463,"y":-1.296661},{"x":-19.60422,"y":-1.033932},{"x":-19.46632,"y":-0.9376636},{"x":-19.32085,"y":-1.019393},{"x":-19.17467,"y":-1.158321},{"x":-19.01633,"y":-1.214837},{"x":-18.67784,"y":-1.081395},{"x":-18.68177,"y":-0.9104306},{"x":-19.32391,"y":-0.7300696},{"x":-19.32608,"y":-0.5958933},{"x":-19.92985,"y":-0.5814762},{"x":-20.12269,"y":-0.8691257},{"x":-20.12657,"y":-1.30092}],[{"x":-16.3651,"y":-0.831529},{"x":-15.95491,"y":-0.8274707},{"x":-15.96028,"y":-0.3324225},{"x":-13.8947,"y":-0.3213733},{"x":-13.90263,"y":-0.1285609}],[{"x":-16.28759,"y":-1.668149},{"x":-15.94066,"y":-1.671024},{"x":-15.93988,"y":-1.829822},{"x":-11.56497,"y":-1.836438},{"x":-11.56514,"y":-2.698946}],[{"x":-12.81856,"y":-0.003014922},{"x":-12.82327,"y":-0.3569322},{"x":-8.911867,"y":-0.3470472},{"x":-8.911534,"y":-0.8289247},{"x":-8.498337,"y":-0.8312216}],[{"x":-10.38579,"y":-2.741977},{"x":-10.39086,"y":-1.834694},{"x":-8.893422,"y":-1.837674},{"x":-8.890488,"y":-1.636299},{"x":-8.533977,"y":-1.63752}],[{"x":-8.639915,"y":-7.992267},{"x":-9.020933,"y":-7.994788},{"x":-8.998542,"y":-9.290235},{"x":-9.796662,"y":-10.09782},{"x":-14.96192,"y":-10.10381},{"x":-15.59402,"y":-9.424212},{"x":-15.5827,"y":-8.194454},{"x":-14.77676,"y":-7.678015},{"x":-12.93738,"y":-7.682444},{"x":-13.9208,"y":-8.602203},{"x":-13.91713,"y":-9.233377},{"x":-13.09354,"y":-9.239123},{"x":-11.13614,"y":-7.195169},{"x":-11.13431,"y":-5.974114},{"x":-13.2091,"y":-3.545341},{"x":-13.79709,"y":-3.532378},{"x":-13.78797,"y":-4.244041},{"x":-12.73769,"y":-5.565},{"x":-14.50105,"y":-5.562207},{"x":-15.14126,"y":-4.667821},{"x":-15.15109,"y":-3.604205},{"x":-14.95765,"y":-3.48847},{"x":-14.29364,"y":-3.075033},{"x":-14.14488,"y":-2.702812},{"x":-11.56075,"y":-2.6872},{"x":-11.55719,"y":-2.134918}],[{"x":-10.38602,"y":-2.14999},{"x":-10.392,"y":-2.66634},{"x":-9.832569,"y":-2.683666},{"x":-9.014154,"y":-3.364316},{"x":-9.005865,"y":-7.186296},{"x":-8.599945,"y":-7.19381}],[{"x":-12.80776,"y":-0.1880744},{"x":-12.81205,"y":0.4562474},{"x":-12.0029,"y":0.4557033},{"x":-11.50556,"y":1.210751},{"x":-11.50788,"y":3.20061},{"x":-12.4618,"y":3.17539},{"x":-12.47126,"y":2.171422},{"x":-12.65883,"y":2.067768},{"x":-13.35524,"y":2.0438},{"x":-13.46784,"y":2.17068},{"x":-13.82986,"y":2.176549},{"x":-13.91401,"y":2.075269},{"x":-14.26823,"y":2.079014},{"x":-14.33605,"y":2.44395},{"x":-14.95626,"y":2.441928},{"x":-14.94309,"y":1.236872},{"x":-14.57808,"y":0.4582416},{"x":-13.90006,"y":0.4574582},{"x":-13.89166,"y":-0.2717739}],[{"x":-8.791619,"y":-1.640076},{"x":-8.477066,"y":-1.652369},{"x":-8.379571,"y":-1.808783},{"x":-0.445395,"y":-1.799595},{"x":-0.4410331,"y":-2.036575},{"x":-0.6242429,"y":-2.039573},{"x":-0.7431719,"y":-1.910856},{"x":-1.83415,"y":-1.89835},{"x":-1.60571,"y":-2.644397},{"x":-1.580572,"y":-3.069704},{"x":1.865649,"y":-3.065668},{"x":2.03713,"y":-2.778933},{"x":1.685836,"y":-2.576459},{"x":1.649244,"y":-2.385174},{"x":2.040159,"y":-2.118975},{"x":2.048617,"y":-1.91668},{"x":1.14473,"y":-1.913548},{"x":0.9921035,"y":-2.037229},{"x":0.8112863,"y":-2.034278},{"x":0.8117996,"y":-1.816262},{"x":2.797431,"y":-1.805758},{"x":2.803455,"y":-0.4749102},{"x":2.896795,"y":-0.3570683},{"x":3.030597,"y":-0.3568428},{"x":3.08345,"y":-0.4869919},{"x":3.083286,"y":-1.113721},{"x":4.010863,"y":-1.101774},{"x":4.008285,"y":-0.6730426},{"x":4.367944,"y":-0.6712218}],[{"x":-8.83287,"y":-0.8354387},{"x":-8.49282,"y":-0.8382605},{"x":-8.184837,"y":-0.733498},{"x":-8.186178,"y":-0.4499176},{"x":-8.452353,"y":-0.29589},{"x":-8.442046,"y":-0.03380224},{"x":-8.088207,"y":0.0967712},{"x":-7.92689,"y":0.3655184},{"x":-7.925612,"y":1.401187},{"x":-7.639612,"y":1.401898},{"x":-7.523113,"y":1.349831},{"x":-7.290526,"y":1.335646},{"x":-7.10547,"y":1.422877},{"x":-6.33165,"y":1.419261},{"x":-6.159031,"y":1.33948},{"x":-5.942391,"y":1.334746},{"x":-5.776771,"y":1.406438},{"x":-5.004841,"y":1.408416},{"x":-4.844049,"y":1.352117},{"x":-4.608784,"y":1.352113},{"x":-4.456556,"y":1.40971},{"x":-3.523595,"y":1.412577},{"x":-3.520935,"y":0.9213981},{"x":-2.465536,"y":0.8827403},{"x":-2.474633,"y":1.314879},{"x":-1.337851,"y":1.377257},{"x":-1.323959,"y":3.023837},{"x":-1.618507,"y":3.447274},{"x":-1.609065,"y":5.558088},{"x":-1.834859,"y":5.78409},{"x":-2.293582,"y":5.801613},{"x":-2.304999,"y":6.677835},{"x":-1.520619,"y":6.680501},{"x":-1.520418,"y":7.443104}],[{"x":-0.3275228,"y":7.319745},{"x":-0.3270569,"y":6.667881},{"x":0.01766863,"y":6.66899},{"x":0.01241617,"y":2.920892},{"x":0.04391556,"y":2.739981},{"x":0.03591204,"y":1.305636},{"x":0.1272773,"y":1.222615},{"x":0.2902149,"y":1.120902},{"x":0.2959781,"y":0.6275588},{"x":0.2466311,"y":0.3748338},{"x":0.4014665,"y":0.1959238},{"x":0.5784051,"y":0.08054905},{"x":0.5582557,"y":-0.1377602},{"x":0.6901353,"y":-0.2651669},{"x":0.813319,"y":-0.1825323},{"x":1.882595,"y":-0.1882137},{"x":1.886091,"y":-0.9860865},{"x":1.98844,"y":-1.07367},{"x":2.121817,"y":-0.9885357},{"x":2.079613,"y":0.3314859},{"x":4.0155,"y":0.3134927},{"x":4.017383,"y":0.1146271},{"x":4.410442,"y":0.1145072}],[{"x":-3.789423,"y":8.060939},{"x":-3.360394,"y":8.058386},{"x":-3.361936,"y":7.585318},{"x":-1.518651,"y":7.582849},{"x":-1.51629,"y":6.897281}],[{"x":-0.3276537,"y":6.740432},{"x":-0.3267135,"y":7.58643},{"x":2.348417,"y":7.593921},{"x":2.340089,"y":8.057134},{"x":2.838824,"y":8.065406}],[{"x":-3.854213,"y":8.853719},{"x":-3.356832,"y":8.854605},{"x":-3.324265,"y":9.093654},{"x":-2.802774,"y":9.424075},{"x":-1.606528,"y":9.42758},{"x":-1.398799,"y":9.331013},{"x":-1.240515,"y":9.30058},{"x":-1.124339,"y":9.321713},{"x":-0.8871441,"y":9.414958},{"x":0.2057831,"y":9.418169},{"x":0.353672,"y":9.342675},{"x":0.5148584,"y":9.281999},{"x":0.6768073,"y":9.316348},{"x":0.8391489,"y":9.421098},{"x":2.01583,"y":9.419906},{"x":2.169501,"y":9.29791},{"x":2.33303,"y":9.234224},{"x":2.33536,"y":8.86784},{"x":2.874809,"y":8.866095}],[{"x":-3.525622,"y":8.858198},{"x":-3.885725,"y":8.85726},{"x":-3.888912,"y":9.050066},{"x":-4.097179,"y":9.064512},{"x":-4.3266,"y":9.153421},{"x":-4.323749,"y":10.11809},{"x":-4.537128,"y":10.76189},{"x":-4.719747,"y":11.13734},{"x":-5.025217,"y":11.59097},{"x":-5.225776,"y":11.59822},{"x":-5.334115,"y":11.66203},{"x":-5.343767,"y":11.82},{"x":-5.692319,"y":12.23553},{"x":-6.077424,"y":12.52052},{"x":-6.623687,"y":12.75151},{"x":-6.782733,"y":12.6902},{"x":-6.89463,"y":12.7574},{"x":-6.892749,"y":12.8636},{"x":-7.074221,"y":12.98624},{"x":-7.754135,"y":13.16192},{"x":-8.523164,"y":13.23847},{"x":-8.684267,"y":13.13656},{"x":-8.85572,"y":13.14779},{"x":-8.938745,"y":13.25068},{"x":-9.333408,"y":13.24967},{"x":-10.03124,"y":13.14435},{"x":-10.7647,"y":12.90052},{"x":-10.87891,"y":12.73576},{"x":-11.00628,"y":12.7222},{"x":-11.10844,"y":12.7604},{"x":-11.46173,"y":12.59708},{"x":-11.73124,"y":12.40194},{"x":-11.7576,"y":12.16802},{"x":-11.90705,"y":12.01866},{"x":-12.1746,"y":11.93242},{"x":-12.32829,"y":11.7779},{"x":-12.35934,"y":11.59476},{"x":-12.63039,"y":11.56134},{"x":-12.83441,"y":11.41108},{"x":-13.14259,"y":10.93692},{"x":-13.32554,"y":10.52832},{"x":-13.30396,"y":9.197877},{"x":-13.46796,"y":9.05337},{"x":-13.4741,"y":8.031837},{"x":-13.42152,"y":7.49264},{"x":-13.22832,"y":6.830685},{"x":-13.00173,"y":6.34046},{"x":-12.61687,"y":5.833189},{"x":-12.38425,"y":5.733351},{"x":-12.23875,"y":5.453109},{"x":-11.69708,"y":5.035163},{"x":-11.22427,"y":4.761021},{"x":-10.90824,"y":4.712702},{"x":-10.71547,"y":4.532854},{"x":-10.38377,"y":4.426901},{"x":-9.824739,"y":4.294689},{"x":-9.326833,"y":4.237856},{"x":-8.720805,"y":4.275678},{"x":-8.27245,"y":4.235127},{"x":-7.612947,"y":4.337722},{"x":-7.018895,"y":4.504151},{"x":-6.868068,"y":4.650097},{"x":-6.766322,"y":4.68113},{"x":-6.624613,"y":4.695828},{"x":-6.114302,"y":4.944323},{"x":-5.643821,"y":5.269239},{"x":-5.340237,"y":5.5402},{"x":-5.296103,"y":5.742735},{"x":-5.100102,"y":5.787309},{"x":-4.989989,"y":5.899269},{"x":-4.74347,"y":6.223245},{"x":-4.45425,"y":6.776549},{"x":-4.261594,"y":7.296413},{"x":-4.163108,"y":7.869389},{"x":-3.914319,"y":7.86322},{"x":-3.910859,"y":8.064019},{"x":-3.560334,"y":8.060819}],[{"x":-7.714983,"y":5.43726},{"x":-7.359429,"y":5.553212},{"x":-6.982174,"y":5.757483},{"x":-6.377391,"y":6.213044},{"x":-6.001368,"y":6.623106},{"x":-5.650852,"y":7.170923}],[{"x":-8.266265,"y":7.298437},{"x":-8.267296,"y":5.851799}],[{"x":-7.57402,"y":7.917111},{"x":-6.056797,"y":7.926265},{"x":-6.136981,"y":7.756708},{"x":-7.462361,"y":7.77766},{"x":-7.554103,"y":7.916078}],[{"x":-9.918072,"y":7.925048},{"x":-11.50218,"y":7.915313},{"x":-11.36237,"y":7.781813},{"x":-10.11233,"y":7.77387},{"x":-9.910605,"y":7.927512}],[{"x":-9.312279,"y":7.313175},{"x":-9.311869,"y":5.828625}],[{"x":-11.93514,"y":7.345225},{"x":-11.71328,"y":6.926175},{"x":-11.47842,"y":6.583897},{"x":-11.19006,"y":6.266077},{"x":-10.82989,"y":5.958067},{"x":-10.4353,"y":5.706132},{"x":-10.16161,"y":5.56689},{"x":-9.851611,"y":5.456525}],[{"x":-7.526872,"y":8.955747},{"x":-6.029075,"y":8.950937}],[{"x":-5.488227,"y":9.383634},{"x":-5.570149,"y":9.376133},{"x":-5.696403,"y":9.719774},{"x":-5.939435,"y":10.11046},{"x":-6.258337,"y":10.48634},{"x":-6.576073,"y":10.77546},{"x":-6.862606,"y":10.97215},{"x":-7.260777,"y":11.2153},{"x":-7.580778,"y":11.33738},{"x":-7.754492,"y":11.54428},{"x":-7.416903,"y":11.44315},{"x":-6.986969,"y":11.22209},{"x":-6.677588,"y":11.0135},{"x":-6.294706,"y":10.71251},{"x":-6.038608,"y":10.43536},{"x":-5.839064,"y":10.15715},{"x":-5.673316,"y":9.863144},{"x":-5.548763,"y":9.577497},{"x":-5.476987,"y":9.375607}],[{"x":-8.265376,"y":9.51088},{"x":-8.263732,"y":11.1425}],[{"x":-8.886846,"y":-7.995717},{"x":-8.532104,"y":-7.997131},{"x":-8.532892,"y":-8.679031},{"x":-8.036824,"y":-8.664539},{"x":-8.043002,"y":-10.4675},{"x":-8.529308,"y":-10.46552},{"x":-8.539575,"y":-11.90405},{"x":-8.924159,"y":-11.90229}],[{"x":-8.925779,"y":-7.195223},{"x":-8.542553,"y":-7.196155},{"x":-8.525546,"y":-5.954915},{"x":-6.683332,"y":-5.956091},{"x":-6.678199,"y":-9.546827},{"x":-5.875857,"y":-9.545488},{"x":-5.882681,"y":-8.876453},{"x":-2.228813,"y":-8.873413},{"x":-2.202244,"y":-11.00626},{"x":-1.736679,"y":-11.01017},{"x":-1.748498,"y":-11.96629},{"x":-1.48267,"y":-11.96517}],[{"x":-8.88196,"y":-12.70318},{"x":-8.550611,"y":-12.70105},{"x":-8.546182,"y":-13.17776},{"x":-1.741881,"y":-13.16913},{"x":-1.748652,"y":-12.78946},{"x":-1.4775,"y":-12.79245}],[{"x":-8.643423,"y":-11.90162},{"x":-9.009799,"y":-11.90135},{"x":-9.008525,"y":-11.35979},{"x":-16.95227,"y":-11.36104},{"x":-16.95452,"y":-13.26016},{"x":-15.15803,"y":-13.26459},{"x":-15.01583,"y":-13.26782},{"x":-14.98855,"y":-13.07643},{"x":-14.6182,"y":-13.09101},{"x":-14.45284,"y":-13.14448},{"x":-14.44932,"y":-14.2475},{"x":-14.57198,"y":-14.41945},{"x":-14.59052,"y":-14.93393},{"x":-14.51265,"y":-15.13859},{"x":-14.31196,"y":-15.15483},{"x":-14.26082,"y":-15.28112},{"x":-14.2628,"y":-16.81508},{"x":-14.16203,"y":-16.92076},{"x":-13.67783,"y":-16.94882},{"x":-13.54266,"y":-16.81123},{"x":-13.53373,"y":-15.18775},{"x":-12.90455,"y":-15.18857},{"x":-12.76699,"y":-15.12105},{"x":-12.7116,"y":-14.99375},{"x":-12.71666,"y":-14.37298},{"x":-12.86688,"y":-14.27475},{"x":-12.85941,"y":-13.15348},{"x":-12.72164,"y":-13.08498},{"x":-12.31283,"y":-13.08239},{"x":-12.29272,"y":-13.24584},{"x":-10.43949,"y":-13.25609},{"x":-8.999346,"y":-13.27575},{"x":-8.994207,"y":-12.70773},{"x":-8.641171,"y":-12.70565}],[{"x":2.545268,"y":8.859646},{"x":2.923118,"y":8.85927},{"x":2.917882,"y":9.496401},{"x":3.329931,"y":9.43081},{"x":3.528259,"y":9.505051},{"x":3.661227,"y":9.701638},{"x":5.628694,"y":9.692849},{"x":5.973226,"y":9.471859},{"x":5.975547,"y":7.876566},{"x":5.758133,"y":7.731698},{"x":4.521097,"y":7.723544},{"x":4.528158,"y":6.634421},{"x":4.2855,"y":6.233686},{"x":2.927807,"y":6.228854},{"x":2.93457,"y":8.066785},{"x":2.602454,"y":8.064691}],[{"x":16.51671,"y":8.646623},{"x":16.17768,"y":8.646716},{"x":16.15307,"y":7.9775},{"x":15.05678,"y":7.974468},{"x":15.0619,"y":8.526336},{"x":14.97818,"y":8.712512},{"x":13.86187,"y":8.45585},{"x":13.80307,"y":8.290793},{"x":13.79954,"y":7.758854},{"x":11.10097,"y":7.751239},{"x":11.10399,"y":7.647718},{"x":10.50988,"y":7.646337},{"x":10.50865,"y":7.739585},{"x":9.735133,"y":7.738641},{"x":9.546408,"y":7.862723},{"x":9.543139,"y":9.509998},{"x":9.856339,"y":9.71242},{"x":13.79913,"y":9.716454},{"x":13.79992,"y":9.141828},{"x":13.85311,"y":9.046237},{"x":14.95903,"y":9.29871},{"x":15.05949,"y":9.457063},{"x":15.06446,"y":9.941607},{"x":16.16467,"y":9.946694},{"x":16.17507,"y":9.443351},{"x":16.5641,"y":9.438322}],[{"x":10.46487,"y":6.304631},{"x":14.24713,"y":6.270213},{"x":14.2531,"y":5.556904},{"x":10.48865,"y":5.568405},{"x":10.48424,"y":6.307353}],[{"x":4.084074,"y":0.1168281},{"x":4.456663,"y":0.1191483},{"x":4.456962,"y":0.5659752},{"x":5.623947,"y":0.564113},{"x":5.618522,"y":1.433488},{"x":5.475291,"y":1.438951},{"x":5.470914,"y":3.680179},{"x":6.078482,"y":3.669206},{"x":6.2293,"y":3.513},{"x":6.70134,"y":3.368061},{"x":7.000872,"y":3.33035},{"x":7.007857,"y":1.434924},{"x":6.804887,"y":1.437576},{"x":6.820045,"y":0.5788898},{"x":7.370389,"y":0.5779886},{"x":7.664962,"y":0.4793224},{"x":7.946116,"y":0.5770458},{"x":8.659927,"y":0.5758769},{"x":8.66353,"y":1.443222},{"x":8.090346,"y":1.441057},{"x":8.093046,"y":2.185557},{"x":8.451802,"y":2.308688},{"x":8.549082,"y":2.496261},{"x":9.209348,"y":2.496133},{"x":9.195152,"y":3.348684},{"x":9.377824,"y":3.368267},{"x":9.575262,"y":3.503911},{"x":9.686353,"y":3.596242},{"x":9.964178,"y":3.602835},{"x":10.14088,"y":3.492202},{"x":10.57477,"y":3.378777},{"x":10.57335,"y":2.329448},{"x":10.08278,"y":2.328432},{"x":10.08187,"y":1.43716},{"x":9.843735,"y":1.435984},{"x":9.840565,"y":0.5657735},{"x":10.48561,"y":0.5688565},{"x":10.77119,"y":0.4898677},{"x":11.07022,"y":0.5716506},{"x":11.70528,"y":0.5746858},{"x":11.71127,"y":1.425143},{"x":11.19475,"y":1.422741},{"x":11.19523,"y":2.827469},{"x":12.1708,"y":2.829023},{"x":12.1765,"y":2.457471},{"x":13.19447,"y":2.460473},{"x":13.20183,"y":1.618829},{"x":12.91092,"y":1.430835},{"x":12.9204,"y":0.5770671},{"x":13.48023,"y":0.5765182},{"x":13.81051,"y":0.4958534},{"x":14.12737,"y":0.5758836},{"x":14.74144,"y":0.5764709},{"x":14.74242,"y":1.424285},{"x":14.27012,"y":1.441725},{"x":14.29426,"y":3.498686},{"x":15.25976,"y":3.486726},{"x":15.26095,"y":2.604861},{"x":15.37234,"y":2.454789},{"x":15.99119,"y":2.45368},{"x":16.09971,"y":2.582138},{"x":16.35321,"y":2.57881},{"x":16.49044,"y":2.495174},{"x":16.71728,"y":2.417478},{"x":16.80511,"y":2.217203},{"x":16.63855,"y":2.037951},{"x":16.4264,"y":1.891837},{"x":16.33355,"y":1.675533},{"x":16.45134,"y":1.547075},{"x":16.3927,"y":1.432017},{"x":15.95241,"y":1.437393},{"x":15.94727,"y":0.5693587},{"x":17.25088,"y":0.572821},{"x":17.24765,"y":0.1191841},{"x":17.65965,"y":0.1213853}],[{"x":4.130811,"y":-0.6807295},{"x":4.454815,"y":-0.6857201},{"x":4.446455,"y":-1.000261},{"x":5.622482,"y":-1.000065},{"x":5.635356,"y":-1.904223},{"x":5.450109,"y":-2.019036},{"x":5.446249,"y":-4.150476},{"x":7.442302,"y":-4.147058},{"x":7.43352,"y":-1.880722},{"x":6.792991,"y":-1.878254},{"x":6.800576,"y":-1.000716},{"x":11.70343,"y":-0.9970887},{"x":11.71421,"y":-1.922409},{"x":11.53619,"y":-2.03492},{"x":11.53099,"y":-4.151629},{"x":12.41465,"y":-4.159852},{"x":12.4172,"y":-3.870794},{"x":13.34505,"y":-3.873369}],[{"x":13.23814,"y":-3.873506},{"x":13.51192,"y":-3.867576},{"x":13.51274,"y":-1.877506},{"x":12.90263,"y":-1.873911},{"x":12.91444,"y":-0.996398},{"x":17.25184,"y":-0.9903842},{"x":17.24835,"y":-0.6918265},{"x":17.57413,"y":-0.6859772}],[{"x":17.3673,"y":-0.6723289},{"x":17.63895,"y":-0.6722896},{"x":17.72919,"y":-0.7998894},{"x":17.71904,"y":-0.9896551},{"x":17.92686,"y":-0.985401},{"x":17.94152,"y":-1.267054},{"x":19.43565,"y":-1.280648},{"x":19.43298,"y":-0.780601},{"x":20.35838,"y":-0.7849787},{"x":20.69626,"y":-1.048021},{"x":20.6809,"y":-2.50104},{"x":24.59558,"y":-2.506082},{"x":24.59263,"y":-1.01672},{"x":24.84958,"y":-0.7467367},{"x":24.85712,"y":-0.008633208},{"x":25.21321,"y":-0.008343983}],[{"x":17.31441,"y":0.1223279},{"x":17.71636,"y":0.1285996},{"x":17.71715,"y":0.4492794},{"x":17.8786,"y":0.5616432},{"x":17.87829,"y":3.728341},{"x":16.89021,"y":3.721987},{"x":16.89806,"y":5.255869},{"x":18.35498,"y":5.251333},{"x":18.31859,"y":4.547088},{"x":17.42471,"y":4.537665},{"x":17.42406,"y":4.373316},{"x":18.37393,"y":4.389128},{"x":18.61214,"y":4.567176},{"x":19.1179,"y":4.830626},{"x":19.26223,"y":5.192926},{"x":19.26349,"y":6.04189}],[{"x":20.46556,"y":6.019821},{"x":20.46805,"y":5.180863},{"x":20.61453,"y":5.050004},{"x":20.72546,"y":4.974508},{"x":20.97356,"y":4.927974},{"x":20.99036,"y":4.217092},{"x":19.52394,"y":4.219594},{"x":19.52042,"y":3.914912},{"x":19.05264,"y":3.909714},{"x":19.05498,"y":1.085529},{"x":19.56385,"y":1.093853},{"x":19.66157,"y":1.037768},{"x":20.0708,"y":1.035189},{"x":20.0787,"y":0.8743849},{"x":23.23643,"y":0.8599467},{"x":23.45657,"y":1.109525},{"x":23.45831,"y":2.095474},{"x":20.36347,"y":2.091295},{"x":20.36407,"y":3.033036},{"x":24.71535,"y":3.02161},{"x":24.73088,"y":2.152309},{"x":24.59524,"y":2.061545},{"x":24.59386,"y":1.029787},{"x":24.81646,"y":1.031042},{"x":24.87513,"y":0.784597},{"x":25.24172,"y":0.7875271}],[{"x":16.22179,"y":9.449925},{"x":16.592,"y":9.438324},{"x":16.59107,"y":9.799691},{"x":16.87871,"y":9.794971},{"x":16.92276,"y":10.31483},{"x":17.01632,"y":10.65433},{"x":17.21138,"y":11.11462},{"x":17.39006,"y":11.29192},{"x":18.45865,"y":10.58858},{"x":18.91402,"y":10.84526},{"x":18.93848,"y":11.36767},{"x":18.21631,"y":11.9339},{"x":18.38113,"y":12.13898},{"x":18.97462,"y":12.31696},{"x":19.51716,"y":12.42964},{"x":19.68595,"y":12.28891},{"x":19.81967,"y":12.22006},{"x":19.98791,"y":12.2399},{"x":20.263,"y":12.43252},{"x":20.84539,"y":12.33231},{"x":21.57466,"y":11.97358},{"x":20.841,"y":11.53123},{"x":20.82343,"y":10.82067},{"x":21.34241,"y":10.61472},{"x":22.39914,"y":11.30403},{"x":22.61796,"y":10.99029},{"x":22.75916,"y":10.68193},{"x":22.85105,"y":10.24614},{"x":22.90274,"y":9.794479},{"x":23.72962,"y":9.795464},{"x":23.7276,"y":9.4373},{"x":24.12314,"y":9.44237}],[{"x":16.26713,"y":8.641379},{"x":16.5984,"y":8.641698},{"x":16.59088,"y":8.362716},{"x":16.85259,"y":8.350898},{"x":16.90724,"y":7.96319},{"x":17.04374,"y":7.638478},{"x":17.12429,"y":7.4979},{"x":17.2701,"y":7.363121},{"x":18.39698,"y":7.98732},{"x":18.75015,"y":7.645493},{"x":18.76575,"y":6.911261},{"x":18.09283,"y":6.498992},{"x":18.49096,"y":6.280388},{"x":18.97352,"y":6.11521},{"x":19.27451,"y":6.077439},{"x":19.27451,"y":5.385819}],[{"x":20.44999,"y":5.35021},{"x":20.45315,"y":6.072403},{"x":20.69072,"y":6.095522},{"x":21.06207,"y":6.208974},{"x":21.33657,"y":6.313317},{"x":21.72756,"y":6.524127},{"x":21.00678,"y":6.97053},{"x":21.03955,"y":7.697658},{"x":21.39902,"y":8.000195},{"x":22.51733,"y":7.360611},{"x":22.68085,"y":7.515319},{"x":22.83217,"y":7.828372},{"x":23.71497,"y":7.834609},{"x":23.71838,"y":8.643151},{"x":24.11422,"y":8.641211}],[{"x":18.82711,"y":9.06955},{"x":19.10436,"y":9.007864},{"x":19.47142,"y":8.891902},{"x":19.89151,"y":8.762231},{"x":20.26792,"y":8.893024},{"x":20.68389,"y":9.02286},{"x":20.94927,"y":9.026827},{"x":21.07219,"y":9.240953},{"x":20.87274,"y":9.32188},{"x":20.62355,"y":9.315363},{"x":20.16726,"y":9.579005},{"x":19.88674,"y":9.651817},{"x":19.60579,"y":9.591384},{"x":19.38572,"y":9.449701},{"x":19.14858,"y":9.328643},{"x":18.9145,"y":9.303246},{"x":18.71852,"y":9.193658},{"x":18.82066,"y":9.081324}],[{"x":23.83367,"y":8.656083},{"x":24.17862,"y":8.646399},{"x":24.18925,"y":5.929824},{"x":24.41528,"y":5.925169},{"x":24.55492,"y":5.789554},{"x":24.56074,"y":5.577405},{"x":24.51148,"y":4.556463},{"x":27.55369,"y":4.553153},{"x":27.54975,"y":4.833504},{"x":28.04312,"y":4.831136},{"x":28.04484,"y":4.660454},{"x":33.23555,"y":4.646424},{"x":33.23402,"y":3.796052}],[{"x":23.80908,"y":9.448383},{"x":24.18992,"y":9.442202},{"x":24.20322,"y":9.769654},{"x":24.34319,"y":9.795302},{"x":24.62013,"y":9.813514},{"x":24.78232,"y":9.883697},{"x":24.94532,"y":10.0641},{"x":27.52629,"y":10.05588},{"x":27.55009,"y":5.704739},{"x":28.04051,"y":5.702101},{"x":28.32124,"y":5.717602},{"x":28.4997,"y":5.997903},{"x":28.82189,"y":5.999148},{"x":28.81489,"y":6.844117},{"x":28.65712,"y":6.838084},{"x":28.66715,"y":7.54099},{"x":28.78553,"y":7.491501},{"x":28.9524,"y":7.492608},{"x":29.12366,"y":7.392945},{"x":29.27793,"y":7.40689},{"x":29.4008,"y":7.51863},{"x":29.46143,"y":7.724545},{"x":29.80367,"y":7.727155},{"x":29.81748,"y":6.834863},{"x":29.68183,"y":6.833421},{"x":29.68577,"y":5.984917},{"x":30.36588,"y":5.983824},{"x":30.36858,"y":6.826981},{"x":30.19855,"y":6.834031},{"x":30.20333,"y":7.760964},{"x":30.51846,"y":7.755438},{"x":30.58057,"y":7.550571},{"x":30.70789,"y":7.402945},{"x":30.82529,"y":7.387939},{"x":30.94207,"y":7.426145},{"x":31.06532,"y":7.577645},{"x":31.233,"y":7.540353},{"x":31.36461,"y":7.477885},{"x":31.36016,"y":7.455339},{"x":31.35309,"y":6.832047},{"x":31.22327,"y":6.82622},{"x":31.22967,"y":5.980998},{"x":31.85829,"y":5.98956},{"x":31.85102,"y":6.834255},{"x":31.67953,"y":6.837724},{"x":31.69362,"y":7.573009},{"x":31.85805,"y":7.522245},{"x":32.06666,"y":7.54718},{"x":32.17828,"y":7.454453},{"x":32.28524,"y":7.431738},{"x":32.48058,"y":7.493668},{"x":32.56321,"y":7.724831},{"x":32.8264,"y":7.720573},{"x":32.8418,"y":6.838792},{"x":32.72615,"y":6.834598},{"x":32.72024,"y":6.003712},{"x":33.34249,"y":5.998977},{"x":33.34361,"y":6.840816},{"x":33.18965,"y":6.830325},{"x":33.19407,"y":7.740204},{"x":33.44451,"y":7.748093},{"x":33.51972,"y":7.59394},{"x":33.61355,"y":7.471326},{"x":33.7496,"y":7.445954},{"x":33.91323,"y":7.541088},{"x":34.00943,"y":7.658882},{"x":34.16102,"y":7.554367},{"x":34.34412,"y":7.493284},{"x":34.33792,"y":6.842475},{"x":34.2001,"y":6.837014},{"x":34.20088,"y":6.001976},{"x":35.00393,"y":5.99776},{"x":34.99923,"y":4.660586},{"x":34.42128,"y":4.655818},{"x":34.43039,"y":3.775892}],[{"x":24.96806,"y":-0.008893895},{"x":25.29822,"y":-0.008040488},{"x":25.29513,"y":-0.4554807},{"x":26.51419,"y":-0.452412},{"x":26.54314,"y":-0.5716544},{"x":26.82701,"y":-0.5710855},{"x":26.82866,"y":1.079186},{"x":26.5874,"y":1.079621},{"x":26.53876,"y":1.007093},{"x":25.29172,"y":1.012162},{"x":25.30005,"y":0.7960464},{"x":24.92213,"y":0.7961385}],[{"x":29.96085,"y":-1.266877},{"x":29.58326,"y":-1.264201},{"x":29.58097,"y":-1.022481},{"x":28.34134,"y":-1.014801},{"x":28.25593,"y":-0.9411207},{"x":28.02172,"y":-0.9342293},{"x":28.01121,"y":-2.637663},{"x":28.2767,"y":-2.634093},{"x":28.30942,"y":-2.505496},{"x":29.58003,"y":-2.49714},{"x":29.5684,"y":-2.078724},{"x":29.93364,"y":-2.080135}],[{"x":4.370167,"y":-12.81249},{"x":4.710572,"y":-12.81282},{"x":4.707167,"y":-13.11517},{"x":6.50629,"y":-13.1103},{"x":6.495592,"y":-14.29286},{"x":5.247363,"y":-14.29567},{"x":5.243162,"y":-15.08895},{"x":8.385489,"y":-15.08733},{"x":9.752759,"y":-15.6652},{"x":9.775661,"y":-16.81787},{"x":10.7572,"y":-16.81301},{"x":10.75556,"y":-14.82494},{"x":9.504161,"y":-14.81039},{"x":8.373489,"y":-14.31838},{"x":7.676992,"y":-14.3109},{"x":7.678667,"y":-13.10184},{"x":9.33233,"y":-13.10819},{"x":9.337194,"y":-10.93457},{"x":10.69647,"y":-10.93515},{"x":10.69263,"y":-9.326107},{"x":11.43536,"y":-9.320176},{"x":11.43323,"y":-9.152807},{"x":11.98803,"y":-9.151772}],[{"x":4.32737,"y":-12.0095},{"x":4.727947,"y":-12.01022},{"x":4.69901,"y":-9.960796},{"x":7.284973,"y":-9.969092},{"x":7.377474,"y":-10.16938},{"x":7.53201,"y":-10.24158},{"x":7.732461,"y":-10.29306},{"x":7.831082,"y":-10.54931},{"x":8.012747,"y":-10.64477},{"x":8.200947,"y":-10.63187},{"x":8.325757,"y":-10.46359},{"x":8.447688,"y":-10.25914},{"x":8.649251,"y":-10.20599},{"x":8.840059,"y":-10.02825},{"x":9.179604,"y":-10.00085},{"x":9.375246,"y":-10.03905},{"x":9.622617,"y":-10.0322},{"x":9.596861,"y":-7.827077},{"x":9.733359,"y":-7.821468},{"x":9.726049,"y":-6.921235},{"x":9.193755,"y":-6.920204},{"x":9.201669,"y":-5.847182},{"x":11.45066,"y":-5.838676},{"x":11.45749,"y":-6.005068},{"x":11.7724,"y":-6.005147}],[{"x":11.91983,"y":-6.651103},{"x":11.43875,"y":-6.649197},{"x":11.43361,"y":-6.921025},{"x":10.89167,"y":-6.927078},{"x":10.89565,"y":-7.792764},{"x":11.38012,"y":-8.026577},{"x":11.39481,"y":-8.504828},{"x":11.8462,"y":-8.518177}],[{"x":7.803439,"y":-11.81633},{"x":7.972384,"y":-11.90594},{"x":8.191087,"y":-11.92311},{"x":8.41914,"y":-11.85654},{"x":8.559229,"y":-11.81633}],[{"x":29.63205,"y":-1.270287},{"x":30.04259,"y":-1.265967},{"x":30.04222,"y":-0.9589438},{"x":31.95887,"y":-0.9821733},{"x":31.98013,"y":-2.309789},{"x":32.03697,"y":-2.419954},{"x":32.13166,"y":-2.419305},{"x":32.19104,"y":-2.274939},{"x":32.18625,"y":-1.397807},{"x":32.65844,"y":-1.308104},{"x":32.80399,"y":-1.168286},{"x":32.81144,"y":-0.7221938},{"x":32.57572,"y":0.3165706},{"x":32.51032,"y":0.7073128},{"x":31.13059,"y":0.8734886},{"x":30.81089,"y":1.259735},{"x":30.03755,"y":1.423906},{"x":30.02572,"y":1.989071},{"x":30.85481,"y":1.985681},{"x":31.07541,"y":1.916072},{"x":31.31409,"y":1.983877},{"x":32.35394,"y":1.979551},{"x":32.38552,"y":2.087662},{"x":33.20528,"y":2.095015},{"x":33.20879,"y":2.99391},{"x":33.11339,"y":3.133411},{"x":33.0251,"y":3.022423},{"x":33.01538,"y":2.703999},{"x":32.41338,"y":2.689663},{"x":32.41671,"y":3.773757},{"x":33.23489,"y":3.781551},{"x":33.23623,"y":4.150618}],[{"x":34.41613,"y":4.105896},{"x":34.42494,"y":3.77648},{"x":35.55035,"y":3.76019},{"x":35.56692,"y":2.703616},{"x":34.76677,"y":2.709988},{"x":34.7392,"y":3.057398},{"x":34.65187,"y":3.164597},{"x":34.55305,"y":3.062527},{"x":34.52196,"y":2.097046},{"x":35.55614,"y":2.10332},{"x":35.57702,"y":2.011288},{"x":36.37502,"y":1.993334},{"x":36.48242,"y":1.913596},{"x":36.60581,"y":1.895214},{"x":36.76078,"y":1.909804},{"x":36.77514,"y":1.59312},{"x":37.71261,"y":1.590941},{"x":37.71786,"y":1.509012},{"x":39.59365,"y":1.496941},{"x":39.58691,"y":1.273642},{"x":39.76616,"y":1.277226},{"x":39.76548,"y":-0.3858772},{"x":39.59784,"y":-0.3800054},{"x":39.60247,"y":-2.166032},{"x":39.78012,"y":-2.162822},{"x":39.77282,"y":-3.743397},{"x":39.56306,"y":-3.749745},{"x":39.55266,"y":-4.141446},{"x":38.96266,"y":-3.7969},{"x":38.1321,"y":-4.17856},{"x":36.65881,"y":-4.175798},{"x":36.66886,"y":-4.021609},{"x":35.60856,"y":-3.336062},{"x":35.88046,"y":-3.101389},{"x":36.27645,"y":-3.269305},{"x":36.76783,"y":-2.843386},{"x":36.7737,"y":-2.629478},{"x":37.73983,"y":-2.600035},{"x":37.82119,"y":-2.467725},{"x":38.26999,"y":-2.494005},{"x":38.56419,"y":-2.176709},{"x":38.5711,"y":-1.834906},{"x":37.71861,"y":-1.686539},{"x":37.5928,"y":-1.414512},{"x":36.17735,"y":-1.44299},{"x":36.04073,"y":-1.527819},{"x":35.52038,"y":-1.306454},{"x":34.1306,"y":-2.346388},{"x":34.1545,"y":-2.921303},{"x":33.87265,"y":-2.9713},{"x":33.64423,"y":-3.263165},{"x":33.55372,"y":-3.504774},{"x":33.66428,"y":-3.626854},{"x":33.96923,"y":-3.622187},{"x":33.98045,"y":-3.862042},{"x":33.71236,"y":-4.074553},{"x":33.69054,"y":-4.181582},{"x":33.13437,"y":-4.17722},{"x":33.13283,"y":-4.952418}],[{"x":29.67477,"y":-2.080447},{"x":30.03953,"y":-2.077792},{"x":30.04152,"y":-2.409874},{"x":31.00941,"y":-2.42762},{"x":31.01302,"y":-1.710206},{"x":31.095,"y":-1.632653},{"x":31.22884,"y":-1.758518},{"x":31.21152,"y":-3.011549},{"x":30.011,"y":-3.006915},{"x":30.02134,"y":-4.181694},{"x":31.95038,"y":-4.181936},{"x":31.94147,"y":-4.982609}],[{"x":11.66818,"y":-6.010667},{"x":12.04166,"y":-6.007255},{"x":12.35067,"y":-5.906975},{"x":12.48136,"y":-5.758442},{"x":12.55682,"y":-5.650322},{"x":12.55944,"y":-5.534685},{"x":13.19915,"y":-5.538996},{"x":13.21112,"y":-5.651604},{"x":13.95297,"y":-5.650913},{"x":14.04543,"y":-5.825204},{"x":14.23148,"y":-5.935197},{"x":14.44239,"y":-6.021726},{"x":15.01721,"y":-6.018828},{"x":15.01684,"y":-5.70316},{"x":15.28448,"y":-5.703488},{"x":15.28519,"y":-5.811196},{"x":17.15269,"y":-5.803948},{"x":17.28468,"y":-5.882973},{"x":17.5261,"y":-6.025017},{"x":18.10297,"y":-6.022561},{"x":18.0975,"y":-5.648492},{"x":18.7179,"y":-5.646848},{"x":18.70556,"y":-4.48939},{"x":17.24664,"y":-4.493034},{"x":17.25615,"y":-4.160796},{"x":17.94451,"y":-3.686165},{"x":20.40288,"y":-3.68711},{"x":20.3946,"y":-4.502329},{"x":19.90919,"y":-4.501302},{"x":19.93807,"y":-5.657718},{"x":20.19437,"y":-5.860076},{"x":20.4719,"y":-5.860795},{"x":20.48564,"y":-6.912728},{"x":19.93501,"y":-6.927544},{"x":19.9352,"y":-8.054311},{"x":21.10738,"y":-8.076041},{"x":21.10897,"y":-8.340407},{"x":21.48705,"y":-8.339218}],[{"x":11.62584,"y":-6.63205},{"x":12.04062,"y":-6.632315},{"x":12.03518,"y":-6.918284},{"x":12.6667,"y":-6.923772},{"x":12.66561,"y":-8.04015},{"x":12.04936,"y":-8.049082},{"x":12.04692,"y":-8.509334},{"x":11.50758,"y":-8.515589}],[{"x":11.53321,"y":-9.143434},{"x":12.03702,"y":-9.139459},{"x":12.03226,"y":-9.355042},{"x":12.561,"y":-9.349961},{"x":12.66747,"y":-9.301188},{"x":12.73792,"y":-9.301228},{"x":12.79714,"y":-9.398258},{"x":12.76946,"y":-11.04868},{"x":12.83964,"y":-11.21926},{"x":13.16295,"y":-11.49791},{"x":15.06858,"y":-11.51625},{"x":15.15434,"y":-11.73109},{"x":17.51911,"y":-11.74314},{"x":17.56006,"y":-11.58142},{"x":18.07401,"y":-11.58419},{"x":18.11588,"y":-11.73979},{"x":20.48231,"y":-11.73459},{"x":20.47447,"y":-10.47591},{"x":19.93372,"y":-10.47463},{"x":19.9336,"y":-9.305803},{"x":21.08979,"y":-9.313205},{"x":21.13617,"y":-9.166745},{"x":21.4399,"y":-9.133141}],[{"x":13.89004,"y":-6.912827},{"x":14.43694,"y":-6.909325},{"x":14.47433,"y":-6.679531},{"x":14.99404,"y":-6.673645},{"x":15.03087,"y":-6.902806},{"x":15.71456,"y":-6.904119},{"x":15.69517,"y":-8.065161},{"x":15.01492,"y":-8.067219},{"x":15.00838,"y":-8.476792},{"x":14.43446,"y":-8.486504},{"x":14.3189,"y":-8.359094},{"x":14.1006,"y":-8.3048},{"x":14.01884,"y":-8.088633},{"x":13.90464,"y":-8.079865},{"x":13.88788,"y":-6.927657}],[{"x":13.88314,"y":-6.910269},{"x":14.43694,"y":-6.909325},{"x":14.48723,"y":-6.675228},{"x":15.01555,"y":-6.673645},{"x":15.03087,"y":-6.902806},{"x":15.63477,"y":-6.919079},{"x":15.63533,"y":-8.050201},{"x":15.04115,"y":-8.071589},{"x":15.05208,"y":-8.485534},{"x":14.43446,"y":-8.486504},{"x":14.42776,"y":-8.062546},{"x":13.89032,"y":-8.043935},{"x":13.88341,"y":-6.905281}],[{"x":13.87022,"y":-6.909733},{"x":14.46705,"y":-6.913627},{"x":14.49499,"y":-6.745507},{"x":15.01125,"y":-6.744769},{"x":15.03087,"y":-6.902805},{"x":15.62678,"y":-6.914086},{"x":15.62759,"y":-8.054941},{"x":15.44877,"y":-8.071518},{"x":15.36105,"y":-8.282927},{"x":15.18491,"y":-8.326846},{"x":15.05208,"y":-8.485534},{"x":14.43446,"y":-8.486504},{"x":14.42776,"y":-8.062546},{"x":13.89279,"y":-8.039896},{"x":13.88036,"y":-6.904354}],[{"x":15.68829,"y":-9.332767},{"x":15.69585,"y":-10.46734},{"x":15.44886,"y":-10.46865},{"x":15.36264,"y":-10.70482},{"x":15.11582,"y":-10.87538},{"x":14.09668,"y":-10.84296},{"x":13.82293,"y":-10.68182},{"x":13.72097,"y":-10.52894},{"x":13.70159,"y":-9.34051},{"x":13.76106,"y":-9.264834},{"x":13.86623,"y":-9.280823},{"x":13.92744,"y":-9.325546},{"x":14.4407,"y":-9.326488},{"x":14.48466,"y":-9.16911},{"x":14.9952,"y":-9.160357},{"x":15.02256,"y":-9.327901},{"x":15.69389,"y":-9.336746}],[{"x":21.1508,"y":-8.346367},{"x":21.53413,"y":-8.347633},{"x":21.51758,"y":-6.341946},{"x":21.68291,"y":-6.316859},{"x":21.80279,"y":-6.191787},{"x":21.77194,"y":-5.863787},{"x":22.00528,"y":-5.733915},{"x":22.24481,"y":-5.835661},{"x":22.52473,"y":-5.634049},{"x":22.3841,"y":-5.348256},{"x":22.64115,"y":-5.3192},{"x":22.82317,"y":-5.09792},{"x":26.05718,"y":-5.102329},{"x":26.13982,"y":-5.173426},{"x":26.26974,"y":-5.247285},{"x":26.391,"y":-5.441343},{"x":26.7892,"y":-5.445518},{"x":26.79752,"y":-5.072999},{"x":27.59477,"y":-5.081031},{"x":27.73421,"y":-5.213257},{"x":27.85027,"y":-5.249107},{"x":29.97207,"y":-5.241584},{"x":30.18326,"y":-5.098794},{"x":30.96158,"y":-5.085246},{"x":31.06723,"y":-5.176783},{"x":31.18582,"y":-5.216821},{"x":31.31787,"y":-5.174421},{"x":31.41771,"y":-5.086376},{"x":31.95201,"y":-5.083991},{"x":31.94058,"y":-4.486775}],[{"x":21.19219,"y":-9.143792},{"x":21.5288,"y":-9.140752},{"x":21.52507,"y":-9.744277},{"x":22.34755,"y":-10.59708},{"x":25.82591,"y":-10.59993},{"x":26.38422,"y":-9.92147},{"x":26.37686,"y":-9.360614},{"x":26.01472,"y":-9.286954},{"x":25.78516,"y":-9.140631},{"x":25.78226,"y":-8.225286},{"x":24.68164,"y":-8.254874},{"x":24.68,"y":-9.103557},{"x":24.51435,"y":-9.20449},{"x":24.13246,"y":-9.212402},{"x":23.95899,"y":-9.035179},{"x":23.94953,"y":-8.224118},{"x":23.61876,"y":-7.977326},{"x":23.6324,"y":-6.948333},{"x":23.96543,"y":-6.721925},{"x":26.33543,"y":-6.723498},{"x":26.33204,"y":-6.314115},{"x":26.79491,"y":-6.313346},{"x":26.78657,"y":-6.72128},{"x":27.36106,"y":-6.721763},{"x":27.37912,"y":-6.894475},{"x":28.02413,"y":-6.899055},{"x":28.30912,"y":-7.137161},{"x":29.87564,"y":-7.137565},{"x":30.20742,"y":-6.86943},{"x":30.79156,"y":-6.84772},{"x":30.82874,"y":-6.585979},{"x":33.31768,"y":-6.595332},{"x":33.32219,"y":-5.098347},{"x":33.1452,"y":-5.106394},{"x":33.13522,"y":-4.403719}],[{"x":3.911768,"y":13.85266},{"x":3.904901,"y":14.37323},{"x":3.055537,"y":14.36932},{"x":3.055922,"y":16.17195},{"x":5.002434,"y":16.1563},{"x":5.19547,"y":16.04318},{"x":5.425587,"y":16.00746},{"x":5.737369,"y":16.10522},{"x":5.825475,"y":16.15562},{"x":6.264071,"y":16.14885},{"x":6.264014,"y":15.87451},{"x":7.026298,"y":15.87314},{"x":7.040138,"y":15.64909},{"x":7.854608,"y":15.6553},{"x":7.853753,"y":16.15864},{"x":14.36638,"y":16.14839},{"x":14.36831,"y":15.49349},{"x":14.80551,"y":15.48425},{"x":14.82727,"y":15.85531},{"x":15.69049,"y":15.86464},{"x":15.83488,"y":15.94158},{"x":15.87892,"y":16.13076},{"x":16.50696,"y":16.14207},{"x":16.51411,"y":15.76042},{"x":17.33245,"y":15.75774},{"x":17.34869,"y":15.27619},{"x":17.49837,"y":15.2232},{"x":17.49736,"y":14.87859},{"x":17.37148,"y":14.82247},{"x":17.37835,"y":14.2142},{"x":14.79822,"y":14.2169},{"x":14.79074,"y":14.54793},{"x":14.37511,"y":14.54386},{"x":14.37879,"y":13.67128},{"x":7.868568,"y":13.67035},{"x":7.868159,"y":14.25339},{"x":7.039716,"y":14.26473},{"x":7.038588,"y":13.67892},{"x":6.098576,"y":13.67701},{"x":6.085096,"y":14.3654},{"x":5.136877,"y":14.35259},{"x":5.132302,"y":13.84139},{"x":3.901567,"y":13.84832}],[{"x":8.585021,"y":14.63922},{"x":8.585626,"y":15.49938},{"x":8.980776,"y":15.56786},{"x":9.390569,"y":15.46082},{"x":9.824985,"y":15.58001},{"x":10.2297,"y":15.4572},{"x":10.628,"y":15.58025},{"x":11.00074,"y":15.44356},{"x":11.37054,"y":15.56585},{"x":11.83204,"y":15.45158},{"x":12.23334,"y":15.59741},{"x":12.63161,"y":15.43584},{"x":13.01441,"y":15.61513},{"x":13.26388,"y":15.59196},{"x":13.4397,"y":15.47673},{"x":13.45238,"y":14.58945},{"x":13.17107,"y":14.44966},{"x":12.91651,"y":14.45591},{"x":12.6266,"y":14.67003},{"x":12.24875,"y":14.45107},{"x":11.80452,"y":14.66617},{"x":11.39094,"y":14.45237},{"x":10.99775,"y":14.66232},{"x":10.60459,"y":14.45356},{"x":10.21147,"y":14.70947},{"x":9.833596,"y":14.45473},{"x":9.404706,"y":14.68518},{"x":8.991151,"y":14.45601},{"x":8.695064,"y":14.45646},{"x":8.588435,"y":14.64316}],[{"x":-1.65789,"y":-12.76731},{"x":-1.316503,"y":-12.76596},{"x":-1.316166,"y":-13.14125},{"x":4.247796,"y":-13.1429},{"x":4.249872,"y":-12.81367},{"x":4.588986,"y":-12.81202}],[{"x":-1.701628,"y":-11.95378},{"x":-1.312271,"y":-11.95691},{"x":-1.250281,"y":-11.7014},{"x":4.178205,"y":-11.70233},{"x":4.203038,"y":-12.00892},{"x":4.617874,"y":-12.00456}],[{"x":-9.862005,"y":-3.885463},{"x":-11.53702,"y":-3.885463},{"x":-11.53702,"y":-4.412988},{"x":-9.862005,"y":-4.412988},{"x":-9.862005,"y":-3.885463}],[{"x":-9.862005,"y":-8.798404},{"x":-11.53702,"y":-8.798404},{"x":-11.53702,"y":-9.311112},{"x":-9.862005,"y":-9.311112},{"x":-9.862005,"y":-8.798404}],[{"x":-9.750773,"y":6.649997},{"x":-10.23127,"y":6.649997},{"x":-10.23127,"y":6.248957},{"x":-9.750773,"y":6.248957},{"x":-9.750773,"y":6.649997}],[{"x":-10.66876,"y":7.337173},{"x":-11.14925,"y":7.337173},{"x":-11.14925,"y":6.93937},{"x":-10.66876,"y":6.93937},{"x":-10.66876,"y":7.337173}],[{"x":-3.049332,"y":-10.10524},{"x":-5.352185,"y":-10.10524},{"x":-5.352185,"y":-10.759},{"x":-3.049332,"y":-10.759},{"x":-3.049332,"y":-10.10524}],[{"x":-3.049332,"y":-11.89438},{"x":-5.352185,"y":-11.89438},{"x":-5.352185,"y":-12.515},{"x":-3.049332,"y":-12.515},{"x":-3.049332,"y":-11.89438}],[{"x":23.09994,"y":0.06535864},{"x":20.56582,"y":0.06535864},{"x":20.56582,"y":-0.1327237},{"x":23.09994,"y":-0.1327237},{"x":23.09994,"y":0.06535864}],[{"x":23.4185,"y":-1.03898},{"x":21.84005,"y":-1.03898},{"x":21.84005,"y":-1.916657},{"x":23.4185,"y":-1.916657},{"x":23.4185,"y":-1.03898}],[{"x":26.48842,"y":9.075023},{"x":25.31991,"y":9.075023},{"x":25.31991,"y":7.648748},{"x":26.48842,"y":7.648748},{"x":26.48842,"y":9.075023}],[{"x":26.48842,"y":6.588984},{"x":25.31991,"y":6.588984},{"x":25.31991,"y":5.19517},{"x":26.48842,"y":5.19517},{"x":26.48842,"y":6.588984}],[{"x":6.719895,"y":-11.36304},{"x":5.47974,"y":-11.36304},{"x":5.47974,"y":-11.81518},{"x":6.719895,"y":-11.81518},{"x":6.719895,"y":-11.36304}],[{"x":8.738053,"y":-11.35507},{"x":7.510914,"y":-11.35507},{"x":7.510914,"y":-11.81518},{"x":8.738053,"y":-11.81518},{"x":8.738053,"y":-11.35507}],[{"x":-0.3291866,"y":0},{"x":0.3291866,"y":0.3291866},{"x":0.6583732,"y":0},{"x":0.3291866,"y":0.3291866},{"x":-0.6583732,"y":0}],[{"x":-0.2542553,"y":0},{"x":0.2542553,"y":0.2542553},{"x":0.5085106,"y":0},{"x":0.2542553,"y":0.2542553},{"x":-0.5085106,"y":0}],[{"x":13.24433,"y":-4.499919},{"x":13.24878,"y":-5.010986},{"x":14.49186,"y":-5.016655},{"x":14.49051,"y":-5.261805},{"x":14.98502,"y":-5.262676},{"x":14.98584,"y":-5.009169},{"x":15.32951,"y":-5.008646},{"x":15.33351,"y":-5.205608},{"x":16.98253,"y":-5.187002},{"x":16.9779,"y":-5.00614},{"x":17.56651,"y":-5.005245},{"x":17.57601,"y":-5.257058},{"x":18.06361,"y":-5.255412},{"x":18.06785,"y":-5.001053},{"x":18.68115,"y":-5.000103},{"x":18.67963,"y":-4.52184},{"x":17.02291,"y":-4.528776},{"x":17.01885,"y":-3.042992},{"x":20.45203,"y":-3.045387},{"x":20.47937,"y":-4.531784},{"x":19.95031,"y":-4.533699},{"x":19.95813,"y":-5.00501},{"x":20.54358,"y":-5.01227},{"x":20.54195,"y":-6.947409},{"x":19.94732,"y":-6.945374},{"x":19.95498,"y":-7.420257},{"x":21.13659,"y":-7.425197},{"x":21.13324,"y":-7.627992},{"x":21.49961,"y":-7.636}],[{"x":11.50533,"y":-9.172968},{"x":11.98279,"y":-9.176331},{"x":11.98922,"y":-9.375989},{"x":12.63209,"y":-9.375942},{"x":12.67537,"y":-9.300284},{"x":12.74272,"y":-9.306814},{"x":12.75727,"y":-9.359571},{"x":12.73983,"y":-11.0426},{"x":12.75965,"y":-11.10165},{"x":12.77119,"y":-11.19073},{"x":12.83158,"y":-11.22746},{"x":12.83995,"y":-11.28305},{"x":12.88717,"y":-11.3183},{"x":12.92459,"y":-11.39211},{"x":12.9953,"y":-11.42959},{"x":13.03014,"y":-11.47267},{"x":13.11683,"y":-11.48415},{"x":13.21314,"y":-11.53158},{"x":14.9728,"y":-11.53044},{"x":15.04783,"y":-11.54779},{"x":15.08521,"y":-11.57961},{"x":15.05895,"y":-11.61776},{"x":15.01808,"y":-11.67069},{"x":15.01304,"y":-11.77359},{"x":17.57772,"y":-11.77746},{"x":17.56416,"y":-11.59646},{"x":18.05105,"y":-11.59746},{"x":18.05142,"y":-11.77575},{"x":20.53649,"y":-11.77254},{"x":20.54081,"y":-9.841003},{"x":19.95102,"y":-9.831125},{"x":19.94536,"y":-9.357937},{"x":21.14175,"y":-9.348281},{"x":21.13952,"y":-9.19059},{"x":21.4812,"y":-9.189327}],[{"x":12.52561,"y":-4.56645},{"x":12.52597,"y":-5.008878},{"x":11.9911,"y":-5.009898},{"x":11.98614,"y":-5.249488},{"x":11.49835,"y":-5.251798}],[{"x":18.06502,"y":-6.945622},{"x":18.67189,"y":-6.941694},{"x":18.68556,"y":-7.4117},{"x":18.0414,"y":-7.428703},{"x":18.05231,"y":-7.733563},{"x":17.58204,"y":-7.742327},{"x":17.57026,"y":-7.412163},{"x":16.94886,"y":-7.41368},{"x":16.94239,"y":-6.941051},{"x":17.58263,"y":-6.939175},{"x":17.58446,"y":-6.696082},{"x":18.06134,"y":-6.696437},{"x":18.06373,"y":-6.947431}],[{"x":13.91727,"y":-9.354623},{"x":14.48683,"y":-9.352476},{"x":14.48688,"y":-9.185989},{"x":14.97638,"y":-9.187501},{"x":14.97462,"y":-9.350637},{"x":15.69605,"y":-9.347918},{"x":15.68239,"y":-9.825948},{"x":14.9872,"y":-9.836722},{"x":14.98026,"y":-10.06423},{"x":15.04466,"y":-10.12155},{"x":15.04745,"y":-10.16676},{"x":14.97416,"y":-10.19373},{"x":14.67902,"y":-10.18203},{"x":14.32132,"y":-10.18746},{"x":14.04608,"y":-10.15917},{"x":13.87163,"y":-10.05492},{"x":13.80922,"y":-9.98138},{"x":13.74488,"y":-9.852007},{"x":13.75978,"y":-9.417733},{"x":13.74012,"y":-9.351611},{"x":13.76781,"y":-9.289933},{"x":13.8448,"y":-9.306457},{"x":13.87092,"y":-9.340744},{"x":13.91896,"y":-9.356215}],[{"x":11.51666,"y":-7.752146},{"x":11.98988,"y":-7.755126},{"x":11.98642,"y":-7.42875},{"x":12.64315,"y":-7.4232},{"x":12.65182,"y":-6.945045},{"x":11.98836,"y":-6.930521},{"x":11.98985,"y":-6.679015},{"x":11.64352,"y":-6.681569}],[{"x":18.04378,"y":-9.351138},{"x":18.67557,"y":-9.353913},{"x":18.68013,"y":-9.838058},{"x":18.06372,"y":-9.842699},{"x":18.0513,"y":-10.15337},{"x":17.56822,"y":-10.15438},{"x":17.57474,"y":-9.839076},{"x":16.9479,"y":-9.835162},{"x":16.94909,"y":-9.349567},{"x":17.57149,"y":-9.347048},{"x":17.57037,"y":-9.181834},{"x":18.04671,"y":-9.183824},{"x":18.05039,"y":-9.350534}],[{"x":14.49625,"y":-6.924356},{"x":13.90842,"y":-6.937685},{"x":13.91825,"y":-7.427229},{"x":14.48365,"y":-7.42862},{"x":14.49392,"y":-7.749632},{"x":14.98278,"y":-7.738917},{"x":14.98528,"y":-7.429673},{"x":15.66884,"y":-7.423855},{"x":15.66691,"y":-6.938859},{"x":14.97106,"y":-6.935668},{"x":14.97844,"y":-6.703414},{"x":14.49264,"y":-6.700523},{"x":14.49782,"y":-6.924356}],[{"x":-2.535742,"y":0.8500072},{"x":-2.049354,"y":0.2889237},{"x":-2.341152,"y":-0.1494614},{"x":-4.450888,"y":-0.1274282},{"x":-6.332565,"y":0.04715132},{"x":-6.0899,"y":0.4930227},{"x":-5.613416,"y":0.8124823},{"x":-3.814947,"y":0.8868923},{"x":-2.468606,"y":0.8575804}],[{"x":34.75221,"y":-0.4173391},{"x":37.05008,"y":-0.4006316},{"x":37.04756,"y":0.04533567},{"x":36.83799,"y":0.2537852},{"x":36.00438,"y":0.2639198},{"x":35.91608,"y":0.4882321},{"x":34.75167,"y":0.4806468},{"x":34.62226,"y":-0.2338717},{"x":34.75784,"y":-0.425309}],[{"x":-2.6355,"y":-0.5922},{"x":-1.787077,"y":-0.253946},{"x":-1.6275,"y":-0.0952},{"x":-1.817253,"y":0.3178226},{"x":-2.393979,"y":0.9440416},{"x":-5.896077,"y":0.9261262},{"x":-6.5835,"y":0.2478},{"x":-6.6325,"y":-0.1442},{"x":-5.7645,"y":-0.6061999},{"x":-2.6355,"y":-0.5922}],[{"x":-5.974792,"y":-2.229493},{"x":-5.71295,"y":-1.798917},{"x":-2.441465,"y":-1.779338},{"x":-2.137287,"y":-2.116978},{"x":-1.82655,"y":-2.507366},{"x":-1.889125,"y":-2.759274},{"x":-2.679174,"y":-3.243704},{"x":-6.227367,"y":-3.306208},{"x":-6.56071,"y":-2.783395},{"x":-6.484576,"y":-2.538279},{"x":-5.943035,"y":-2.210702}],[{"x":-6.516907,"y":6.984062},{"x":-6.733539,"y":6.920181},{"x":-6.969766,"y":6.987423},{"x":-6.738074,"y":7.085166},{"x":-6.516907,"y":6.984062}],[{"x":-6.486807,"y":9.517362},{"x":-6.703438,"y":9.453481},{"x":-6.939665,"y":9.520723},{"x":-6.707974,"y":9.618465},{"x":-6.486807,"y":9.517362}],[{"x":-7.227407,"y":10.26986},{"x":-7.444038,"y":10.20598},{"x":-7.680265,"y":10.27322},{"x":-7.448574,"y":10.37097},{"x":-7.227407,"y":10.26986}]] \ No newline at end of file diff --git a/assets/collision/mira.json b/assets/collision/mira.json new file mode 100644 index 0000000..b08442d --- /dev/null +++ b/assets/collision/mira.json @@ -0,0 +1 @@ +[[{"x":3.71337,"y":-2.238192},{"x":3.687777,"y":-2.593762},{"x":-6.192677,"y":-2.624138},{"x":-6.184436,"y":-0.03365517},{"x":-6.713911,"y":0.02841848},{"x":-6.665236,"y":4.055113},{"x":-2.499738,"y":4.00852},{"x":-2.52292,"y":3.110758},{"x":-2.509094,"y":2.238056},{"x":-2.771595,"y":2.170679},{"x":-2.80248,"y":-0.7535518},{"x":3.695479,"y":-0.6665705}],[{"x":7.211442,"y":2.161355},{"x":8.762964,"y":2.16075},{"x":8.751773,"y":5.226181},{"x":11.12137,"y":5.24228},{"x":11.12562,"y":4.513843},{"x":11.26416,"y":4.509835},{"x":11.25983,"y":5.654586}],[{"x":3.695692,"y":-2.227394},{"x":13.57364,"y":-2.236449},{"x":13.57352,"y":-0.3868256},{"x":13.71494,"y":-0.3952844},{"x":13.70653,"y":-2.223386},{"x":17.07831,"y":-2.222195},{"x":17.08636,"y":1.597083},{"x":13.69615,"y":1.581925},{"x":13.68949,"y":0.692555},{"x":13.57186,"y":0.69209},{"x":13.56429,"y":3.41575},{"x":13.7098,"y":3.405168},{"x":13.69926,"y":2.460877},{"x":16.67167,"y":2.455469},{"x":16.67134,"y":5.129327},{"x":16.25138,"y":4.924525},{"x":15.90153,"y":5.098832},{"x":14.85329,"y":5.113066},{"x":14.64129,"y":4.891927},{"x":14.30725,"y":5.120798},{"x":14.09058,"y":5.117527},{"x":14.05816,"y":5.476541},{"x":13.71774,"y":5.463151},{"x":13.71342,"y":4.504309},{"x":13.56084,"y":4.517361},{"x":13.55398,"y":5.708665}],[{"x":3.680702,"y":-0.7193847},{"x":11.23482,"y":-0.7322021},{"x":11.25548,"y":3.415659},{"x":11.12786,"y":3.419772},{"x":10.75617,"y":3.248261},{"x":10.73357,"y":0.1335044},{"x":3.80519,"y":0.1373074},{"x":3.818368,"y":2.171448},{"x":4.95494,"y":2.163688}],[{"x":24.3919,"y":5.646832},{"x":24.40391,"y":5.442618},{"x":29.13271,"y":5.463554},{"x":29.13235,"y":-0.5738076},{"x":26.70813,"y":-0.5683059},{"x":26.73149,"y":-1.518903},{"x":26.95974,"y":-1.376958},{"x":28.65404,"y":-1.375125},{"x":28.65683,"y":-2.748982},{"x":20.33384,"y":-2.765088},{"x":20.3395,"y":-3.770453},{"x":17.93382,"y":-3.75399},{"x":17.96961,"y":-1.380442},{"x":20.87004,"y":-1.529158},{"x":20.88352,"y":-0.57613},{"x":17.95704,"y":-0.5691274},{"x":17.97062,"y":0.8038477},{"x":18.93854,"y":0.7800388},{"x":18.93254,"y":1.598521},{"x":17.98071,"y":1.602374},{"x":17.96151,"y":5.457216},{"x":22.08923,"y":5.464901}],[{"x":21.05723,"y":5.580141},{"x":21.04314,"y":1.607991},{"x":20.09641,"y":1.596745},{"x":20.0989,"y":0.7935047},{"x":21.20391,"y":0.7903333},{"x":21.19257,"y":5.576942}],[{"x":11.25491,"y":5.620545},{"x":11.25136,"y":7.638169},{"x":12.73018,"y":7.648466},{"x":16.70739,"y":11.60392},{"x":16.70363,"y":15.95111}],[{"x":13.56464,"y":5.627538},{"x":13.55336,"y":6.262187},{"x":17.82847,"y":10.52911}],[{"x":22.0916,"y":5.628248},{"x":22.10105,"y":6.269244},{"x":17.82738,"y":10.53223}],[{"x":18.96374,"y":15.99181},{"x":18.95209,"y":11.60822},{"x":22.92673,"y":7.648565},{"x":23.77438,"y":7.633254},{"x":24.39512,"y":7.429035},{"x":24.40486,"y":5.632056}],[{"x":7.220767,"y":8.833663},{"x":7.22247,"y":13.32097},{"x":7.355358,"y":13.31475},{"x":7.347641,"y":9.647293},{"x":12.08101,"y":9.675213},{"x":12.10811,"y":14.14455},{"x":7.977394,"y":14.13185},{"x":7.965653,"y":14.56834},{"x":5.895285,"y":14.56914},{"x":5.340263,"y":13.75509},{"x":4.994918,"y":13.86703},{"x":4.97183,"y":10.77085},{"x":4.854324,"y":10.77326},{"x":4.82991,"y":14.37352},{"x":3.731284,"y":14.37308},{"x":2.88799,"y":13.48531},{"x":2.05535,"y":13.47907},{"x":1.203818,"y":14.39449},{"x":0.08164263,"y":14.39737},{"x":0.09667063,"y":9.66668},{"x":4.986578,"y":9.656466},{"x":4.97504,"y":8.829696}],[{"x":16.70916,"y":15.85023},{"x":16.70025,"y":16.77103},{"x":12.87164,"y":16.76122},{"x":12.86896,"y":20.66066},{"x":13.45543,"y":20.44748},{"x":13.97733,"y":20.66153},{"x":13.98698,"y":20.99139},{"x":15.44601,"y":20.98349},{"x":15.45127,"y":20.65192},{"x":15.75683,"y":20.46402},{"x":16.55697,"y":20.66496},{"x":16.56016,"y":17.77095},{"x":16.69854,"y":17.78921},{"x":16.69959,"y":21.05262},{"x":17.28384,"y":21.04772}],[{"x":17.29037,"y":21.03862},{"x":17.28807,"y":21.8748},{"x":12.74644,"y":21.8763},{"x":12.75951,"y":22.57441},{"x":13.10651,"y":23.37466},{"x":13.73439,"y":24.10774},{"x":15.4092,"y":25.05675},{"x":16.66343,"y":25.38271},{"x":17.84418,"y":25.4593},{"x":19.55612,"y":25.26398},{"x":21.22287,"y":24.61058},{"x":22.23365,"y":23.81404},{"x":22.85189,"y":22.84578},{"x":22.946,"y":21.83673},{"x":18.39528,"y":21.88615},{"x":18.39615,"y":21.04512}],[{"x":18.39268,"y":21.06862},{"x":19.00124,"y":21.0586},{"x":18.97912,"y":19.36902},{"x":19.09182,"y":19.36764},{"x":19.07861,"y":20.87417},{"x":19.93716,"y":20.85499},{"x":19.93814,"y":21.016},{"x":20.60378,"y":20.99647},{"x":20.59911,"y":20.85824},{"x":22.44798,"y":20.86359},{"x":22.43852,"y":20.04767},{"x":22.78794,"y":20.04396},{"x":22.81651,"y":16.90077},{"x":19.54131,"y":16.91623},{"x":19.54423,"y":16.77063},{"x":19.10311,"y":16.76015},{"x":19.10099,"y":18.36283},{"x":18.95522,"y":18.3579},{"x":18.97521,"y":15.8332}],[{"x":7.212324,"y":9.84881},{"x":7.219448,"y":2.166317}],[{"x":4.975575,"y":9.639039},{"x":4.948083,"y":2.163976}],[{"x":16.568,"y":19.02069},{"x":16.128,"y":19.02069},{"x":16.128,"y":18.061},{"x":16.568,"y":18.061},{"x":16.568,"y":19.02069}],[{"x":14.298,"y":19.32877},{"x":12.868,"y":19.32877},{"x":12.868,"y":18.837},{"x":14.298,"y":18.837},{"x":14.298,"y":19.32877}],[{"x":17.9349,"y":5.586789},{"x":18.00781,"y":5.227939},{"x":18.67603,"y":4.793009},{"x":19.14774,"y":5.096714},{"x":19.15981,"y":5.218315},{"x":20.28024,"y":5.215115},{"x":20.59529,"y":4.862257},{"x":21.07071,"y":5.041684},{"x":21.05624,"y":5.598478},{"x":17.9349,"y":5.586789}],[{"x":20.47344,"y":3.445615},{"x":20.90137,"y":3.802666},{"x":20.90182,"y":4.066891},{"x":20.46002,"y":4.36205},{"x":20.02631,"y":4.068808},{"x":20.01486,"y":3.775074},{"x":20.47344,"y":3.445615}],[{"x":20.57644,"y":2.875615},{"x":21.00437,"y":3.232666},{"x":21.00482,"y":3.496891},{"x":20.56302,"y":3.79205},{"x":20.12931,"y":3.498807},{"x":20.11786,"y":3.205074},{"x":20.57644,"y":2.875615}],[{"x":13.56155,"y":17.24444},{"x":13.35024,"y":17.03253},{"x":13.13379,"y":17.16789},{"x":12.972,"y":17.29},{"x":12.9723,"y":18.24428},{"x":14.402,"y":18.26},{"x":14.442,"y":17.2},{"x":13.56155,"y":17.24444}],[{"x":-0.2100389,"y":0},{"x":0.2100389,"y":0.2100389},{"x":0.4200778,"y":0},{"x":0.2100389,"y":0.2100389},{"x":-0.4200778,"y":0}],[{"x":-0.15,"y":0},{"x":0.15,"y":0.15},{"x":0.3,"y":0},{"x":0.15,"y":0.15},{"x":-0.3,"y":0}],[{"x":-0.1982777,"y":0},{"x":0.1982777,"y":0.1982777},{"x":0.3965554,"y":0},{"x":0.1982777,"y":0.1982777},{"x":-0.3965554,"y":0}],[{"x":-0.1982777,"y":0},{"x":0.1982777,"y":0.1982777},{"x":0.3965554,"y":0},{"x":0.1982777,"y":0.1982777},{"x":-0.3965554,"y":0}],[{"x":21.03973,"y":3.091555},{"x":19.02226,"y":3.091555},{"x":19.02226,"y":2.636287},{"x":21.03973,"y":2.636287},{"x":21.03973,"y":3.091555}],[{"x":4.878,"y":13.541},{"x":4.248,"y":13.541},{"x":4.248,"y":13.351},{"x":4.878,"y":13.351},{"x":4.878,"y":13.541}],[{"x":4.878,"y":12.713},{"x":4.248,"y":12.713},{"x":4.248,"y":12.523},{"x":4.878,"y":12.523},{"x":4.878,"y":12.713}],[{"x":4.878,"y":11.676},{"x":4.248,"y":11.676},{"x":4.248,"y":11.486},{"x":4.878,"y":11.486},{"x":4.878,"y":11.676}],[{"x":-6.140824,"y":2.366569},{"x":-6.841852,"y":2.366569},{"x":-6.841852,"y":1.44923},{"x":-6.140824,"y":1.44923},{"x":-6.140824,"y":2.366569}],[{"x":-2.1,"y":2.1913},{"x":-2.78,"y":2.1913},{"x":-2.78,"y":1.08},{"x":-2.1,"y":1.08},{"x":-2.1,"y":2.1913}],[{"x":24.92,"y":3.80943},{"x":23.16,"y":3.80943},{"x":23.16,"y":0.9315456},{"x":24.92,"y":0.9315456},{"x":24.92,"y":3.80943}],[{"x":27.78,"y":3.80943},{"x":26.02,"y":3.80943},{"x":26.02,"y":0.9315456},{"x":27.78,"y":0.9315456},{"x":27.78,"y":3.80943}],[{"x":28.753,"y":6.152},{"x":28.073,"y":6.152},{"x":28.073,"y":5.172},{"x":28.753,"y":5.172},{"x":28.753,"y":6.152}],[{"x":29.165,"y":3.735192},{"x":28.755,"y":3.735192},{"x":28.755,"y":1.065314},{"x":29.165,"y":1.065314},{"x":29.165,"y":3.735192}],[{"x":29.145,"y":5.06},{"x":28.735,"y":5.06},{"x":28.735,"y":4.28},{"x":29.145,"y":4.28},{"x":29.145,"y":5.06}],[{"x":19.027,"y":2.864326},{"x":18.617,"y":2.864326},{"x":18.617,"y":2.661},{"x":19.027,"y":2.661},{"x":19.027,"y":2.864326}],[{"x":0.7080002,"y":13.566},{"x":0.07800007,"y":13.566},{"x":0.07800007,"y":13.376},{"x":0.7080002,"y":13.376},{"x":0.7080002,"y":13.566}],[{"x":0.5790001,"y":13.4695},{"x":0.08900023,"y":13.4695},{"x":0.08900023,"y":12.682},{"x":0.5790001,"y":12.682},{"x":0.5790001,"y":13.4695}],[{"x":0.7080002,"y":12.703},{"x":0.07800007,"y":12.703},{"x":0.07800007,"y":12.513},{"x":0.7080002,"y":12.513},{"x":0.7080002,"y":12.703}],[{"x":0.5790001,"y":12.779},{"x":0.08900023,"y":12.779},{"x":0.08900023,"y":11.809},{"x":0.5790001,"y":11.809},{"x":0.5790001,"y":12.779}],[{"x":0.7080002,"y":11.701},{"x":0.07800007,"y":11.701},{"x":0.07800007,"y":11.511},{"x":0.7080002,"y":11.511},{"x":0.7080002,"y":11.701}],[{"x":1.022,"y":11.5454},{"x":0.2020001,"y":11.5454},{"x":0.2020001,"y":11.114},{"x":1.022,"y":11.114},{"x":1.022,"y":11.5454}],[{"x":0.7080002,"y":10.187},{"x":0.07800007,"y":10.187},{"x":0.07800007,"y":9.997},{"x":0.7080002,"y":9.997},{"x":0.7080002,"y":10.187}],[{"x":11.635,"y":9.885},{"x":7.905001,"y":9.885},{"x":7.905001,"y":9.655001},{"x":11.635,"y":9.655001},{"x":11.635,"y":9.885}],[{"x":10.53,"y":11.48153},{"x":8.13,"y":11.48153},{"x":8.13,"y":10.605},{"x":10.53,"y":10.605},{"x":10.53,"y":11.48153}],[{"x":10.50025,"y":4.627614},{"x":9.624989,"y":4.627614},{"x":9.624989,"y":4.265},{"x":10.50025,"y":4.265},{"x":10.50025,"y":4.627614}],[{"x":22.63,"y":16.915},{"x":19.55,"y":16.915},{"x":19.55,"y":16.765},{"x":22.63,"y":16.765},{"x":22.63,"y":16.915}],[{"x":22.095,"y":18.117},{"x":19.965,"y":18.117},{"x":19.965,"y":17.61187},{"x":22.095,"y":17.61187},{"x":22.095,"y":18.117}],[{"x":19.871,"y":23.511},{"x":18.441,"y":23.511},{"x":18.441,"y":23.261},{"x":19.871,"y":23.261},{"x":19.871,"y":23.511}],[{"x":17.135,"y":23.425},{"x":15.705,"y":23.425},{"x":15.705,"y":23.175},{"x":17.135,"y":23.175},{"x":17.135,"y":23.425}],[{"x":21.845,"y":23.615},{"x":20.415,"y":23.615},{"x":20.415,"y":23.365},{"x":21.845,"y":23.365},{"x":21.845,"y":23.615}],[{"x":15.625,"y":23.475},{"x":14.195,"y":23.475},{"x":14.195,"y":23.225},{"x":15.625,"y":23.225},{"x":15.625,"y":23.475}],[{"x":15.399,"y":24.317},{"x":13.949,"y":24.317},{"x":13.949,"y":23.967},{"x":15.399,"y":23.967},{"x":15.399,"y":24.317}],[{"x":15.756,"y":22.593},{"x":14.306,"y":22.593},{"x":14.306,"y":22.243},{"x":15.756,"y":22.243},{"x":15.756,"y":22.593}],[{"x":17.316,"y":22.679},{"x":15.866,"y":22.679},{"x":15.866,"y":22.329},{"x":17.316,"y":22.329},{"x":17.316,"y":22.679}],[{"x":19.706,"y":22.699},{"x":18.256,"y":22.699},{"x":18.256,"y":22.349},{"x":19.706,"y":22.349},{"x":19.706,"y":22.699}],[{"x":22.68,"y":22.702},{"x":21.23,"y":22.702},{"x":21.23,"y":22.352},{"x":22.68,"y":22.352},{"x":22.68,"y":22.702}],[{"x":16.23,"y":25.004},{"x":14.8,"y":25.004},{"x":14.8,"y":24.754},{"x":16.23,"y":24.754},{"x":16.23,"y":25.004}],[{"x":21.038,"y":24.766},{"x":19.588,"y":24.766},{"x":19.588,"y":24.416},{"x":21.038,"y":24.416},{"x":21.038,"y":24.766}],[{"x":19.367,"y":-2.518978},{"x":18.68754,"y":-2.518978},{"x":18.68754,"y":-2.839065},{"x":19.367,"y":-2.839065},{"x":19.367,"y":-2.518978}],[{"x":17.001,"y":1.865},{"x":16.231,"y":1.865},{"x":16.231,"y":0.875},{"x":17.001,"y":0.875},{"x":17.001,"y":1.865}],[{"x":15.925,"y":1.865},{"x":15.155,"y":1.865},{"x":15.155,"y":0.875},{"x":15.925,"y":0.875},{"x":15.925,"y":1.865}],[{"x":14.539,"y":1.865},{"x":13.769,"y":1.865},{"x":13.769,"y":0.875},{"x":14.539,"y":0.875},{"x":14.539,"y":1.865}],[{"x":14.72,"y":-1.882},{"x":13.7,"y":-1.882},{"x":13.7,"y":-2.232},{"x":14.72,"y":-2.232},{"x":14.72,"y":-1.882}],[{"x":17.08,"y":-1.879},{"x":16.06,"y":-1.879},{"x":16.06,"y":-2.229},{"x":17.08,"y":-2.229},{"x":17.08,"y":-1.879}],[{"x":20.427,"y":2.613},{"x":20.427,"y":2.153},{"x":20.797,"y":1.793},{"x":21.057,"y":1.953},{"x":20.427,"y":2.613}],[{"x":9.81149,"y":13.37871},{"x":9.815001,"y":12.48},{"x":11.2387,"y":12.45026},{"x":11.25847,"y":11.15674},{"x":11.47295,"y":11.17267},{"x":11.465,"y":10.86},{"x":12.11164,"y":10.50449},{"x":12.11447,"y":13.38711},{"x":9.81149,"y":13.37871}],[{"x":22.0661,"y":19.44129},{"x":21.59683,"y":19.86015},{"x":20.43074,"y":19.8552},{"x":20.04636,"y":19.4479},{"x":19.75579,"y":19.44509},{"x":19.736,"y":18.319},{"x":20.446,"y":17.909},{"x":21.616,"y":17.909},{"x":22.076,"y":18.279},{"x":22.0661,"y":19.44129}],[{"x":16.60246,"y":5.043699},{"x":16.4291,"y":4.853531},{"x":16.10734,"y":5.052094},{"x":15.05132,"y":5.074209},{"x":14.85036,"y":4.780072},{"x":14.47607,"y":5.052905},{"x":14.26516,"y":5.059663},{"x":14.26178,"y":5.7193},{"x":16.94973,"y":5.700385},{"x":16.94588,"y":5.045084},{"x":16.60246,"y":5.043699}]] \ No newline at end of file diff --git a/assets/collision/polus.json b/assets/collision/polus.json new file mode 100644 index 0000000..d6fe23b --- /dev/null +++ b/assets/collision/polus.json @@ -0,0 +1 @@ +[[{"x":4.468355,"y":-25.19164},{"x":4.642188,"y":-25.4748},{"x":5.859158,"y":-25.97136},{"x":7.379907,"y":-26.17045},{"x":9.318335,"y":-25.85212},{"x":9.691259,"y":-25.51607}],[{"x":12.85454,"y":-25.81685},{"x":13.21841,"y":-26.08871},{"x":15.07888,"y":-26.52698},{"x":17.51235,"y":-26.34335},{"x":19.41673,"y":-26.09551},{"x":19.68621,"y":-25.85137}],[{"x":31.83391,"y":-17.77156},{"x":31.16781,"y":-16.94566},{"x":30.99393,"y":-16.22257},{"x":32.84105,"y":-16.14146},{"x":32.84063,"y":-15.6644},{"x":31.01169,"y":-15.65007},{"x":31.19056,"y":-14.61455},{"x":32.02383,"y":-13.94915},{"x":33.24313,"y":-13.53686},{"x":34.74972,"y":-13.62016},{"x":34.87443,"y":-11.78193}],[{"x":1.163654,"y":-8.0028},{"x":11.41751,"y":-7.992749},{"x":11.42015,"y":-9.245324},{"x":11.07954,"y":-9.219257},{"x":10.46802,"y":-9.175865},{"x":10.10005,"y":-8.969795},{"x":9.726253,"y":-8.987286},{"x":9.737602,"y":-9.120133},{"x":7.71242,"y":-9.124039},{"x":7.702194,"y":-8.981174},{"x":2.543196,"y":-8.972954},{"x":2.216505,"y":-9.236499},{"x":1.586406,"y":-8.953769},{"x":1.588347,"y":-10.41234},{"x":1.889818,"y":-10.42414},{"x":1.875751,"y":-11.16787},{"x":1.56425,"y":-11.43982},{"x":1.58513,"y":-13.01106},{"x":4.34094,"y":-13.01894},{"x":4.351696,"y":-11.30745},{"x":3.332622,"y":-11.3479},{"x":3.13042,"y":-11.16702},{"x":3.143933,"y":-10.42473},{"x":4.461988,"y":-10.43749},{"x":4.454574,"y":-13.01405},{"x":4.805012,"y":-13.01713},{"x":4.800202,"y":-13.99301},{"x":4.261829,"y":-13.97445}],[{"x":6.059853,"y":-13.98625},{"x":6.056842,"y":-13.01136},{"x":6.365222,"y":-13.00313},{"x":6.363031,"y":-10.41905},{"x":6.883934,"y":-10.43037},{"x":6.866985,"y":-11.38156},{"x":6.683385,"y":-11.38787},{"x":6.68111,"y":-13.98867},{"x":6.055891,"y":-13.98866}],[{"x":6.660733,"y":-13.96993},{"x":11.38878,"y":-13.96052},{"x":11.4079,"y":-10.2124},{"x":11.06013,"y":-10.21909},{"x":11.0841,"y":-10.41887},{"x":8.096922,"y":-10.41325},{"x":8.091213,"y":-11.39648},{"x":11.39786,"y":-11.37933}],[{"x":4.623094,"y":-13.24155},{"x":4.616889,"y":-17.96251}],[{"x":6.22798,"y":-17.95516},{"x":6.21671,"y":-13.17807}],[{"x":4.329903,"y":-17.80734},{"x":4.848855,"y":-17.80634},{"x":4.866112,"y":-18.79152},{"x":3.729993,"y":-18.78318},{"x":3.712253,"y":-18.00079},{"x":4.014073,"y":-18.00014},{"x":4.023358,"y":-16.33687},{"x":3.302862,"y":-15.68751},{"x":2.222462,"y":-15.69674},{"x":2.171762,"y":-16.38062},{"x":1.870695,"y":-16.55518},{"x":1.413249,"y":-16.39653},{"x":1.182948,"y":-15.71359},{"x":0.315588,"y":-15.69561},{"x":0.3355923,"y":-16.98934},{"x":0.5901208,"y":-17.0723},{"x":0.5943825,"y":-17.20404},{"x":0.8280015,"y":-17.29915},{"x":0.8179843,"y":-18.00991},{"x":2.493732,"y":-18.01904},{"x":2.49319,"y":-21.35849},{"x":2.079746,"y":-21.36394},{"x":2.078685,"y":-20.61282},{"x":2.35622,"y":-20.60777},{"x":2.368558,"y":-18.95865},{"x":2.076443,"y":-18.97806},{"x":1.885863,"y":-18.75928},{"x":1.117207,"y":-18.76488},{"x":0.8289199,"y":-19.03341},{"x":0.8386979,"y":-19.20543},{"x":1.388847,"y":-19.19547},{"x":1.596443,"y":-19.43404},{"x":1.575914,"y":-19.69593},{"x":1.435838,"y":-19.77969},{"x":0.8422089,"y":-19.78425},{"x":0.8446279,"y":-19.98297},{"x":0.3109691,"y":-20.59622},{"x":0.8417668,"y":-20.61433},{"x":0.8157988,"y":-21.63438},{"x":0.3295369,"y":-21.70924},{"x":0.3366578,"y":-22.43502},{"x":2.793279,"y":-22.44424},{"x":2.792992,"y":-23.2027},{"x":1.461118,"y":-23.19614},{"x":1.31747,"y":-23.37593},{"x":0.31354,"y":-23.61072},{"x":0.3137934,"y":-24.99285},{"x":4.304623,"y":-24.96215},{"x":4.311398,"y":-23.18888},{"x":4.065456,"y":-23.198},{"x":4.066553,"y":-22.43078},{"x":4.303843,"y":-22.44131},{"x":4.305241,"y":-21.84784},{"x":5.262867,"y":-21.85781},{"x":5.281539,"y":-22.83879},{"x":4.633771,"y":-22.82001},{"x":4.633868,"y":-25.707}],[{"x":6.121937,"y":-17.81924},{"x":6.125292,"y":-18.77762},{"x":6.825426,"y":-18.76699},{"x":6.839787,"y":-21.85088},{"x":6.540904,"y":-21.85227},{"x":6.540395,"y":-22.78975},{"x":7.153381,"y":-22.81718},{"x":7.160014,"y":-17.79613},{"x":6.122066,"y":-17.81233}],[{"x":9.454855,"y":-25.96852},{"x":9.431522,"y":-21.63816},{"x":12.19316,"y":-21.62917},{"x":12.18578,"y":-20.95824},{"x":12.39046,"y":-20.95943},{"x":12.36431,"y":-22.44428},{"x":10.78102,"y":-22.44403},{"x":10.67416,"y":-22.7368},{"x":10.25398,"y":-22.84688},{"x":10.11095,"y":-23.10449},{"x":10.11586,"y":-23.96232},{"x":11.675,"y":-23.98213},{"x":11.98683,"y":-24.2565},{"x":12.07754,"y":-24.65993},{"x":12.11319,"y":-25.18934},{"x":13.01008,"y":-25.17044},{"x":14.1152,"y":-24.04868},{"x":14.13201,"y":-22.4912},{"x":13.70978,"y":-22.44748},{"x":13.69046,"y":-20.95766},{"x":13.92116,"y":-20.93906},{"x":13.92223,"y":-21.61344},{"x":14.35059,"y":-21.61405},{"x":14.33282,"y":-24.663},{"x":12.82521,"y":-26.22824}],[{"x":19.41761,"y":-26.89183},{"x":19.41428,"y":-23.9883},{"x":18.17723,"y":-24.03974},{"x":18.07403,"y":-23.80378},{"x":17.23896,"y":-23.79051},{"x":17.24987,"y":-22.49761},{"x":17.55,"y":-22.48637},{"x":17.55483,"y":-22.85442},{"x":19.63054,"y":-22.86251},{"x":19.63194,"y":-20.89175},{"x":19.23694,"y":-20.54975},{"x":19.22899,"y":-19.82631},{"x":20.36378,"y":-19.8261},{"x":20.34842,"y":-20.56401},{"x":20.06929,"y":-20.57189},{"x":19.98769,"y":-20.68674},{"x":19.72377,"y":-20.70757},{"x":19.73185,"y":-25.68816},{"x":20.47411,"y":-25.67962},{"x":20.4746,"y":-23.78805},{"x":20.92922,"y":-23.78977},{"x":20.91884,"y":-25.68327},{"x":22.64618,"y":-25.65857},{"x":22.64977,"y":-24.8419},{"x":21.57603,"y":-24.84848},{"x":21.57071,"y":-23.77324},{"x":22.64747,"y":-23.79463},{"x":22.65281,"y":-23.03153},{"x":23.25619,"y":-23.03007},{"x":23.27328,"y":-23.97827},{"x":22.75698,"y":-23.98265},{"x":22.74235,"y":-25.67866},{"x":25.35623,"y":-25.67291},{"x":25.34784,"y":-25.31824},{"x":25.6509,"y":-25.32745},{"x":25.64791,"y":-25.98885}],[{"x":25.36081,"y":-24.2927},{"x":26.78346,"y":-24.34598},{"x":26.9757,"y":-24.08747},{"x":26.94914,"y":-20.68051},{"x":27.24854,"y":-20.39237},{"x":33.72597,"y":-20.36598}],[{"x":25.60491,"y":-25.32418},{"x":28.29795,"y":-25.32301},{"x":28.56379,"y":-25.11761},{"x":28.55518,"y":-21.54346},{"x":28.8744,"y":-21.15343},{"x":33.65784,"y":-21.16417}],[{"x":33.62392,"y":-20.36062},{"x":33.74138,"y":-20.37108},{"x":33.98942,"y":-19.83228},{"x":34.34132,"y":-19.84434},{"x":35.3353,"y":-18.84248},{"x":38.09467,"y":-18.81666},{"x":38.33393,"y":-19.0464},{"x":38.4529,"y":-18.96912},{"x":38.45289,"y":-18.25862},{"x":38.2281,"y":-17.98815},{"x":37.93935,"y":-17.9932}],[{"x":33.62782,"y":-21.26315},{"x":33.6063,"y":-20.97958},{"x":33.72726,"y":-20.98293},{"x":33.81273,"y":-21.31415},{"x":34.1008,"y":-21.30166},{"x":34.48833,"y":-21.31375},{"x":34.62857,"y":-21.45783},{"x":34.63147,"y":-22.14026},{"x":35.06453,"y":-22.50477},{"x":37.82113,"y":-22.5367},{"x":37.87976,"y":-22.09232},{"x":38.05656,"y":-21.70172},{"x":38.46301,"y":-21.59719},{"x":39.04375,"y":-21.58111},{"x":39.37287,"y":-21.19324},{"x":39.36033,"y":-20.183},{"x":39.15706,"y":-19.87025},{"x":39.17252,"y":-19.18412},{"x":39.29811,"y":-19.09086},{"x":39.48417,"y":-19.31238}],[{"x":39.86636,"y":-11.77958},{"x":39.85297,"y":-18.35519},{"x":39.66782,"y":-18.8361},{"x":39.34599,"y":-19.17971}],[{"x":38.26079,"y":-11.81432},{"x":38.25633,"y":-18.10769}],[{"x":24.79112,"y":-8.297972},{"x":24.60275,"y":-8.309859},{"x":24.60161,"y":-9.347557},{"x":24.93522,"y":-9.361137},{"x":24.93626,"y":-9.281505},{"x":25.99243,"y":-9.277384},{"x":25.98319,"y":-8.309302},{"x":24.76362,"y":-8.30243},{"x":24.76752,"y":-6.596991},{"x":28.4891,"y":-6.597414},{"x":28.45923,"y":-7.883276},{"x":28.0819,"y":-7.898211},{"x":27.26835,"y":-8.311927},{"x":27.25182,"y":-9.284495},{"x":27.67033,"y":-9.284494},{"x":27.64346,"y":-9.429042},{"x":30.11549,"y":-9.410277},{"x":30.1084,"y":-9.26344},{"x":30.76909,"y":-9.30785},{"x":30.7845,"y":-8.539226},{"x":29.29904,"y":-8.507231},{"x":29.30643,"y":-7.69715},{"x":29.00145,"y":-7.67394},{"x":28.97839,"y":-7.201445},{"x":29.26816,"y":-7.067931},{"x":30.77146,"y":-6.979141},{"x":32.51074,"y":-6.985449},{"x":32.5293,"y":-7.183598},{"x":32.63257,"y":-7.192318},{"x":32.61849,"y":-5.816308},{"x":33.23944,"y":-5.262764},{"x":34.96804,"y":-5.275941},{"x":35.62245,"y":-5.857418},{"x":36.93289,"y":-5.911241},{"x":36.93803,"y":-6.817193},{"x":36.95642,"y":-7.421597},{"x":37.01499,"y":-7.498929},{"x":37.21291,"y":-7.564006},{"x":37.30141,"y":-7.493206},{"x":39.42292,"y":-7.540127},{"x":39.60057,"y":-7.493062},{"x":39.68266,"y":-7.383775},{"x":39.69355,"y":-6.823347},{"x":41.01453,"y":-6.819366},{"x":41.00942,"y":-8.521671},{"x":39.64987,"y":-8.511548},{"x":39.65253,"y":-8.208335},{"x":37.28139,"y":-8.251371},{"x":36.92989,"y":-8.531352},{"x":36.92381,"y":-9.269684},{"x":37.82073,"y":-9.275565},{"x":37.82654,"y":-9.387205},{"x":38.14684,"y":-9.405945},{"x":38.16234,"y":-9.270851},{"x":41.01444,"y":-9.254274},{"x":41.00807,"y":-10.88156},{"x":39.70001,"y":-10.87982},{"x":39.71012,"y":-11.84312},{"x":41.33603,"y":-11.85758}],[{"x":38.43769,"y":-10.88076},{"x":38.44409,"y":-11.83403},{"x":32.30521,"y":-11.84694},{"x":32.30261,"y":-11.41184},{"x":24.60489,"y":-11.42625},{"x":24.60373,"y":-10.31385},{"x":24.93105,"y":-10.3164},{"x":24.9278,"y":-10.46288},{"x":32.50663,"y":-10.4634},{"x":32.49953,"y":-9.272195},{"x":32.03727,"y":-9.283109},{"x":32.03503,"y":-8.515261},{"x":32.50163,"y":-8.525516},{"x":32.50784,"y":-8.143551},{"x":32.61598,"y":-8.14327},{"x":32.60762,"y":-8.518142},{"x":35.66286,"y":-8.51203},{"x":35.66552,"y":-9.276382},{"x":35.50318,"y":-9.27581},{"x":35.49751,"y":-9.757851},{"x":35.39729,"y":-9.761734},{"x":35.39405,"y":-9.366034},{"x":34.50618,"y":-9.353576},{"x":34.50914,"y":-9.807557},{"x":33.2071,"y":-9.797601},{"x":33.23382,"y":-9.251056},{"x":32.62589,"y":-9.238699},{"x":32.60516,"y":-10.88941},{"x":35.38573,"y":-10.884},{"x":35.39033,"y":-10.73012},{"x":35.502,"y":-10.7328},{"x":35.49858,"y":-10.87894},{"x":37.81333,"y":-10.87426},{"x":37.81733,"y":-10.3591},{"x":38.1471,"y":-10.35867},{"x":38.14536,"y":-10.87858},{"x":38.43985,"y":-10.88128}],[{"x":17.69288,"y":-12.43071},{"x":17.92506,"y":-12.44274},{"x":17.91992,"y":-12.73771},{"x":18.13973,"y":-13.14827},{"x":18.93378,"y":-13.03682},{"x":18.93032,"y":-13.74802},{"x":23.40946,"y":-13.73718},{"x":23.41752,"y":-10.04595},{"x":17.67672,"y":-10.04396},{"x":17.69304,"y":-10.65219},{"x":19.11459,"y":-10.72677},{"x":19.19703,"y":-10.83733},{"x":20.77446,"y":-10.84928},{"x":21.06972,"y":-11.36423},{"x":23.21907,"y":-11.98615},{"x":23.20252,"y":-12.70573},{"x":22.73357,"y":-12.50255},{"x":22.23224,"y":-13.00422},{"x":20.03675,"y":-12.9942},{"x":20.02708,"y":-12.52135},{"x":19.64642,"y":-12.50077},{"x":19.58921,"y":-12.24613},{"x":19.4246,"y":-12.09461},{"x":19.07064,"y":-12.11856},{"x":19.0561,"y":-11.69396},{"x":17.68979,"y":-11.67623},{"x":17.69365,"y":-12.43585}],[{"x":2.592513,"y":-8.025942},{"x":4.280735,"y":-7.398408},{"x":4.290099,"y":-4.471621},{"x":4.004474,"y":-4.254463},{"x":3.906721,"y":-3.920575},{"x":4.174633,"y":-3.568251},{"x":4.674113,"y":-3.396756},{"x":5.219826,"y":-3.662916},{"x":5.343629,"y":-3.998812},{"x":5.228846,"y":-4.248911},{"x":4.984394,"y":-4.46321},{"x":4.982031,"y":-7.379822},{"x":6.230354,"y":-7.271032}],[{"x":23.14033,"y":-6.594245},{"x":23.80352,"y":-6.434936},{"x":23.8171,"y":-3.478621},{"x":23.53148,"y":-3.261463},{"x":23.43372,"y":-2.927575},{"x":23.70163,"y":-2.575251},{"x":24.20111,"y":-2.403756},{"x":24.74683,"y":-2.669916},{"x":24.87063,"y":-3.005812},{"x":24.75585,"y":-3.255911},{"x":24.51139,"y":-3.47021},{"x":24.50903,"y":-6.386822},{"x":24.79138,"y":-6.653455}],[{"x":5.413117,"y":-8.351901},{"x":7.054794,"y":-8.276548},{"x":8.697342,"y":-8.510894},{"x":10.10862,"y":-8.303051},{"x":10.58135,"y":-8.118687},{"x":11.00681,"y":-7.484922},{"x":12.62112,"y":-8.204644},{"x":14.23929,"y":-8.974723},{"x":14.40617,"y":-7.258311},{"x":14.29719,"y":-2.043067},{"x":15.70589,"y":-1.51522},{"x":17.53691,"y":-1.506043},{"x":19.03707,"y":-2.029848},{"x":18.93472,"y":-7.274326},{"x":19.20114,"y":-8.927617},{"x":22.39211,"y":-7.533558},{"x":23.25742,"y":-7.609662},{"x":23.75232,"y":-7.498259}],[{"x":37.91449,"y":-20.31673},{"x":35.00881,"y":-20.31673},{"x":35.00881,"y":-20.94084},{"x":37.91449,"y":-20.94084},{"x":37.91449,"y":-20.31673}],[{"x":26.02585,"y":-6.64},{"x":24.817,"y":-6.64},{"x":24.817,"y":-6.970619},{"x":26.02585,"y":-6.970619},{"x":26.02585,"y":-6.64}],[{"x":11.77179,"y":-19.62465},{"x":11.83028,"y":-18.52067},{"x":13.4037,"y":-18.52042},{"x":13.44733,"y":-15.98955},{"x":13.68375,"y":-15.79114},{"x":13.66221,"y":-15.21871},{"x":13.32477,"y":-14.72645},{"x":11.799,"y":-14.733},{"x":11.589,"y":-14.723},{"x":9.954119,"y":-14.72645},{"x":10.04784,"y":-19.60156},{"x":10.24256,"y":-19.60833},{"x":10.2018,"y":-15.63915},{"x":12.86989,"y":-15.59822},{"x":12.99521,"y":-16.18124},{"x":13.21599,"y":-16.24457},{"x":13.21647,"y":-17.73367},{"x":11.58001,"y":-17.74002},{"x":11.55608,"y":-19.60504},{"x":11.77179,"y":-19.62465}],[{"x":22.90844,"y":-18.77619},{"x":23.78691,"y":-18.78369},{"x":23.8818,"y":-17.88425},{"x":23.83846,"y":-16.25285},{"x":24.49371,"y":-16.31531},{"x":24.48757,"y":-16.49074},{"x":27.01841,"y":-16.46032},{"x":27.30907,"y":-16.29564},{"x":28.57129,"y":-16.29163},{"x":28.57926,"y":-16.8445},{"x":28.90884,"y":-16.85584},{"x":28.90578,"y":-15.3694},{"x":15.61483,"y":-15.35862},{"x":15.62812,"y":-20.79835},{"x":17.24424,"y":-20.7777},{"x":17.25434,"y":-21.51932},{"x":17.56835,"y":-21.50746},{"x":17.57353,"y":-20.74102},{"x":17.96331,"y":-20.58899},{"x":17.9802,"y":-19.81679},{"x":15.96782,"y":-19.82635},{"x":15.96752,"y":-18.91302},{"x":16.61518,"y":-18.76492},{"x":16.62491,"y":-18.02716},{"x":16.45051,"y":-18.01276},{"x":16.46912,"y":-16.46037},{"x":16.88453,"y":-16.43662},{"x":17.00264,"y":-16.34876},{"x":21.93412,"y":-16.3298},{"x":21.9517,"y":-16.55123},{"x":23.38988,"y":-16.55636},{"x":23.38412,"y":-18.00589},{"x":22.89434,"y":-18.00573},{"x":22.90844,"y":-18.77619}],[{"x":21.63955,"y":-18.01692},{"x":17.88043,"y":-18.02346},{"x":17.88024,"y":-18.81244},{"x":18.13617,"y":-18.86934},{"x":21.36609,"y":-18.88367},{"x":21.63298,"y":-18.80225},{"x":21.63955,"y":-18.01692}],[{"x":25.04842,"y":-18.75886},{"x":25.3532,"y":-18.74552},{"x":25.34759,"y":-19.82735},{"x":21.61452,"y":-19.81656},{"x":21.65306,"y":-22.25628},{"x":22.08868,"y":-22.44897},{"x":22.14978,"y":-20.58658},{"x":25.36657,"y":-20.61784},{"x":25.35131,"y":-23.00266},{"x":24.53445,"y":-23.02254},{"x":24.5266,"y":-23.98506},{"x":25.34336,"y":-23.98594},{"x":25.33836,"y":-24.35742},{"x":25.65167,"y":-24.35947},{"x":25.67025,"y":-18.97481},{"x":28.91121,"y":-18.98343},{"x":28.9024,"y":-17.81323},{"x":28.5792,"y":-17.8131},{"x":28.57479,"y":-18.02147},{"x":25.03823,"y":-18.00087},{"x":25.04842,"y":-18.75886}],[{"x":26.86648,"y":-7.398125},{"x":26.58685,"y":-7.362242},{"x":26.25525,"y":-7.46819},{"x":26.14983,"y":-7.690572},{"x":26.36041,"y":-8.006925},{"x":26.76204,"y":-8.007652},{"x":27.03698,"y":-7.880741},{"x":27.13068,"y":-7.605845},{"x":26.86648,"y":-7.398125}],[{"x":5.790905,"y":-19.75676},{"x":3.727096,"y":-19.75676},{"x":3.727096,"y":-20.96208},{"x":5.790905,"y":-20.96208},{"x":5.790905,"y":-19.75676}],[{"x":26.83904,"y":-14.12032},{"x":26.22075,"y":-14.01883},{"x":25.39735,"y":-14.13572},{"x":24.71892,"y":-13.72345},{"x":25.51597,"y":-13.2925},{"x":27.56451,"y":-13.30547},{"x":28.30361,"y":-13.6028},{"x":28.42681,"y":-13.89908},{"x":26.83904,"y":-14.12032}],[{"x":13.3388,"y":-6.003677},{"x":12.77013,"y":-6.260994},{"x":12.46447,"y":-6.640699},{"x":12.43997,"y":-7.028212},{"x":12.30328,"y":-7.102982},{"x":12.30591,"y":-7.660471},{"x":12.69129,"y":-8.015654},{"x":13.37645,"y":-7.761706},{"x":13.74081,"y":-7.960196},{"x":14.04209,"y":-7.840932},{"x":14.03963,"y":-7.349671},{"x":13.97028,"y":-6.885252},{"x":13.74353,"y":-6.663803},{"x":13.74,"y":-6.37474},{"x":13.3388,"y":-6.003677}],[{"x":20.79322,"y":-7.794276},{"x":20.20507,"y":-7.589413},{"x":20.23226,"y":-7.977169},{"x":19.84346,"y":-8.353935},{"x":19.24475,"y":-8.107004},{"x":19.61344,"y":-7.113365},{"x":19.65041,"y":-6.401253},{"x":20.03278,"y":-6.080639},{"x":21.13224,"y":-6.860869},{"x":21.15035,"y":-7.431738},{"x":20.79322,"y":-7.794276}],[{"x":9.379,"y":-11.10174},{"x":8.338992,"y":-11.10174},{"x":8.338992,"y":-11.9455},{"x":9.379,"y":-11.9455},{"x":9.379,"y":-11.10174}],[{"x":11.147,"y":-11.10174},{"x":10.10699,"y":-11.10174},{"x":10.10699,"y":-11.9455},{"x":11.147,"y":-11.9455},{"x":11.147,"y":-11.10174}],[{"x":11.147,"y":-12.94574},{"x":10.10699,"y":-12.94574},{"x":10.10699,"y":-13.7895},{"x":11.147,"y":-13.7895},{"x":11.147,"y":-12.94574}],[{"x":9.37,"y":-12.94574},{"x":8.329992,"y":-12.94574},{"x":8.329992,"y":-13.7895},{"x":9.37,"y":-13.7895},{"x":9.37,"y":-12.94574}],[{"x":4.303,"y":-24.20208},{"x":3.323,"y":-24.20208},{"x":3.323,"y":-24.508},{"x":4.303,"y":-24.508},{"x":4.303,"y":-24.20208}],[{"x":22.72785,"y":-17.09251},{"x":22.22638,"y":-17.09251},{"x":22.22638,"y":-17.53884},{"x":22.72785,"y":-17.53884},{"x":22.72785,"y":-17.09251}],[{"x":24.79221,"y":-21.106},{"x":22.99592,"y":-21.106},{"x":22.99592,"y":-22.106},{"x":24.79221,"y":-22.106},{"x":24.79221,"y":-21.106}],[{"x":34.16841,"y":-5.455648},{"x":33.56845,"y":-5.455648},{"x":33.56845,"y":-5.682265},{"x":34.16841,"y":-5.682265},{"x":34.16841,"y":-5.455648}],[{"x":35.77344,"y":-6.916783},{"x":33.96344,"y":-6.916783},{"x":33.96344,"y":-7.673556},{"x":35.77344,"y":-7.673556},{"x":35.77344,"y":-6.916783}],[{"x":18.05003,"y":-25.48757},{"x":17.57003,"y":-25.48757},{"x":17.57003,"y":-25.70757},{"x":18.05003,"y":-25.70757},{"x":18.05003,"y":-25.48757}],[{"x":18.77,"y":-25.59},{"x":18.29,"y":-25.59},{"x":18.29,"y":-25.81},{"x":18.77,"y":-25.81},{"x":18.77,"y":-25.59}],[{"x":32.08207,"y":-11.78346},{"x":31.60206,"y":-11.78346},{"x":31.60206,"y":-12.00346},{"x":32.08207,"y":-12.00346},{"x":32.08207,"y":-11.78346}],[{"x":12.63623,"y":-11.89787},{"x":12.15623,"y":-11.89787},{"x":12.15623,"y":-12.11787},{"x":12.63623,"y":-12.11787},{"x":12.63623,"y":-11.89787}],[{"x":7.059857,"y":-16.24459},{"x":6.579858,"y":-16.24459},{"x":6.579858,"y":-16.46459},{"x":7.059857,"y":-16.46459},{"x":7.059857,"y":-16.24459}],[{"x":1.571452,"y":-10.26345},{"x":1.819386,"y":-10.43345},{"x":2.262032,"y":-10.0961},{"x":1.571452,"y":-10.26345}],[{"x":1.334,"y":-24.403},{"x":0.3184614,"y":-24.39593},{"x":0.3239999,"y":-24.973},{"x":0.674,"y":-24.973},{"x":0.6772294,"y":-24.77208},{"x":1.324769,"y":-24.75715},{"x":1.334,"y":-24.403}],[{"x":8.508034,"y":-23.93293},{"x":8.629685,"y":-24.40218},{"x":8.92936,"y":-24.96339},{"x":9.599236,"y":-25.21856},{"x":9.57372,"y":-22.93649},{"x":8.644652,"y":-23.21924},{"x":8.508034,"y":-23.93293}],[{"x":11.67719,"y":-16.53101},{"x":11.29618,"y":-16.53452},{"x":11.06358,"y":-16.58503},{"x":10.94267,"y":-16.71943},{"x":10.93917,"y":-16.87988},{"x":11.06962,"y":-17.01637},{"x":11.56154,"y":-17.0409},{"x":11.88714,"y":-16.91338},{"x":11.89764,"y":-16.71949},{"x":11.67719,"y":-16.53101}],[{"x":21.01822,"y":-17.32524},{"x":21.40012,"y":-17.168},{"x":21.40401,"y":-17.06523},{"x":21.00678,"y":-16.91912},{"x":20.329,"y":-16.81467},{"x":18.87623,"y":-16.81245},{"x":18.07026,"y":-16.91774},{"x":17.70623,"y":-17.03189},{"x":17.71012,"y":-17.188},{"x":17.74012,"y":-17.208},{"x":18.04012,"y":-17.328},{"x":18.83012,"y":-17.448},{"x":20.22012,"y":-17.448},{"x":21.01822,"y":-17.32524}],[{"x":27.34005,"y":-6.938899},{"x":27.93693,"y":-7.230727},{"x":28.42066,"y":-6.909329},{"x":27.80876,"y":-6.653845},{"x":27.34005,"y":-6.938899}],[{"x":16.65074,"y":-10.12311},{"x":16.00821,"y":-10.30374},{"x":15.95542,"y":-10.52961},{"x":16.30542,"y":-10.77919},{"x":16.83255,"y":-10.98451},{"x":17.15542,"y":-10.73961},{"x":17.15542,"y":-10.18961},{"x":16.65074,"y":-10.12311}],[{"x":4.995,"y":-3.943596},{"x":4.688505,"y":-4.143763},{"x":4.360964,"y":-3.921563},{"x":4.666411,"y":-3.684916},{"x":4.995,"y":-3.943596}],[{"x":24.537,"y":-2.915734},{"x":24.2305,"y":-3.115901},{"x":23.90296,"y":-2.893701},{"x":24.20841,"y":-2.657055},{"x":24.537,"y":-2.915734}],[{"x":28.8746,"y":-18.89537},{"x":32.58327,"y":-17.87351},{"x":31.65491,"y":-17.43377},{"x":28.904,"y":-18.19115},{"x":28.8746,"y":-18.89537}],[{"x":31.08879,"y":-13.97953},{"x":30.4176,"y":-13.91366},{"x":30.037,"y":-14.07843},{"x":30.39278,"y":-14.2843},{"x":30.83512,"y":-14.17834},{"x":31.08879,"y":-13.97953}],[{"x":32.36998,"y":-12.94724},{"x":33.05407,"y":-13.68856},{"x":34.27784,"y":-13.63817},{"x":32.97806,"y":-11.86354},{"x":32.30158,"y":-11.74354},{"x":32.36998,"y":-12.94724}],[{"x":-0.3,"y":0},{"x":0.3,"y":0.3},{"x":0.6,"y":0},{"x":0.3,"y":0.3},{"x":-0.6,"y":0}]] \ No newline at end of file diff --git a/assets/collision/skeld.json b/assets/collision/skeld.json new file mode 100644 index 0000000..2ad2528 --- /dev/null +++ b/assets/collision/skeld.json @@ -0,0 +1 @@ +[[{"x":0.2362284,"y":-7.726103},{"x":2.013968,"y":-7.715738},{"x":2.011803,"y":-10.30677},{"x":6.082888,"y":-10.30429},{"x":6.962117,"y":-9.435246},{"x":6.962895,"y":-6.817364},{"x":6.706806,"y":-6.619056},{"x":6.232102,"y":-6.602984},{"x":6.176251,"y":-6.938542},{"x":3.060317,"y":-6.960407},{"x":3.011859,"y":-6.6034},{"x":0.2607511,"y":-6.571015}],[{"x":0.8954936,"y":-6.595578},{"x":0.08411826,"y":-6.767831},{"x":0.05762036,"y":-4.452175}],[{"x":0.07118475,"y":-8.635859},{"x":0.08622147,"y":-7.654803},{"x":0.9666418,"y":-7.737996}],[{"x":-1.460379,"y":-8.631944},{"x":-1.462344,"y":-4.483645}],[{"x":-7.368402,"y":1.584177},{"x":-6.555078,"y":1.57672},{"x":-6.557156,"y":4.008165},{"x":-4.60883,"y":5.933904},{"x":1.670171,"y":5.95224},{"x":4.543502,"y":3.066632},{"x":4.553433,"y":1.587478},{"x":5.637702,"y":1.583386}],[{"x":-0.2999234,"y":-5.142708},{"x":-0.2951662,"y":-4.452511},{"x":1.997185,"y":-4.436467},{"x":4.564693,"y":-1.878594},{"x":4.549082,"y":0.4435357},{"x":5.659974,"y":0.4535991}],[{"x":-7.401506,"y":0.4553653},{"x":-6.580028,"y":0.4451815},{"x":-6.567801,"y":-1.97265},{"x":-4.072343,"y":-4.44449},{"x":-1.812371,"y":-4.433851},{"x":-1.811063,"y":-5.56378}],[{"x":14.56604,"y":-4.229232},{"x":15.64946,"y":-4.230859},{"x":15.65051,"y":-2.836758},{"x":17.51895,"y":-2.849281},{"x":19.1273,"y":-4.154813},{"x":19.13222,"y":-5.43117},{"x":17.50414,"y":-6.75653},{"x":15.63534,"y":-6.741789},{"x":15.64649,"y":-5.373594},{"x":14.97682,"y":-5.367826}],[{"x":4.952006,"y":1.57443},{"x":6.752982,"y":1.585721},{"x":7.033926,"y":1.475315},{"x":7.801225,"y":1.480557},{"x":7.989945,"y":2.125443},{"x":8.366776,"y":2.596887},{"x":8.386627,"y":3.642199},{"x":9.479401,"y":3.64006},{"x":11.58195,"y":1.583232},{"x":10.50412,"y":1.58118},{"x":10.46297,"y":1.088706},{"x":10.14301,"y":0.6494932},{"x":10.15121,"y":-1.103494},{"x":10.2626,"y":-1.332904}],[{"x":4.956852,"y":0.4148467},{"x":7.101061,"y":0.4562553},{"x":7.263814,"y":0.3892393},{"x":8.363285,"y":0.3629824},{"x":8.846294,"y":0.135911},{"x":8.820242,"y":-1.119593},{"x":8.771524,"y":-1.311896}],[{"x":8.222275,"y":-4.019724},{"x":7.755623,"y":-4.004018},{"x":7.610951,"y":-4.239169},{"x":7.185065,"y":-4.238694},{"x":7.009696,"y":-4.505672},{"x":7.019401,"y":-5.130587},{"x":6.245888,"y":-5.137787},{"x":6.252361,"y":-4.420362},{"x":6.121649,"y":-4.258716},{"x":5.744867,"y":-4.264971},{"x":5.575344,"y":-4.418731},{"x":5.553154,"y":-5.131573},{"x":4.840867,"y":-5.133445},{"x":4.825717,"y":-4.378825},{"x":4.445279,"y":-4.140391},{"x":5.200883,"y":-3.392043},{"x":5.570336,"y":-3.395252},{"x":6.07529,"y":-2.981499},{"x":6.987266,"y":-2.986429},{"x":7.193809,"y":-2.857087},{"x":8.241753,"y":-2.880474}],[{"x":6.565016,"y":-11.72411},{"x":6.987641,"y":-11.79513},{"x":7.896163,"y":-11.79405},{"x":8.188145,"y":-11.67882},{"x":8.455678,"y":-11.41463},{"x":8.826189,"y":-11.20363},{"x":8.838142,"y":-10.35416},{"x":8.728677,"y":-9.931278}],[{"x":6.575041,"y":-12.87208},{"x":7.911303,"y":-12.93333},{"x":8.129161,"y":-13.3627},{"x":8.807487,"y":-13.87456},{"x":8.824697,"y":-14.20615},{"x":7.358915,"y":-14.20941},{"x":7.3613,"y":-14.74166},{"x":8.720889,"y":-14.71317},{"x":8.717556,"y":-14.93626},{"x":9.488122,"y":-14.93082},{"x":10.1447,"y":-14.29308},{"x":10.12637,"y":-13.5075},{"x":10.55909,"y":-12.89536},{"x":10.55823,"y":-12.24413},{"x":10.14061,"y":-11.52396},{"x":10.14535,"y":-10.80471},{"x":11.66409,"y":-10.79793},{"x":11.65609,"y":-10.42626},{"x":10.26556,"y":-10.42765},{"x":10.24841,"y":-9.86418}],[{"x":15.17467,"y":-4.419531},{"x":12.64121,"y":-4.44558},{"x":12.63906,"y":-2.948531},{"x":10.23936,"y":-3.092088},{"x":10.22813,"y":-0.9974822}],[{"x":10.24439,"y":-10.28774},{"x":10.25753,"y":-7.140551},{"x":12.62506,"y":-7.155302},{"x":12.63361,"y":-5.415534},{"x":15.18507,"y":-5.4197}],[{"x":8.746116,"y":-10.24708},{"x":8.741796,"y":-6.048161},{"x":11.1656,"y":-6.042319},{"x":11.16558,"y":-4.080832},{"x":7.765801,"y":-4.095026}],[{"x":7.799847,"y":-2.876018},{"x":8.758554,"y":-2.886162},{"x":8.773032,"y":-0.922443}],[{"x":-14.81697,"y":-11.22099},{"x":-11.38458,"y":-11.21203},{"x":-11.37703,"y":-14.06057},{"x":-10.22454,"y":-14.0326},{"x":-10.21113,"y":-10.33264},{"x":-7.675366,"y":-10.31545}],[{"x":-7.695002,"y":-10.31545},{"x":-7.695909,"y":-9.451391},{"x":-10.11512,"y":-9.437295},{"x":-10.13741,"y":-7.76803},{"x":-6.895009,"y":-7.855294},{"x":-6.745681,"y":-7.990388},{"x":-5.197094,"y":-7.945567},{"x":-5.198377,"y":-8.148785},{"x":-6.449782,"y":-9.321177},{"x":-6.435663,"y":-11.45714},{"x":-7.44524,"y":-12.40876},{"x":-8.85337,"y":-12.36879},{"x":-8.805441,"y":-14.07697}],[{"x":-8.805441,"y":-14.07697},{"x":-5.374582,"y":-14.06365}],[{"x":-14.81383,"y":-12.35692},{"x":-12.88635,"y":-12.35124},{"x":-12.88583,"y":-15.19543},{"x":-5.270284,"y":-15.17392}],[{"x":-18.90468,"y":-4.917564},{"x":-19.21733,"y":-4.914915},{"x":-19.25125,"y":-3.487691},{"x":-20.80061,"y":-3.466249},{"x":-20.82514,"y":-1.781794},{"x":-21.77498,"y":-1.781393},{"x":-22.19863,"y":-2.061523},{"x":-22.18964,"y":-2.351079},{"x":-22.92343,"y":-2.855991},{"x":-22.93843,"y":-3.568924},{"x":-22.23674,"y":-3.921036},{"x":-21.91414,"y":-4.717618},{"x":-21.90926,"y":-5.25623},{"x":-21.48744,"y":-5.424995},{"x":-21.48222,"y":-5.773309},{"x":-21.60671,"y":-5.917229},{"x":-21.98811,"y":-5.893168},{"x":-21.98224,"y":-6.374194},{"x":-22.23363,"y":-6.524475},{"x":-22.58218,"y":-6.172986},{"x":-22.88113,"y":-6.147035},{"x":-22.92309,"y":-8.299733},{"x":-21.77963,"y":-9.033682},{"x":-20.80727,"y":-9.037098},{"x":-20.82744,"y":-7.339758},{"x":-19.23684,"y":-7.333115},{"x":-19.2312,"y":-6.027665},{"x":-18.87985,"y":-6.043848}],[{"x":1.514437,"y":-12.88248},{"x":0.9000312,"y":-12.87428},{"x":0.9177916,"y":-17.50703},{"x":-3.021377,"y":-17.54297},{"x":-5.077038,"y":-15.46409},{"x":-5.080357,"y":-15.18036},{"x":-5.737282,"y":-15.17595}],[{"x":0.02917442,"y":-7.893129},{"x":0.03507514,"y":-9.129563},{"x":0.9134803,"y":-9.266632},{"x":0.9189174,"y":-11.73293},{"x":1.625746,"y":-11.7288}],[{"x":-5.709477,"y":-14.03647},{"x":-5.07719,"y":-14.0213},{"x":-5.074898,"y":-11.00069},{"x":-4.480464,"y":-10.70592},{"x":-4.46635,"y":-10.3222},{"x":-2.930291,"y":-9.498736},{"x":-2.852392,"y":-9.117779},{"x":-1.468678,"y":-9.132879},{"x":-1.450918,"y":-8.018958}],[{"x":-17.66159,"y":-8.313709},{"x":-17.66675,"y":-9.546221},{"x":-19.51328,"y":-9.537824},{"x":-19.51333,"y":-13.05503},{"x":-18.33128,"y":-13.99051},{"x":-14.8677,"y":-13.99421},{"x":-14.86014,"y":-12.34251},{"x":-14.46036,"y":-12.34108}],[{"x":-16.16101,"y":-8.316375},{"x":-16.16359,"y":-9.572023},{"x":-14.88622,"y":-9.570913},{"x":-14.88741,"y":-11.21787},{"x":-14.32632,"y":-11.20666}],[{"x":0.97958,"y":-11.76019},{"x":6.992189,"y":-11.7485}],[{"x":0.9887347,"y":-12.91188},{"x":4.368167,"y":-12.876},{"x":4.358963,"y":-13.96144}],[{"x":5.864928,"y":-13.42998},{"x":5.870777,"y":-12.87888},{"x":6.897767,"y":-12.87967}],[{"x":-15.03895,"y":1.597145},{"x":-6.570155,"y":1.575777}],[{"x":-15.1985,"y":0.4579078},{"x":-10.23706,"y":0.4484872},{"x":-10.23486,"y":-0.4350799}],[{"x":-8.766523,"y":-0.6480405},{"x":-8.767955,"y":0.4350485},{"x":-6.58421,"y":0.4233765}],[{"x":-14.63285,"y":-4.891836},{"x":-16.24534,"y":-4.88406},{"x":-16.24988,"y":-1.635784}],[{"x":-17.56018,"y":-1.635784},{"x":-17.5364,"y":-3.693102},{"x":-17.65632,"y":-4.623451},{"x":-17.45963,"y":-4.732869},{"x":-17.46077,"y":-4.988844},{"x":-19.02956,"y":-5.000385}],[{"x":-16.16554,"y":-9.545579},{"x":-16.20093,"y":-7.663517},{"x":-16.33388,"y":-7.573332},{"x":-16.31617,"y":-6.972282},{"x":-16.15963,"y":-6.06042},{"x":-14.49686,"y":-6.07415}],[{"x":-18.98414,"y":-6.040682},{"x":-17.65042,"y":-6.052687},{"x":-17.67024,"y":-9.558265}],[{"x":-14.56532,"y":1.662872},{"x":-14.90609,"y":1.650298},{"x":-14.88668,"y":2.885079},{"x":-18.32795,"y":2.894751},{"x":-19.52896,"y":1.864622},{"x":-19.52396,"y":-1.570519},{"x":-17.71379,"y":-1.537421},{"x":-17.68546,"y":-2.032646}],[{"x":-14.55745,"y":0.4402371},{"x":-14.85756,"y":0.4410935},{"x":-14.83369,"y":-1.524606},{"x":-16.12798,"y":-1.591758},{"x":-16.13887,"y":-2.05983}],[{"x":4.390248,"y":-12.89099},{"x":4.412769,"y":-14.44803},{"x":4.073301,"y":-14.74718},{"x":1.539549,"y":-14.71471},{"x":1.546466,"y":-15.85303},{"x":2.15479,"y":-15.84039},{"x":2.124448,"y":-16.75854},{"x":1.752654,"y":-16.75457},{"x":2.532989,"y":-17.46411},{"x":2.886859,"y":-17.46804},{"x":2.883881,"y":-16.92094},{"x":3.369814,"y":-17.0816},{"x":3.656673,"y":-16.95642},{"x":4.00171,"y":-16.79352},{"x":4.377112,"y":-16.82868},{"x":4.413591,"y":-17.07326},{"x":4.874448,"y":-17.04011},{"x":5.397761,"y":-16.8904},{"x":5.406366,"y":-17.49508},{"x":5.998445,"y":-16.9528},{"x":6.004679,"y":-15.85465},{"x":6.492789,"y":-15.85043},{"x":6.49343,"y":-14.44846},{"x":5.879263,"y":-14.47317},{"x":5.880812,"y":-12.86388}],[{"x":-15.45731,"y":-4.882467},{"x":-14.46863,"y":-4.895476},{"x":-14.47101,"y":-3.812231},{"x":-13.55466,"y":-2.877563},{"x":-13.45982,"y":-3.3577},{"x":-13.1225,"y":-3.353385},{"x":-13.08784,"y":-2.893233},{"x":-12.57413,"y":-2.867874},{"x":-11.92476,"y":-3.50242},{"x":-11.93444,"y":-4.685921},{"x":-12.92189,"y":-4.678928},{"x":-12.92335,"y":-5.555384},{"x":-12.75325,"y":-5.688099},{"x":-12.42195,"y":-5.651134},{"x":-12.38069,"y":-5.922665},{"x":-11.94423,"y":-5.987844},{"x":-11.91953,"y":-6.434968},{"x":-12.05945,"y":-6.434929},{"x":-12.06055,"y":-7.300493},{"x":-14.32722,"y":-7.307874},{"x":-14.32708,"y":-6.44428},{"x":-14.46767,"y":-6.447207},{"x":-14.47318,"y":-6.049348},{"x":-15.37626,"y":-6.048749}],[{"x":-9.896128,"y":0.09396344},{"x":-9.906479,"y":-1.014971},{"x":-11.16,"y":-1.020813},{"x":-11.12743,"y":-4.796037},{"x":-10.33474,"y":-5.799318},{"x":-5.22831,"y":-5.799926},{"x":-5.220803,"y":-5.351019},{"x":-5.824704,"y":-4.780326},{"x":-5.461809,"y":-4.228658},{"x":-6.392942,"y":-3.4428},{"x":-6.636661,"y":-3.934034},{"x":-6.987354,"y":-3.613638},{"x":-6.967412,"y":-1.019102},{"x":-8.405022,"y":-1.028633},{"x":-8.408207,"y":0.2032199}],[{"x":3.33,"y":-8.261624},{"x":3.33,"y":-9.313201},{"x":5.765989,"y":-9.313227},{"x":5.742,"y":-8.261624},{"x":3.33,"y":-8.261624}],[{"x":17.88076,"y":-6.179931},{"x":17.45793,"y":-6.386068},{"x":17.72214,"y":-6.643311},{"x":18.11662,"y":-6.310138},{"x":17.88076,"y":-6.179931}],[{"x":18.50979,"y":-4.473197},{"x":18.06764,"y":-4.467421},{"x":17.77722,"y":-4.937558},{"x":18.57143,"y":-4.977489},{"x":18.50979,"y":-4.473197}],[{"x":17.65212,"y":-3.412386},{"x":17.2644,"y":-3.7608},{"x":17.72674,"y":-3.964799},{"x":18.16902,"y":-3.658661},{"x":17.65212,"y":-3.412386}],[{"x":-0.6781706,"y":-13.55313},{"x":-0.5186334,"y":-13.65023},{"x":-0.356359,"y":-13.66372},{"x":-0.1854412,"y":-13.57278},{"x":-0.1816655,"y":-13.09209},{"x":-0.2930961,"y":-12.94377}],[{"x":-3.246386,"y":-12.28785},{"x":-3.709773,"y":-12.07104},{"x":-4.100151,"y":-12.41943},{"x":-4.086681,"y":-12.96832},{"x":-3.531352,"y":-13.16967},{"x":-3.234791,"y":-12.87577},{"x":-3.238448,"y":-13.60751},{"x":-2.558987,"y":-13.60691},{"x":-2.55681,"y":-14.15934},{"x":-2.294851,"y":-14.15913},{"x":-2.283567,"y":-14.56503},{"x":-1.581326,"y":-14.83519},{"x":-1.148905,"y":-14.39955},{"x":-1.143201,"y":-14.16828},{"x":-0.6143369,"y":-14.17394},{"x":-0.6246856,"y":-13.58801},{"x":-0.2763608,"y":-12.96624},{"x":-0.283671,"y":-11.13291},{"x":-1.927792,"y":-11.11836},{"x":-1.925223,"y":-11.35053},{"x":-3.245068,"y":-11.37238},{"x":-3.246306,"y":-12.29369}],[{"x":-15.80511,"y":-11.39031},{"x":-15.91522,"y":-10.76074},{"x":-16.07204,"y":-10.53166},{"x":-18.71611,"y":-10.4536},{"x":-19.24034,"y":-10.09965},{"x":-19.53993,"y":-10.4893},{"x":-19.49779,"y":-13.02382},{"x":-18.53159,"y":-13.02814},{"x":-18.40778,"y":-12.43455},{"x":-16.19409,"y":-12.45347},{"x":-15.88496,"y":-12.03066},{"x":-15.80511,"y":-11.39031}],[{"x":-15.83031,"y":0.8088304},{"x":-15.94042,"y":1.436132},{"x":-16.09725,"y":1.664383},{"x":-18.74131,"y":1.742164},{"x":-19.26554,"y":2.094836},{"x":-19.56513,"y":1.706586},{"x":-19.52299,"y":-0.8187767},{"x":-18.55679,"y":-0.823085},{"x":-18.43298,"y":-0.2316318},{"x":-16.21929,"y":-0.2504853},{"x":-15.91016,"y":0.1707977},{"x":-15.83031,"y":0.8088304}],[{"x":-16.22955,"y":-12.49125},{"x":-15.74837,"y":-12.49532},{"x":-15.72997,"y":-10.52501},{"x":-16.2295,"y":-10.52501}],[{"x":-16.25475,"y":-0.2881263},{"x":-15.77357,"y":-0.2921864},{"x":-15.75517,"y":1.671011},{"x":-16.2547,"y":1.671011}],[{"x":16.476,"y":-5.988},{"x":15.54,"y":-5.988},{"x":15.54,"y":-6.78},{"x":16.476,"y":-6.78},{"x":16.476,"y":-5.988}],[{"x":16.476,"y":-2.772},{"x":15.54,"y":-2.772},{"x":15.54,"y":-3.564},{"x":16.476,"y":-3.564},{"x":16.476,"y":-2.772}],[{"x":-6.957,"y":-2.549},{"x":-8.067,"y":-2.549},{"x":-8.067,"y":-3.379},{"x":-6.957,"y":-3.379},{"x":-6.957,"y":-2.549}],[{"x":-10.077,"y":-2.549},{"x":-11.187,"y":-2.549},{"x":-11.187,"y":-3.379},{"x":-10.077,"y":-3.379},{"x":-10.077,"y":-2.549}],[{"x":-6.957,"y":-1.229},{"x":-8.067,"y":-1.229},{"x":-8.067,"y":-2.059},{"x":-6.957,"y":-2.059},{"x":-6.957,"y":-1.229}],[{"x":-10.077,"y":-1.229},{"x":-11.187,"y":-1.229},{"x":-11.187,"y":-2.059},{"x":-10.077,"y":-2.059},{"x":-10.077,"y":-1.229}],[{"x":-2.910683,"y":2.348959},{"x":-2.188303,"y":2.785507},{"x":-1.933683,"y":3.511235},{"x":-2.064806,"y":3.991863},{"x":-2.610682,"y":4.384067},{"x":-3.341935,"y":4.587026},{"x":-4.275301,"y":4.375321},{"x":-4.825263,"y":3.795358},{"x":-4.775846,"y":2.94697},{"x":-4.058721,"y":2.382533},{"x":-2.910683,"y":2.348959}],[{"x":2.014117,"y":2.371759},{"x":2.736497,"y":2.808307},{"x":2.991117,"y":3.534035},{"x":2.859994,"y":4.014663},{"x":2.314118,"y":4.406867},{"x":1.582865,"y":4.609826},{"x":0.6494999,"y":4.398121},{"x":0.09953787,"y":3.818158},{"x":0.1489543,"y":2.96977},{"x":0.8660797,"y":2.405333},{"x":2.014117,"y":2.371759}],[{"x":-0.3918829,"y":-0.2814406},{"x":0.3304967,"y":0.1551068},{"x":0.5851166,"y":0.8808349},{"x":0.4539942,"y":1.361462},{"x":-0.09188195,"y":1.753667},{"x":-0.8231351,"y":1.956626},{"x":-1.7565,"y":1.744921},{"x":-2.306462,"y":1.164958},{"x":-2.257046,"y":0.31657},{"x":-1.53992,"y":-0.2478667},{"x":-0.3918829,"y":-0.2814406}],[{"x":-2.939483,"y":-2.830241},{"x":-2.217103,"y":-2.393693},{"x":-1.962484,"y":-1.667965},{"x":-2.093606,"y":-1.187338},{"x":-2.639482,"y":-0.7951334},{"x":-3.370735,"y":-0.5921744},{"x":-4.304101,"y":-0.8038793},{"x":-4.854062,"y":-1.383842},{"x":-4.804646,"y":-2.23223},{"x":-4.087521,"y":-2.796667},{"x":-2.939483,"y":-2.830241}],[{"x":2.054917,"y":-2.830241},{"x":2.777297,"y":-2.393693},{"x":3.031917,"y":-1.667965},{"x":2.900795,"y":-1.187338},{"x":2.354918,"y":-0.7951334},{"x":1.623665,"y":-0.5921744},{"x":0.6902999,"y":-0.8038793},{"x":0.1403379,"y":-1.383842},{"x":0.1897544,"y":-2.23223},{"x":0.9068797,"y":-2.796667},{"x":2.054917,"y":-2.830241}]] \ No newline at end of file diff --git a/gamedataprocessor.go b/gamedataprocessor.go new file mode 100644 index 0000000..3ec2c50 --- /dev/null +++ b/gamedataprocessor.go @@ -0,0 +1,157 @@ +package main + +import ( + "encoding/hex" + "fmt" + + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/gamedata" + "codehub.onpointcoding.net/sean/go-susapp/innernetobjects" + "codehub.onpointcoding.net/sean/go-susapp/mapdata" + "codehub.onpointcoding.net/sean/go-susapp/packets" + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type GameDataProcessor struct { + sus *SusApp + gameRpcProcessor *GameRPCProcessor + subpacketTagMap map[byte]func(net *protocol.PacketHandler, GameID int32, h *protocol.Hazel) +} + +func NewGameDataProcessor(sus *SusApp) *GameDataProcessor { + proc := &GameDataProcessor{sus: sus, gameRpcProcessor: NewGameRPCProcessor(sus)} + proc.subpacketTagMap = map[byte]func(net *protocol.PacketHandler, GameID int32, h *protocol.Hazel){ + 0x01: proc.captureGameData, + 0x02: proc.captureGameRPC, + 0x04: proc.captureGameSpawn, + 0x05: proc.captureGameDespawn, + } + return proc +} + +func (proc *GameDataProcessor) receiveGameDataSubpacket(net *protocol.PacketHandler, GameID int32, h []*protocol.Hazel) { + for i := 0; i < len(h); i++ { + v, ok := proc.subpacketTagMap[h[i].GetTag()] + if ok { + v(net, GameID, h[i]) + } else { + fmt.Printf("Unable to parse gamedata subpackets with tag of %x\n", h[i].GetTag()) + fmt.Printf("Packet: %v\n", hex.EncodeToString(h[i].GetUnderlyingPacket().Dump())) + } + } +} + +func (proc *GameDataProcessor) captureGameData(net *protocol.PacketHandler, GameID int32, h *protocol.Hazel) { + a := &gamedata.DataH2G{} + a.Read(h.GetUnderlyingPacket()) + + netId := a.Component.NetID + if netId != 0 { + b, ok := proc.sus.state.CurrentGame.NetObjects[netId] + if ok { + (*b).Read(a.Component.RawData, false) + } + } +} + +func (proc *GameDataProcessor) captureGameRPC(net *protocol.PacketHandler, GameID int32, h *protocol.Hazel) { + proc.gameRpcProcessor.receiveGameRpcCall(net, GameID, gamedata.NewRpcFromHazel(h)) +} + +func (proc *GameDataProcessor) captureGameSpawn(net *protocol.PacketHandler, GameID int32, h *protocol.Hazel) { + a := &gamedata.SpawnH2G{} + a.Read(h.GetUnderlyingPacket()) + + if len(a.Components) >= 1 { + z := a.Components[0] + netId := z.NetID + switch a.SpawnType { + case enum.SPAWNTYPE_GAME_DATA: + b := &innernetobjects.GameData{} + b.Read(z.ComponentData.GetUnderlyingPacket(), true) + var c innernetobjects.InnerNetObject = b + proc.sus.state.CurrentGame.NetObjects[netId] = &c + proc.sus.state.CurrentGame.GameDataNetID = netId + fmt.Printf("GameData Net ID: %v\n", netId) + case enum.SPAWNTYPE_SKELD_SHIP_STATUS: + b := innernetobjects.NewSkeldShipStatus() + b.Read(z.ComponentData.GetUnderlyingPacket(), true) + var c innernetobjects.InnerNetObject = b + proc.sus.state.CurrentGame.NetObjects[netId] = &c + proc.sus.state.CurrentGame.MapNetObjectNetID = netId + fmt.Printf("SkeldShipStatus Net ID: %v\n", netId) + case enum.SPAWNTYPE_MIRA_SHIP_STATUS: + b := innernetobjects.NewMiraShipStatus() + b.Read(z.ComponentData.GetUnderlyingPacket(), true) + var c innernetobjects.InnerNetObject = b + proc.sus.state.CurrentGame.NetObjects[netId] = &c + proc.sus.state.CurrentGame.MapNetObjectNetID = netId + fmt.Printf("MiraShipStatus Net ID: %v\n", netId) + case enum.SPAWNTYPE_POLUS_SHIP_STATUS: + b := innernetobjects.NewPolusShipStatus() + b.Read(z.ComponentData.GetUnderlyingPacket(), true) + var c innernetobjects.InnerNetObject = b + proc.sus.state.CurrentGame.NetObjects[netId] = &c + proc.sus.state.CurrentGame.MapNetObjectNetID = netId + fmt.Printf("PolusShipStatus Net ID: %v\n", netId) + case enum.SPAWNTYPE_PLAYER_CONTROL: + b := &innernetobjects.PlayerControl{} + b.Read(z.ComponentData.GetUnderlyingPacket(), true) + var c innernetobjects.InnerNetObject = b + proc.sus.state.CurrentGame.NetObjects[netId] = &c + y := &PlayerObject{} + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerNetID[netId] = y + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerClientID[a.OwnerID] = y + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerClientID[a.OwnerID].PlayerClientNetID = netId + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerClientID[a.OwnerID].PlayerControllerNetID = netId + fmt.Printf("Player Net ID: %v\n", netId) + fmt.Printf("Player Owner ID: %v\n", a.OwnerID) + + isMyPlayer := a.OwnerID == proc.sus.state.CurrentGame.ClientID + if isMyPlayer { + fmt.Println("Owner ID matches current Client ID so its my player controller") + proc.sus.state.CurrentGame.ClientPlayerNetID = z.NetID + + // Send RPC CheckName + proc.sus.state.CurrentGame.CheckNameNonce = net.GetNonce() + var d1 gamedata.RpcSub = &gamedata.RpcCheckName{Name: proc.sus.state.Settings.PlayerName} + var d2 packets.GameDataSubpacket = gamedata.NewRpcFromRpcSub(proc.sus.state.CurrentGame.ClientPlayerNetID, &d1) + var d3 protocol.HazelPayload = packets.NewGameDataToSingleSubpacket(proc.sus.state.CurrentGame.GameID, proc.sus.state.CurrentGame.HostClientID, &d2) + net.SendReliablePacket(proc.sus.state.CurrentGame.CheckNameNonce, []*protocol.Hazel{protocol.NewHazelFromPayload(&d3)}) + + // Send RPC CheckColor + proc.sus.state.CurrentGame.CheckColorNonce = net.GetNonce() + var e1 gamedata.RpcSub = &gamedata.RpcCheckColor{Color: proc.sus.state.Settings.PlayerColor} + var e2 packets.GameDataSubpacket = gamedata.NewRpcFromRpcSub(proc.sus.state.CurrentGame.ClientPlayerNetID, &e1) + var e3 protocol.HazelPayload = packets.NewGameDataToSingleSubpacket(proc.sus.state.CurrentGame.GameID, proc.sus.state.CurrentGame.HostClientID, &e2) + net.SendReliablePacket(proc.sus.state.CurrentGame.CheckColorNonce, []*protocol.Hazel{protocol.NewHazelFromPayload(&e3)}) + } + + if len(a.Components) >= 2 { + y := a.Components[1].NetID + d := innernetobjects.PlayerPhysics{} + d.Read(a.Components[1].ComponentData.GetUnderlyingPacket(), true) + var e innernetobjects.InnerNetObject = &d + proc.sus.state.CurrentGame.NetObjects[y] = &e + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerClientID[a.OwnerID].PlayerPhysicsNetID = y + } + if len(a.Components) >= 3 { + y := a.Components[2].NetID + d := innernetobjects.PlayerNetworkTransform{} + d.Read(a.Components[2].ComponentData.GetUnderlyingPacket(), true) + var e innernetobjects.InnerNetObject = &d + proc.sus.state.CurrentGame.NetObjects[y] = &e + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerClientID[a.OwnerID].PlayerNetworkTransform = y + + if isMyPlayer { + d.TargetPosition = mapdata.LobbySpawnPositions[int(b.PlayerID)%len(mapdata.LobbySpawnPositions)] + proc.sus.movementProcessor.Tick(0) + } + } + } + } +} + +func (proc *GameDataProcessor) captureGameDespawn(net *protocol.PacketHandler, GameID int32, h *protocol.Hazel) { + +} diff --git a/gamerpcprocessor.go b/gamerpcprocessor.go new file mode 100644 index 0000000..fb24609 --- /dev/null +++ b/gamerpcprocessor.go @@ -0,0 +1,101 @@ +package main + +import ( + "encoding/hex" + "fmt" + + "codehub.onpointcoding.net/sean/go-susapp/gamedata" + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type GameRPCProcessor struct { + sus *SusApp + rpcCallMap map[byte]func(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) +} + +func NewGameRPCProcessor(sus *SusApp) *GameRPCProcessor { + proc := &GameRPCProcessor{sus: sus} + proc.rpcCallMap = map[byte]func(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc){ + 0x00: proc.captureRpcPlayAnimation, + 0x02: proc.captureRpcSyncSettings, + 0x03: proc.captureRpcSetInfected, + 0x04: proc.captureRpcExiled, + 0x06: proc.captureRpcSetName, + 0x08: proc.captureRpcSetColor, + 0x09: proc.captureRpcSetHat, + 0x0a: proc.captureRpcSetSkin, + 0x11: proc.captureRpcSetPet, + 0x12: proc.captureRpcSetStartCounter, + } + return proc +} + +func (proc *GameRPCProcessor) receiveGameRpcCall(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + v, ok := proc.rpcCallMap[rpc.RPCCallID] + if ok { + v(net, GameID, rpc) + } else { + fmt.Printf("Unable to parse gamedata rpc call with id of %x\n", rpc.RPCCallID) + fmt.Printf("Packet: %v\n", hex.EncodeToString(rpc.UnderlyingHazel.GetUnderlyingPacket().Dump())) + } +} + +func (proc *GameRPCProcessor) captureRpcPlayAnimation(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + +} + +func (proc *GameRPCProcessor) captureRpcSyncSettings(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + a := &gamedata.RpcSyncSettings{} + a.ReadFromPacket(rpc.UnderlyingHazel.GetUnderlyingPacket()) + if a.Options.IsDefaults { + proc.sus.state.CurrentGame.GameOptions = protocol.NewDefaultGameOptionsData() + } else { + proc.sus.state.CurrentGame.GameOptions = a.Options + } +} + +func (proc *GameRPCProcessor) captureRpcSetInfected(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + +} + +func (proc *GameRPCProcessor) captureRpcExiled(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + +} + +func (proc *GameRPCProcessor) captureRpcSetName(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + a := &gamedata.RpcSetName{} + a.ReadFromPacket(rpc.UnderlyingHazel.GetUnderlyingPacket()) + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerNetID[rpc.SenderNetID].PlayerName = a.Name +} + +func (proc *GameRPCProcessor) captureRpcSetColor(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + a := &gamedata.RpcSetColor{} + a.ReadFromPacket(rpc.UnderlyingHazel.GetUnderlyingPacket()) + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerNetID[rpc.SenderNetID].PlayerColor = a.Color +} + +func (proc *GameRPCProcessor) captureRpcSetHat(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + a := &gamedata.RpcSetHat{} + a.ReadFromPacket(rpc.UnderlyingHazel.GetUnderlyingPacket()) + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerNetID[rpc.SenderNetID].PlayerHat = a.HatID +} + +func (proc *GameRPCProcessor) captureRpcSetSkin(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + a := &gamedata.RpcSetSkin{} + a.ReadFromPacket(rpc.UnderlyingHazel.GetUnderlyingPacket()) + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerNetID[rpc.SenderNetID].PlayerSkin = a.SkinID +} + +func (proc *GameRPCProcessor) captureRpcSetPet(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + a := &gamedata.RpcSetPet{} + a.ReadFromPacket(rpc.UnderlyingHazel.GetUnderlyingPacket()) + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerNetID[rpc.SenderNetID].PlayerPet = a.PetID +} + +func (proc *GameRPCProcessor) captureRpcSetStartCounter(net *protocol.PacketHandler, GameID int32, rpc *gamedata.Rpc) { + a := &gamedata.RpcSetStartCounter{} + a.ReadFromPacket(rpc.UnderlyingHazel.GetUnderlyingPacket()) + if proc.sus.state.CurrentGame != nil { + proc.sus.state.CurrentGame.StartGameTimer = a.TimeRemaining + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..4b87733 --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module codehub.onpointcoding.net/sean/go-susapp + +go 1.17 + +replace ( + codehub.onpointcoding.net/sean/go-susapp/enum => ./src/enum + codehub.onpointcoding.net/sean/go-susapp/gamedata => ./src/gamedata + codehub.onpointcoding.net/sean/go-susapp/innernetobjects => ./src/innernetobjects + codehub.onpointcoding.net/sean/go-susapp/mapdata => ./src/mapdata + codehub.onpointcoding.net/sean/go-susapp/packets => ./src/packets + codehub.onpointcoding.net/sean/go-susapp/protocol => ./src/protocol + codehub.onpointcoding.net/sean/go-susapp/systemtypes => ./src/systemtypes + codehub.onpointcoding.net/sean/go-susapp/util => ./src/util +) + +require ( + codehub.onpointcoding.net/sean/go-susapp/enum v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/gamedata v0.0.0-00010101000000-000000000000 + codehub.onpointcoding.net/sean/go-susapp/innernetobjects v0.0.0-00010101000000-000000000000 + codehub.onpointcoding.net/sean/go-susapp/packets v0.0.0-00010101000000-000000000000 + codehub.onpointcoding.net/sean/go-susapp/protocol v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/systemtypes v1.0.0 // indirect + codehub.onpointcoding.net/sean/go-susapp/util v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/mapdata v1.0.0 + github.com/gotk3/gotk3 v0.6.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7fc94be --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/gotk3/gotk3 v0.6.1 h1:GJ400a0ecEEWrzjBvzBzH+pB/esEMIGdB9zPSmBdoeo= +github.com/gotk3/gotk3 v0.6.1/go.mod h1:/hqFpkNa9T3JgNAE2fLvCdov7c5bw//FHNZrZ3Uv9/Q= diff --git a/keyhandler.go b/keyhandler.go new file mode 100644 index 0000000..6b5a47f --- /dev/null +++ b/keyhandler.go @@ -0,0 +1,80 @@ +package main + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "github.com/gotk3/gotk3/gdk" + "github.com/gotk3/gotk3/gtk" +) + +type KeyInputHandler struct { + state *State +} + +func NewKeyInputHandler(state *State) *KeyInputHandler { + return &KeyInputHandler{state} +} + +func (input *KeyInputHandler) keyPressEvent(win *gtk.ApplicationWindow, ev *gdk.Event) { + keyEvent := gdk.EventKeyNewFromEvent(ev) + k := gdk.KeyvalToUpper(keyEvent.KeyVal()) + if input.state.Screen == enum.SCREEN_TITLE && input.state.IsTypingReady { + if k >= gdk.KEY_A && k <= gdk.KEY_Z { + input.state.TypedGameID = input.state.TypedGameID + string(gdk.KeyvalToUnicode(k)) + } else if k == gdk.KEY_BackSpace { + if len(input.state.TypedGameID) > 0 { + input.state.TypedGameID = input.state.TypedGameID[:len(input.state.TypedGameID)-1] + } + } + } else if input.state.Screen == enum.SCREEN_LOBBY { + if k == gdk.KEY_W { + input.state.HoldingUp = true + } + if k == gdk.KEY_A { + input.state.HoldingLeft = true + } + if k == gdk.KEY_S { + input.state.HoldingDown = true + } + if k == gdk.KEY_D { + input.state.HoldingRight = true + } + input.updateMovementDirections() + } +} + +func (input *KeyInputHandler) keyReleaseEvent(win *gtk.ApplicationWindow, ev *gdk.Event) { + keyEvent := gdk.EventKeyNewFromEvent(ev) + k := gdk.KeyvalToUpper(keyEvent.KeyVal()) + if input.state.Screen == enum.SCREEN_LOBBY { + if k == gdk.KEY_W { + input.state.HoldingUp = false + } + if k == gdk.KEY_A { + input.state.HoldingLeft = false + } + if k == gdk.KEY_S { + input.state.HoldingDown = false + } + if k == gdk.KEY_D { + input.state.HoldingRight = false + } + input.updateMovementDirections() + } +} + +func (input *KeyInputHandler) updateMovementDirections() { + input.state.MovementX = 0 + input.state.MovementY = 0 + if input.state.HoldingUp { + input.state.MovementY-- + } + if input.state.HoldingLeft { + input.state.MovementX-- + } + if input.state.HoldingDown { + input.state.MovementY++ + } + if input.state.HoldingRight { + input.state.MovementX++ + } +} diff --git a/mousehandler.go b/mousehandler.go new file mode 100644 index 0000000..d0b27a2 --- /dev/null +++ b/mousehandler.go @@ -0,0 +1,79 @@ +package main + +import ( + "time" + + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/util" + "github.com/gotk3/gotk3/gdk" + "github.com/gotk3/gotk3/gtk" +) + +type MouseInputHandler struct { + state *State + renderer *Renderer + playGame func() + openSettings func() + disconnectFromGame func() + prevTime int64 +} + +func NewMouseInputHandler(state *State, renderer *Renderer, playGame func(), openSettings func(), disconnectFromGame func()) *MouseInputHandler { + return &MouseInputHandler{state, renderer, playGame, openSettings, disconnectFromGame, time.Now().UnixMilli()} +} + +/*func (input *MouseInputHandler) pollingEvent(delta int64) { + +}*/ + +func (input *MouseInputHandler) motionEvent(da *gtk.DrawingArea, event *gdk.Event) bool { + eventMotion := gdk.EventMotionNewFromEvent(event) + input.renderer.mouseX, input.renderer.mouseY = eventMotion.MotionVal() + return false +} + +func (input *MouseInputHandler) buttonPressEvent(window *gtk.ApplicationWindow, ev *gdk.Event) bool { + btn := gdk.EventButtonNewFromEvent(ev) + switch btn.Button() { + case gdk.BUTTON_PRIMARY: + w := input.renderer.w + h := input.renderer.h + if input.state.Screen == enum.SCREEN_TITLE { + if util.CollisionIntersectCenter(input.renderer.mouseX, input.renderer.mouseY, w/2, h/2, 200, 40) { + input.playGame() + } else if util.CollisionIntersectCenter(input.renderer.mouseX, input.renderer.mouseY, w-40, h-40, 40, 40) { + input.openSettings() + } else { + if util.CollisionIntersectCenter(input.renderer.mouseX, input.renderer.mouseY, w/2, h/2-60, 200, 40) { + input.state.IsTypingReady = true + } else { + input.state.IsTypingReady = false + } + } + } else if input.state.Screen == enum.SCREEN_LOBBY { + if input.state.IsLobbyMenuOpen { + mw := w / 2 + mh := h / 2 + overlayBottom := mh + 200 + if util.CollisionIntersectCenter(input.renderer.mouseX, input.renderer.mouseY, mw, overlayBottom-40, 200, 40) { + input.state.IsLobbyMenuOpen = false + } else if util.CollisionIntersectCenter(input.renderer.mouseX, input.renderer.mouseY, mw, overlayBottom-100, 200, 40) { + input.disconnectFromGame() + } + } else { + if util.CollisionIntersectCenter(input.renderer.mouseX, input.renderer.mouseY, w-40, 40, 40, 40) { + input.state.IsLobbyMenuOpen = true + } + } + } else if input.state.Screen == enum.SCREEN_ERROR { + mw := w / 2 + mh := h / 2 + if util.CollisionIntersectCenter(input.renderer.mouseX, input.renderer.mouseY, mw+190, mh-190, 40, 40) { + input.state.Screen = enum.SCREEN_TITLE + input.state.IsLobbyMenuOpen = false + } + } + return true + } + return false +} diff --git a/movementprocessor.go b/movementprocessor.go new file mode 100644 index 0000000..a30ecd3 --- /dev/null +++ b/movementprocessor.go @@ -0,0 +1,41 @@ +package main + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/gamedata" + "codehub.onpointcoding.net/sean/go-susapp/innernetobjects" + "codehub.onpointcoding.net/sean/go-susapp/packets" + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type MovementProcessor struct { + sus *SusApp +} + +func (proc *MovementProcessor) Tick(delta int64) { + if proc.sus.state.CurrentGame != nil && proc.sus.networkHandler != nil { + p := protocol.NewEmptyPacket() + cp, ok := proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerNetID[proc.sus.state.CurrentGame.ClientPlayerNetID] + if ok { + netId := cp.PlayerNetworkTransform + var c0 innernetobjects.InnerNetObject = *proc.sus.state.CurrentGame.NetObjects[netId] + a, ok := c0.(*innernetobjects.PlayerNetworkTransform) + if ok { + a.LastSequenceID++ + prevVelocityZero := a.TargetVelocity.X == 0 && a.TargetVelocity.Y == 0 + a.TargetVelocity.X = enum.PLAYER_SPEED * proc.sus.state.CurrentGame.GameOptions.PlayerSpeedModifier * proc.sus.state.MovementX / 2 + a.TargetVelocity.Y = enum.PLAYER_SPEED * proc.sus.state.CurrentGame.GameOptions.PlayerSpeedModifier * proc.sus.state.MovementY / 2 + nextVelocityZero := a.TargetVelocity.X == 0 && a.TargetVelocity.Y == 0 + if !nextVelocityZero || prevVelocityZero != nextVelocityZero { + a.TargetPosition.X += a.TargetVelocity.X * (float32(delta) / 1000) + a.TargetPosition.Y -= a.TargetVelocity.Y * (float32(delta) / 1000) + c0.Write(p, false) + var c1 protocol.UpdateComponent = protocol.UpdateComponent{NetID: netId, RawData: p} + var c2 packets.GameDataSubpacket = &gamedata.DataH2G{Component: &c1} + var c3 protocol.HazelPayload = packets.NewGameDataSingleSubpacket(proc.sus.state.CurrentGame.GameID, &c2) + proc.sus.networkHandler.SendNormalPacket([]*protocol.Hazel{protocol.NewHazelFromPayload(&c3)}) + } + } + } + } +} diff --git a/packetprocessor.go b/packetprocessor.go new file mode 100644 index 0000000..4b14140 --- /dev/null +++ b/packetprocessor.go @@ -0,0 +1,172 @@ +package main + +import ( + "encoding/hex" + "fmt" + + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/gamedata" + "codehub.onpointcoding.net/sean/go-susapp/innernetobjects" + "codehub.onpointcoding.net/sean/go-susapp/packets" + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/util" +) + +type PacketProcessor struct { + sus *SusApp + gameDataProcessor *GameDataProcessor + normalPacketTagMap map[byte]func(net *protocol.PacketHandler, h *protocol.Hazel) + reliablePacketTagMap map[byte]func(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) +} + +func NewPacketProcessor(sus *SusApp) *PacketProcessor { + proc := &PacketProcessor{sus: sus, gameDataProcessor: NewGameDataProcessor(sus)} + proc.normalPacketTagMap = map[byte]func(net *protocol.PacketHandler, h *protocol.Hazel){ + 0x05: proc.captureUnreliableGameDataAll, + 0x06: proc.captureUnreliableGameDataTo, + } + proc.reliablePacketTagMap = map[byte]func(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel){ + 0x04: proc.captureRemovePlayer, + 0x05: proc.captureGameDataAll, + 0x06: proc.captureGameDataTo, + 0x07: proc.captureJoinedGame, + 0x0a: proc.captureAlterGame, + 0x0d: proc.captureRedirect, + } + return proc +} + +func (proc *PacketProcessor) receiveNormalPacket(net *protocol.PacketHandler, h []*protocol.Hazel) { + for i := 0; i < len(h); i++ { + v, ok := proc.normalPacketTagMap[h[i].GetTag()] + if ok { + v(net, h[i]) + } else { + fmt.Printf("Unable to parse normal packet with tag of %x\n", h[i].GetTag()) + fmt.Printf("Packet %v\n", hex.EncodeToString(h[i].GetUnderlyingPacket().Dump())) + } + } +} + +func (proc *PacketProcessor) receiveReliablePacket(net *protocol.PacketHandler, nonce uint16, h []*protocol.Hazel) { + for i := 0; i < len(h); i++ { + v, ok := proc.reliablePacketTagMap[h[i].GetTag()] + if ok { + v(net, nonce, h[i]) + net.SendAcknowledgementPacket(nonce, 0xff) + } else { + fmt.Printf("Unable to parse packets with tag of %x\n", h[i].GetTag()) + fmt.Printf("Packet: %v\n", hex.EncodeToString(h[i].GetUnderlyingPacket().Dump())) + } + } +} + +func (proc *PacketProcessor) captureGameDataAll(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { + a := &packets.GameDataAll{} + a.Read(h.GetUnderlyingPacket()) + proc.gameDataProcessor.receiveGameDataSubpacket(net, a.GameID, a.Subpackets) +} + +func (proc *PacketProcessor) captureGameDataTo(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { + a := &packets.GameDataTo{} + a.Read(h.GetUnderlyingPacket()) + + if a.TargetClientID == proc.sus.state.CurrentGame.ClientID { + proc.gameDataProcessor.receiveGameDataSubpacket(net, a.GameID, a.Subpackets) + } +} + +func (proc *PacketProcessor) captureUnreliableGameDataAll(net *protocol.PacketHandler, h *protocol.Hazel) { + a := &packets.GameDataAll{} + a.Read(h.GetUnderlyingPacket()) + proc.gameDataProcessor.receiveGameDataSubpacket(net, a.GameID, a.Subpackets) +} + +func (proc *PacketProcessor) captureUnreliableGameDataTo(net *protocol.PacketHandler, h *protocol.Hazel) { + a := &packets.GameDataTo{} + a.Read(h.GetUnderlyingPacket()) + if a.TargetClientID == proc.sus.state.CurrentGame.ClientID { + proc.gameDataProcessor.receiveGameDataSubpacket(net, a.GameID, a.Subpackets) + } +} + +func (proc *PacketProcessor) captureJoinedGame(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { + a := &packets.JoinedGameH2C{} + a.Read(h.GetUnderlyingPacket()) + fmt.Printf("Joined game: %v\n", a.GameID) + fmt.Printf("Client id: %v\n", a.JoinedClientID) + fmt.Printf("Host client id: %v\n", a.HostClientID) + proc.sus.state.CurrentGame = &CurrentGameData{ + GameID: a.GameID, + ClientID: a.JoinedClientID, + HostClientID: a.HostClientID, + OtherClientIDs: make([]uint32, len(a.OtherClientIDs)), + GamePrivacy: 0, + GameOptions: protocol.NewDefaultGameOptionsData(), + NetObjects: map[uint32]*innernetobjects.InnerNetObject{}, + PlayerNetObjects: &PlayerNetObjectMaps{}, + } + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerClientID = map[uint32]*PlayerObject{} + proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerNetID = map[uint32]*PlayerObject{} + for j := 0; j < len(a.OtherClientIDs); j++ { + proc.sus.state.CurrentGame.OtherClientIDs[j] = a.OtherClientIDs[j] + } + proc.sus.state.Screen = enum.SCREEN_LOBBY + + // Send platform + var b1 packets.GameDataSubpacket = &gamedata.ClientInfoC2S{ + ClientID: proc.sus.state.CurrentGame.ClientID, + PlatformID: enum.PLATFORM_LinuxPlayer, + } + var b2 protocol.HazelPayload = packets.NewGameDataSingleSubpacket(proc.sus.state.CurrentGame.GameID, &b1) + net.SendReliablePacket(net.GetNonce(), []*protocol.Hazel{protocol.NewHazelFromPayload(&b2)}) + + // Send scene change + var c1 packets.GameDataSubpacket = &gamedata.SceneChangeC2H{ + ClientID: proc.sus.state.CurrentGame.ClientID, + SceneName: "OnlineGame", + } + var c2 protocol.HazelPayload = packets.NewGameDataSingleSubpacket(proc.sus.state.CurrentGame.GameID, &c1) + net.SendReliablePacket(net.GetNonce(), []*protocol.Hazel{protocol.NewHazelFromPayload(&c2)}) +} + +func (proc *PacketProcessor) captureAlterGame(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { + a := &packets.AlterGameH2G{} + a.Read(h.GetUnderlyingPacket()) + fmt.Printf("Alter game: %v\n", a.GameID) + fmt.Printf("Tag ID: %v\n", a.TagID) + fmt.Printf("Tag value: %v\n", a.TagValue) + if a.GameID == proc.sus.state.CurrentGame.GameID && a.TagID == 1 { + proc.sus.state.CurrentGame.GamePrivacy = a.TagValue + } +} + +func (proc *PacketProcessor) captureRedirect(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { + a := &packets.RedirectS2C{} + a.Read(h.GetUnderlyingPacket()) + fmt.Printf("Redirect: %v:%v", a.IPAddress.String(), a.Port) + if proc.sus.networkHandler != nil { + proc.sus.networkHandler.SendNormalDisconnectPacket() + proc.sus.networkHandler.Close() + proc.sus.networkHandler = nil + } + proc.sus.setupPacketHandler(&a.IPAddress, int(a.Port), func(ph *protocol.PacketHandler) { + fmt.Println("Sending first packet to new server") + ph.SendHelloPacket(ph.GetNonce(), 0, proc.sus.state.Version, proc.sus.state.Settings.PlayerName, 0, proc.sus.state.Settings.Language, proc.sus.state.Settings.QuickChatMode) + y, _ := util.CodeToGameID(proc.sus.state.TypedGameID) + var z protocol.HazelPayload = &packets.JoinGameC2S{GameID: y} + ph.SendReliablePacket(ph.GetNonce(), []*protocol.Hazel{ + protocol.NewHazelFromPayload(&z), + }) + }) +} + +func (proc *PacketProcessor) captureRemovePlayer(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { + a := &packets.RemovePlayerS2G{} + a.Read(h.GetUnderlyingPacket()) + if a.GameID == proc.sus.state.CurrentGame.GameID && a.HostClientID == proc.sus.state.CurrentGame.ClientID { + fmt.Println("Host player disconnect and left me in charge") + fmt.Println("I can't host a game so I'm disconnecting too") + net.SendNormalDisconnectPacket() + } +} diff --git a/project.json b/project.json new file mode 100644 index 0000000..ad02d87 --- /dev/null +++ b/project.json @@ -0,0 +1,9 @@ +{ + "Package": "melonapp", + "Version": "1.0-0", + "Section": "base", + "Priority": "optional", + "Architecture": "amd64", + "Maintainer": "MrMelon54 ", + "Description": "Melon App" +} diff --git a/renderer.go b/renderer.go new file mode 100644 index 0000000..1042cb3 --- /dev/null +++ b/renderer.go @@ -0,0 +1,273 @@ +package main + +import ( + "math" + "strconv" + "time" + + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/innernetobjects" + "codehub.onpointcoding.net/sean/go-susapp/util" + + "github.com/gotk3/gotk3/cairo" + "github.com/gotk3/gotk3/gtk" + "github.com/gotk3/gotk3/pango" +) + +type Renderer struct { + state *State + da *gtk.DrawingArea + cr *cairo.Context + w float64 + h float64 + mouseX float64 + mouseY float64 + delta int64 +} + +var defaultRenderFont string = "Noto Sans" + +func NewRenderer(state *State) *Renderer { + return &Renderer{state: state} +} + +func (r *Renderer) Draw(da *gtk.DrawingArea, cr *cairo.Context) { + r.delta = time.Now().UnixMilli() + r.da = da + r.cr = cr + allocation := da.GetAllocation() + r.w = float64(allocation.GetWidth()) + r.h = float64(allocation.GetHeight()) + + versionText := GetVersionFormatted(r.state.Version) + + // Background + r.DrawFilledRectangle(0, 0, r.w, r.h, 0x14181c) + + // Version numbers + r.DrawText(10, 10, versionText, "Noto Sans Display", 16, 0xffffff) + + if r.state.Screen == enum.SCREEN_TITLE { + colorLen := enum.GetPlayerTotalColors() + for i := 0; i < colorLen; i++ { + r.DrawFilledRectangle(float64(i)*20+(r.w-float64(colorLen)-float64(colorLen*20))/2, 0, 20, 20, enum.GetPlayerMainColor(i)) + r.DrawFilledRectangle(float64(i)*20+(r.w-float64(colorLen)-float64(colorLen*20))/2, 20, 20, 20, enum.GetPlayerShadedColor(i)) + } + + r.DrawButton(r.w-120, 20, 100, 40, r.state.Settings.PlayerName, defaultRenderFont, 16) + r.DrawButton(r.w-120, 60, 100, 40, r.state.Settings.ServerAddress, defaultRenderFont, 16) + r.DrawButton(r.w-120, 100, 100, 40, strconv.FormatInt(r.state.Settings.ServerPort, 10), defaultRenderFont, 16) + + r.DrawTextInput((r.w-200)/2, (r.h-20)/2-60, 200, 40, r.state.TypedGameID, defaultRenderFont, 16, r.state.IsTypingReady) + r.DrawButton((r.w-200)/2, (r.h-20)/2, 200, 40, "Play", defaultRenderFont, 16) + r.DrawButton(r.w-60, r.h-60, 40, 40, "⚙", defaultRenderFont, 16) + } else if r.state.Screen == enum.SCREEN_WAITING { + mw := r.w / 2 + mh := r.h / 2 + a := float64(r.delta/10) * (math.Pi / 180) + b := a + math.Pi/2 + c := a + math.Pi + d := a + math.Pi*3/2 + r.DrawFilledRoundedRectangle(mw+50*math.Sin(a)-20, mh+50*math.Cos(a)-20, 40, 40, 1, 20, 0xffffff) + r.DrawFilledRoundedRectangle(mw+50*math.Sin(b)-20, mh+50*math.Cos(b)-20, 40, 40, 1, 20, 0xffffff) + r.DrawFilledRoundedRectangle(mw+50*math.Sin(c)-20, mh+50*math.Cos(c)-20, 40, 40, 1, 20, 0xffffff) + r.DrawFilledRoundedRectangle(mw+50*math.Sin(d)-20, mh+50*math.Cos(d)-20, 40, 40, 1, 20, 0xffffff) + } else if r.state.Screen == enum.SCREEN_LOBBY { + r.DrawButton((r.w-100)/2, r.h-60, 100, 40, util.CodeFromGameID(r.state.CurrentGame.GameID), defaultRenderFont, 16) + r.DrawButton(r.w-60, 20, 40, 40, "⚙", defaultRenderFont, 16) + + if r.state.CurrentGame.GamePrivacy == 1 { + r.DrawButton((r.w-100)/2+120, r.h-60, 100, 40, "Public", defaultRenderFont, 16) + } else if r.state.CurrentGame.GamePrivacy == 0 { + r.DrawButton((r.w-100)/2+120, r.h-60, 100, 40, "Private", defaultRenderFont, 16) + } + + r.DrawPlayers() + + if r.state.IsLobbyMenuOpen { + mw := r.w / 2 + mh := r.h / 2 + overlayBottom := mh + 200 + + r.DrawFilledRectangle(0, 0, r.w, r.h, 0x80000000) + r.DrawFilledRoundedRectangle(mw-200, mh-200, 400, 400, 1, 20, 0x000000) + + r.DrawButton((r.w-200)/2, overlayBottom-60, 200, 40, "Return to Game", defaultRenderFont, 16) + r.DrawButton((r.w-200)/2, overlayBottom-120, 200, 40, "Leave Game", defaultRenderFont, 16) + } + } else if r.state.Screen == enum.SCREEN_ERROR { + mw := r.w / 2 + mh := r.h / 2 + r.DrawFilledRoundedRectangle(mw-200, mh-200, 400, 400, 1, 20, 0x000000) + + r.DrawText(mw-190, mh-190, "Disconnected", defaultRenderFont, 16, 0xffffff) + r.DrawButton(mw+170, mh-210, 40, 40, "×", defaultRenderFont, 16) + if r.state.DisconnectReason == 0xfe { + r.DrawWrappedText(mw-190, mh-156, 380, r.state.DisconnectStringReason, defaultRenderFont, 16, 0xffffff) + } else { + r.DrawWrappedText(mw-190, mh-156, 380, enum.DisconnectMessageText(r.state.DisconnectReason), defaultRenderFont, 16, 0xffffff) + } + } +} + +func (r *Renderer) SetRGB(red int, green int, blue int, alpha int) { + r.cr.SetSourceRGBA(float64(red)/0xff, float64(green)/0xff, float64(blue)/0xff, 0xff/float64(alpha)) +} + +func (r *Renderer) SetHex(hex int) { + r.SetRGB((hex/(256*256))%256, (hex/256)%256, hex%256, (hex/(256*256*256))%256) +} + +func (r *Renderer) DrawRectangle(x float64, y float64, w float64, h float64, color int) { + r.SetHex(color) + r.cr.Rectangle(x, y, w, h) + r.cr.Stroke() +} + +func (r *Renderer) DrawFilledRectangle(x float64, y float64, w float64, h float64, color int) { + r.SetHex(color) + r.cr.Rectangle(x, y, w, h) + r.cr.Fill() +} + +func (r *Renderer) DrawBorderedRectangle(x float64, y float64, w float64, h float64, color int, border int) { + r.DrawFilledRectangle(x, y, w, h, color) + r.DrawRectangle(x, y, w, h, border) +} + +func (r *Renderer) DrawRoundedRectangle(x float64, y float64, w float64, h float64, aspect float64, corner_radius float64, color int) { + a := corner_radius / aspect + d := math.Pi / 180 + r.cr.NewPath() + r.cr.Arc(x+w-a, y+a, a, -90*d, 0*d) + r.cr.Arc(x+w-a, y+h-a, a, 0*d, 90*d) + r.cr.Arc(x+a, y+h-a, a, 90*d, 180*d) + r.cr.Arc(x+a, y+a, a, 180*d, 270*d) + r.cr.ClosePath() + r.SetHex(color) + r.cr.Stroke() +} + +func (r *Renderer) DrawFilledRoundedRectangle(x float64, y float64, w float64, h float64, aspect float64, corner_radius float64, color int) { + a := corner_radius / aspect + d := math.Pi / 180 + r.cr.NewPath() + r.cr.Arc(x+w-a, y+a, a, -90*d, 0*d) + r.cr.Arc(x+w-a, y+h-a, a, 0*d, 90*d) + r.cr.Arc(x+a, y+h-a, a, 90*d, 180*d) + r.cr.Arc(x+a, y+a, a, 180*d, 270*d) + r.cr.ClosePath() + r.SetHex(color) + r.cr.FillPreserve() +} + +func (r *Renderer) DrawBorderedRoundedRectangle(x float64, y float64, w float64, h float64, aspect float64, corner_radius float64, color int, border int) { + a := corner_radius / aspect + d := math.Pi / 180 + r.cr.NewPath() + r.cr.Arc(x+w-a, y+a, a, -90*d, 0*d) + r.cr.Arc(x+w-a, y+h-a, a, 0*d, 90*d) + r.cr.Arc(x+a, y+h-a, a, 90*d, 180*d) + r.cr.Arc(x+a, y+a, a, 180*d, 270*d) + r.cr.ClosePath() + r.SetHex(color) + r.cr.FillPreserve() + r.SetHex(border) + r.cr.Stroke() +} + +func (r *Renderer) DrawText(x float64, y float64, text string, fontFamily string, fontSize float64, color int) { + layout := pango.CairoCreateLayout(r.cr) + font := pango.FontDescriptionFromString("sans normal 16") + font.SetFamily(fontFamily) + + layout.SetFontDescription(font) + layout.SetText(text, len(text)) + + r.SetHex(color) + r.cr.MoveTo(x, y) + pango.CairoUpdateLayout(r.cr, layout) + pango.CairoShowLayout(r.cr, layout) +} + +func (r *Renderer) DrawWrappedText(x float64, y float64, w float64, text string, fontFamily string, fontSize float64, color int) { + layout := pango.CairoCreateLayout(r.cr) + layout.SetWidth(int(w) * pango.SCALE) + font := pango.FontDescriptionFromString("sans normal 16") + font.SetFamily(fontFamily) + + layout.SetFontDescription(font) + layout.SetText(text, len(text)) + + r.SetHex(color) + r.cr.MoveTo(x, y) + pango.CairoUpdateLayout(r.cr, layout) + pango.CairoShowLayout(r.cr, layout) +} + +func (r *Renderer) DrawButton(x float64, y float64, w float64, h float64, text string, fontFamily string, fontSize float64) { + r.DrawFilledRoundedRectangle(x, y, w-4, h-4, w/h, 10, 0x000000) + + if r.mouseX > x && r.mouseX < x+w && r.mouseY > y && r.mouseY < y+h { + r.DrawRoundedRectangle(x, y, w-4, h-4, w/h, 10, 0x00ff00) + } else { + r.DrawRoundedRectangle(x, y, w-4, h-4, w/h, 10, 0xffffff) + } + + layout := pango.CairoCreateLayout(r.cr) + font := pango.FontDescriptionFromString("sans normal 16") + font.SetFamily(fontFamily) + + layout.SetFontDescription(font) + layout.SetText(text, len(text)) + + textWidth, textHeight := layout.GetSize() + textWidth /= pango.SCALE + textHeight /= pango.SCALE + + r.SetHex(0xffffff) + r.cr.MoveTo(x+(w-4-float64(textWidth))/2, y+(h-4-float64(textHeight))/2) + pango.CairoUpdateLayout(r.cr, layout) + pango.CairoShowLayout(r.cr, layout) +} + +func (r *Renderer) DrawTextInput(x float64, y float64, w float64, h float64, text string, fontFamily string, fontSize float64, inFocus bool) { + if r.mouseX > x && r.mouseX < x+w && r.mouseY > y && r.mouseY < y+h { + r.DrawFilledRectangle(x, y+h-6, w-4, 2, 0x00ffff) + } else { + r.DrawFilledRectangle(x, y+h-6, w-4, 2, 0xffffff) + } + + layout := pango.CairoCreateLayout(r.cr) + font := pango.FontDescriptionFromString("sans normal 16") + font.SetFamily(fontFamily) + + layout.SetFontDescription(font) + layout.SetText(text, len(text)) + + textWidth, textHeight := layout.GetSize() + textWidth /= pango.SCALE + textHeight /= pango.SCALE + + r.SetHex(0xffffff) + r.cr.MoveTo(x+(w-float64(textWidth))/2, y+(h-12-float64(textHeight))/2) + pango.CairoUpdateLayout(r.cr, layout) + pango.CairoShowLayout(r.cr, layout) + + if inFocus && r.delta%1600 < 800 { + r.DrawFilledRectangle(x+(w+float64(textWidth))/2, y+4, 2, 20, 0xffffff) + } +} + +func (r *Renderer) DrawPlayers() { + for _, item := range r.state.CurrentGame.PlayerNetObjects.ByPlayerClientID { + var t innernetobjects.InnerNetObject = *r.state.CurrentGame.NetObjects[item.PlayerNetworkTransform] + a, ok := t.(*innernetobjects.PlayerNetworkTransform) + if ok { + x := r.w/2 + float64(a.TargetPosition.X)*50 + y := r.h/2 - float64(a.TargetPosition.Y)*50 + r.DrawFilledRectangle(float64(x-5), float64(y-5), 10, 10, enum.GetPlayerMainColor(int(item.PlayerColor))) + r.DrawText(x, y, strconv.FormatFloat(float64(a.TargetPosition.X), 'e', 4, 64)+strconv.FormatFloat(float64(a.TargetPosition.Y), 'e', 4, 64), defaultRenderFont, 10, 0xffffff) + } + } +} diff --git a/settings.go b/settings.go new file mode 100644 index 0000000..5891546 --- /dev/null +++ b/settings.go @@ -0,0 +1,37 @@ +package main + +import ( + "encoding/json" + + "codehub.onpointcoding.net/sean/go-susapp/enum" +) + +type SettingsJSON struct { + PlayerName string `json:"PlayerName"` + PlayerColor byte `json:"PlayerColor"` + ServerAddress string `json:"ServerAddress"` + ServerPort int64 `json:"ServerPort"` + Language uint32 `json:"Language"` + QuickChatMode byte `json:"QuickChatMode"` +} + +func UnmarshalSettings(data []byte) (SettingsJSON, error) { + var r SettingsJSON + err := json.Unmarshal(data, &r) + return r, err +} + +func (r *SettingsJSON) Marshal() ([]byte, error) { + return json.Marshal(r) +} + +func NewDefaultSettingsJSON() SettingsJSON { + return SettingsJSON{ + PlayerName: "Player 1", + PlayerColor: 0, + ServerAddress: "localhost", + ServerPort: 22023, + Language: enum.GAMEKEYWORD_ALL, + QuickChatMode: enum.QUICK_CHAT_ONLY, + } +} diff --git a/settingswindow.go b/settingswindow.go new file mode 100644 index 0000000..ab954d1 --- /dev/null +++ b/settingswindow.go @@ -0,0 +1,225 @@ +package main + +import ( + "fmt" + "log" + "strconv" + + "codehub.onpointcoding.net/sean/go-susapp/enum" + "github.com/gotk3/gotk3/glib" + "github.com/gotk3/gotk3/gtk" +) + +var langaugeIdxMap = []int{ + enum.GAMEKEYWORD_ALL, + enum.GAMEKEYWORD_OTHER, + enum.GAMEKEYWORD_SPANISH, + enum.GAMEKEYWORD_KOREAN, + enum.GAMEKEYWORD_RUSSIAN, + enum.GAMEKEYWORD_PORTUGUESE, + enum.GAMEKEYWORD_ARABIC, + enum.GAMEKEYWORD_FILIPINO, + enum.GAMEKEYWORD_POLISH, + enum.GAMEKEYWORD_ENGLISH, + enum.GAMEKEYWORD_JAPANESE, +} +var quickChatIdxMap = []int{ + enum.FREE_CHAT_OR_QUICK_CHAT, + enum.QUICK_CHAT_ONLY, +} + +func openSettingsWindow(application *gtk.Application, mainWindow *gtk.ApplicationWindow, state *State) { + optWindow, err := gtk.ApplicationWindowNew(application) + if err != nil { + log.Fatal("Could not create application window.", err) + } + // Set ApplicationWindow Properties + optWindow.SetTitle("Sus App > Settings") + optWindow.SetDefaultSize(350, 350) + + layout, _ := gtk.LayoutNew(nil, nil) + optWindow.Add(layout) + + _, usernameEntry := addTextWidget(layout, 8, "Username:", state.Settings.PlayerName) + _, colorEntry := addColorWidget(layout, 50, "Color:", state.Settings.PlayerColor) + _, serverEntry := addTextWidget(layout, 92, "Address:", state.Settings.ServerAddress) + _, portEntry := addTextWidget(layout, 134, "Port:", strconv.FormatInt(state.Settings.ServerPort, 10)) + _, languageEntry := addLanguageWidget(layout, 176, "Language:", state.Settings.Language) + _, quickChatEntry := addQuickChatWidget(layout, 218, "Quick Chat:", state.Settings.QuickChatMode) + + applyBtn, _ := gtk.ButtonNewWithLabel("Apply") + layout.Put(applyBtn, 8, 218) + applyBtn.SetSizeRequest(334, 34) + applyBtn.Connect("clicked", func(b *gtk.Button) { + // + // Username + // + a1, err := usernameEntry.GetText() + if err == nil { + state.Settings.PlayerName = a1 + } else { + showSettingsErrorDialog(optWindow, "Failed to get new player name") + } + + // + // Server + // + a2, err := serverEntry.GetText() + if err == nil { + state.Settings.ServerAddress = a2 + } else { + showSettingsErrorDialog(optWindow, "Failed to get new server address") + } + + // + // Port + // + a3, err := portEntry.GetText() + if err == nil { + b3, err := strconv.ParseInt(a3, 10, 64) + if err == nil { + state.Settings.ServerPort = b3 + } else { + showSettingsErrorDialog(optWindow, fmt.Sprintf("Failed to parse port %v as it is not a valid integer", a3)) + } + } else { + showSettingsErrorDialog(optWindow, "Failed to get new port") + } + + // + // Language + // + a4 := languageEntry.GetActive() + state.Settings.Language = uint32(langaugeIdxMap[a4]) + + // + // Quick chat mode + // + a5 := quickChatEntry.GetActive() + state.Settings.QuickChatMode = byte(quickChatIdxMap[a5]) + + // + // Color + // + a6 := colorEntry.GetActive() + state.Settings.PlayerColor = byte(a6) + + // + // Save + // + state.SaveSettings() + }) + + optWindow.ShowAll() +} + +func showSettingsErrorDialog(optWindow *gtk.ApplicationWindow, msg string) { + mDialog := gtk.MessageDialogNew(optWindow, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg) + mDialog.SetTitle("Sus App Error") + mDialog.Run() + mDialog.Destroy() +} + +func addTextWidget(layout *gtk.Layout, y int, txt string, orig string) (*gtk.Label, *gtk.Entry) { + label, _ := gtk.LabelNew(txt) + layout.Put(label, 8, y+6) + + entry, _ := gtk.EntryNew() + layout.Put(entry, 100, y) + entry.SetSizeRequest(242, 34) + entry.SetText(orig) + + return label, entry +} + +func addLanguageWidget(layout *gtk.Layout, y int, txt string, orig uint32) (*gtk.Label, *gtk.ComboBox) { + var origIter *gtk.TreeIter + langStore, _ := gtk.ListStoreNew(glib.TYPE_UINT, glib.TYPE_STRING) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_ALL, "All", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_OTHER, "Other", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_SPANISH, "Spanish", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_KOREAN, "Korean", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_RUSSIAN, "Russian", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_PORTUGUESE, "Portuguese", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_ARABIC, "Arabic", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_FILIPINO, "Filipino", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_POLISH, "Polish", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_ENGLISH, "English", orig, origIter) + _, origIter = addRow(langStore, enum.GAMEKEYWORD_JAPANESE, "Japanese", orig, origIter) + + label, _ := gtk.LabelNew(txt) + layout.Put(label, 8, y+6) + + combo, _ := gtk.ComboBoxNewWithModel(langStore) + rendererText, _ := gtk.CellRendererTextNew() + combo.PackStart(rendererText, true) + combo.AddAttribute(rendererText, "text", 1) + combo.SetSizeRequest(242, 34) + combo.SetActiveIter(origIter) + layout.Put(combo, 100, y) + + return label, combo +} + +func addQuickChatWidget(layout *gtk.Layout, y int, txt string, orig byte) (*gtk.Label, *gtk.ComboBox) { + var origIter *gtk.TreeIter + modeStore, _ := gtk.ListStoreNew(glib.TYPE_UINT, glib.TYPE_STRING) + _, origIter = addRow(modeStore, enum.FREE_CHAT_OR_QUICK_CHAT, "Free Chat or Quick Chat", uint32(orig), origIter) + _, origIter = addRow(modeStore, enum.QUICK_CHAT_ONLY, "Quick Chat Only", uint32(orig), origIter) + + label, _ := gtk.LabelNew(txt) + layout.Put(label, 8, y+6) + + combo, _ := gtk.ComboBoxNewWithModel(modeStore) + rendererText, _ := gtk.CellRendererTextNew() + combo.PackStart(rendererText, true) + combo.AddAttribute(rendererText, "text", 1) + combo.SetSizeRequest(242, 34) + combo.SetActiveIter(origIter) + layout.Put(combo, 100, y) + + return label, combo +} + +func addColorWidget(layout *gtk.Layout, y int, txt string, orig byte) (*gtk.Label, *gtk.ComboBox) { + var origIter *gtk.TreeIter + modeStore, _ := gtk.ListStoreNew(glib.TYPE_UINT, glib.TYPE_STRING) + + for i := 0; i < enum.GetPlayerTotalColors(); i++ { + _, origIter = addRow(modeStore, uint32(i), enum.GetPlayerColorName(i), uint32(orig), origIter) + } + + label, _ := gtk.LabelNew(txt) + layout.Put(label, 8, y+6) + + combo, _ := gtk.ComboBoxNewWithModel(modeStore) + rendererText, _ := gtk.CellRendererTextNew() + combo.PackStart(rendererText, true) + combo.AddAttribute(rendererText, "text", 1) + combo.SetSizeRequest(242, 34) + combo.SetActiveIter(origIter) + layout.Put(combo, 100, y) + + return label, combo +} + +// Append a row to the list store for the tree view +func addRow(listStore *gtk.ListStore, id uint32, feature string, origId uint32, origIter *gtk.TreeIter) (*gtk.TreeIter, *gtk.TreeIter) { + // Get an iterator for a new row at the end of the list store + iter := listStore.Append() + + if id == origId { + origIter = iter + } + + // Set the contents of the list store row that the iterator represents + err := listStore.Set(iter, + []int{0, 1}, + []interface{}{id, feature}) + + if err != nil { + log.Fatal("Unable to add row:", err) + } + + return iter, origIter +} diff --git a/src/enum/disconnectreason.go b/src/enum/disconnectreason.go new file mode 100644 index 0000000..28d972a --- /dev/null +++ b/src/enum/disconnectreason.go @@ -0,0 +1,61 @@ +package enum + +const ( + DISCONNECT_EXIT_GAME = 0 + DISCONNECT_GAME_FULL = 1 + DISCONNECT_GAME_STARTED = 2 + DISCONNECT_GAME_NOT_FOUND = 3 + DISCONNECT_INCORRECT_VERSION = 5 + DISCONNECT_BANNED = 6 + DISCONNECT_KICKED = 7 + DISCONNECT_CUSTOM = 8 + DISCONNECT_INVALID_NAME = 9 + DISCONNECT_HACKING = 10 + DISCONNECT_NOT_AUTHORIZED = 11 + DISCONNECT_DESTROY = 16 + DISCONNECT_ERROR = 17 + DISCONNECT_INCORRECT_GAME = 18 + DISCONNECT_SERVER_REQUEST = 19 + DISCONNECT_SERVER_FULL = 20 + DISCONNECT_FOCUS_LOST_BACKGROUND = 207 +) + +func DisconnectMessageText(reason byte) string { + switch reason { + case DISCONNECT_EXIT_GAME: + return "Exit game" + case DISCONNECT_GAME_FULL: + return "Game full" + case DISCONNECT_GAME_STARTED: + return "Game started" + case DISCONNECT_GAME_NOT_FOUND: + return "Game not found" + case DISCONNECT_INCORRECT_VERSION: + return "Incorrect version" + case DISCONNECT_BANNED: + return "Banned" + case DISCONNECT_KICKED: + return "Kicked" + case DISCONNECT_CUSTOM: + return "Custom" + case DISCONNECT_INVALID_NAME: + return "Invalid name" + case DISCONNECT_HACKING: + return "Hacking" + case DISCONNECT_NOT_AUTHORIZED: + return "Not authorized" + case DISCONNECT_DESTROY: + return "Destroy" + case DISCONNECT_ERROR: + return "Error" + case DISCONNECT_INCORRECT_GAME: + return "Incorrect game" + case DISCONNECT_SERVER_REQUEST: + return "Server request" + case DISCONNECT_SERVER_FULL: + return "Server full" + case DISCONNECT_FOCUS_LOST_BACKGROUND: + return "Focus lost background" + } + return "Lost connection" +} diff --git a/src/enum/gamekeyword.go b/src/enum/gamekeyword.go new file mode 100644 index 0000000..b8a1bd3 --- /dev/null +++ b/src/enum/gamekeyword.go @@ -0,0 +1,15 @@ +package enum + +const ( + GAMEKEYWORD_ALL = 0 + GAMEKEYWORD_OTHER = 1 + GAMEKEYWORD_SPANISH = 2 + GAMEKEYWORD_KOREAN = 4 + GAMEKEYWORD_RUSSIAN = 8 + GAMEKEYWORD_PORTUGUESE = 16 + GAMEKEYWORD_ARABIC = 32 + GAMEKEYWORD_FILIPINO = 64 + GAMEKEYWORD_POLISH = 128 + GAMEKEYWORD_ENGLISH = 256 + GAMEKEYWORD_JAPANESE = 512 +) diff --git a/src/enum/gameoverreason.go b/src/enum/gameoverreason.go new file mode 100644 index 0000000..7bb8f32 --- /dev/null +++ b/src/enum/gameoverreason.go @@ -0,0 +1,11 @@ +package enum + +const ( + GAMEOVERREASON_CREWMATES_BY_VOTE = 0 + GAMEOVERREASON_CREWMATES_BY_TASK = 1 + GAMEOVERREASON_IMPOSTORS_BY_VOTE = 2 + GAMEOVERREASON_IMPOSTORS_BY_KILL = 3 + GAMEOVERREASON_IMPOSTORS_BY_SABOTAGE = 4 + GAMEOVERREASON_IMPOSTOR_DISCONNECT = 5 + GAMEOVERREASON_CREWMATE_DISCONNECT = 6 +) diff --git a/src/enum/gamestate.go b/src/enum/gamestate.go new file mode 100644 index 0000000..d8cf140 --- /dev/null +++ b/src/enum/gamestate.go @@ -0,0 +1,8 @@ +package enum + +const ( + GAMESTATE_NOT_STARTED = 0 + GAMESTATE_STARTED = 1 + GAMESTATE_ENDED = 2 + GAMESTATE_DESTROYED = 3 +) diff --git a/src/enum/go.mod b/src/enum/go.mod new file mode 100644 index 0000000..59d4ce2 --- /dev/null +++ b/src/enum/go.mod @@ -0,0 +1,3 @@ +module codehub.onpointcoding.net/sean/go-susapp/enum + +go 1.17 diff --git a/src/enum/hat.go b/src/enum/hat.go new file mode 100644 index 0000000..c414a2f --- /dev/null +++ b/src/enum/hat.go @@ -0,0 +1,119 @@ +package enum + +const ( + HAT_NONE = 0 + HAT_ASTRONAUT = 1 + HAT_BASEBALL_CAP = 2 + HAT_BRAIN_SLUG = 3 + HAT_BUSH_HAT = 4 + HAT_CAPTAIN_HAT = 5 + HAT_DOUBLE_TOP_HAT = 6 + HAT_FLOWERPOT = 7 + HAT_GOGGLES = 8 + HAT_HARD_HAT = 9 + HAT_MILITARY_HAT = 10 + HAT_PAPER_HAT = 11 + HAT_PARTY_HAT = 12 + HAT_POLICE_HAT = 13 + HAT_STETHOSCOPE = 14 + HAT_TOP_HAT = 15 + HAT_TOWEL_WIZARD = 16 + HAT_USHANKA = 17 + HAT_VIKING = 18 + HAT_WALL_GUARD_CAP = 19 + HAT_SNOWMAN = 20 + HAT_REINDEER_ANTLERS = 21 + HAT_CHRISTMAS_LIGHTS = 22 + HAT_SANTA_HAT = 23 + HAT_CHRISTMAS_TREE = 24 + HAT_CHRISTMAS_PRESENT = 25 + HAT_CANDY_CANES = 26 + HAT_ELF_HAT = 27 + HAT_NEW_YEARS_2018 = 28 + HAT_WHITE_HAT = 29 + HAT_CROWN = 30 + HAT_EYEBROWS = 31 + HAT_HALO = 32 + HAT_HERO_CAP = 33 + HAT_PIP_CAP = 34 + HAT_PLUNGER = 35 + HAT_SCUBA_MASK = 36 + HAT_HENRY_STICKMIN = 37 + HAT_STRAW_HAT = 38 + HAT_TEN_GALLON_HAT = 39 + HAT_THIRD_EYE = 40 + HAT_TOILET_PAPER = 41 + HAT_TOPPAT_CLAN_LEADER = 42 + HAT_BLACK_FEDORA = 43 + HAT_SKI_GOGGLES = 44 + HAT_HEARING_PROTECTION = 45 + HAT_HAZMAT_MASK = 46 + HAT_FACE_MASK = 47 + HAT_MIRA_SECURITY_CAP = 48 + HAT_SAFARI_HAT = 49 + HAT_BANANA = 50 + HAT_BEANIE = 51 + HAT_BEAR_EARS = 52 + HAT_CHEESE = 53 + HAT_CHERRY = 54 + HAT_EGG = 55 + HAT_GREEN_FEDORA = 56 + HAT_FLAMINGO = 57 + HAT_FLOWER_PIN = 58 + HAT_KNIGHT_HELMET = 59 + HAT_PLANT = 60 + HAT_BAT_EYES = 61 + HAT_BAT_WINGS = 62 + HAT_HORNS = 63 + HAT_MOHAWK = 64 + HAT_PUMPKIN = 65 + HAT_SCARY_PAPER_BAG = 66 + HAT_WITCH_HAT = 67 + HAT_WOLF_EARS = 68 + HAT_PIRATE_HAT = 69 + HAT_PLAGUE_DOCTOR = 70 + HAT_MACHETE = 71 + HAT_HOCKEY_MASK = 72 + HAT_MINER_HELMET = 73 + HAT_WINTER_CAP = 74 + HAT_ARCHAEOLOGIST_HAT = 75 + HAT_ANTENNA = 76 + HAT_BALLOON = 77 + HAT_BIRD_NEST = 78 + HAT_BLACK_BELT = 79 + HAT_CAUTION_SIGN = 80 + HAT_CHEF_HAT = 81 + HAT_COP_HAT = 82 + HAT_DO_RAG = 83 + HAT_DUM_STICKER = 84 + HAT_FEZ = 85 + HAT_GENERAL_HAT = 86 + HAT_POMPADOUR_HAIR = 87 + HAT_HUNTER_HAT = 88 + HAT_JUNGLE_HAT = 89 + HAT_MINI_CREWMATE = 90 + HAT_NINJA_MASK = 91 + HAT_RAM_HORNS = 92 + HAT_MINI_CREWMATE_SNOWMAN = 93 + HAT_GEOFF_KEIGHLEY_MASK = 94 + HAT_DAVE_PANPA_CAP = 95 + HAT_ELLIE_ROSE_HAIR = 96 + HAT_SVEN_SVENSSON_HAT = 97 + HAT_BURT_CURTIS_HAT = 98 + HAT_ELLRY_MOHAWK = 99 + HAT_THOMAS_CHESTERSHIRE_MONOCLES = 100 + HAT_WIZARD_HAT = 101 + HAT_FREDRICK_MUENSTER_HAT = 102 + HAT_MR_MACBETH_HAT = 103 + HAT_TOPPAT_HENRY_STICKMIN_HAT = 104 + HAT_TOPPAT_ELLIE_ROSE_HAT = 105 + HAT_GEOFFREY_PLUMB_HAT = 106 + HAT_ANGRY_EYEBROWS = 107 + HAT_CHOCOLATE_ICE_CREAM = 108 + HAT_HEART_PIN = 109 + HAT_PONYTAIL = 110 + HAT_RUBBER_GLOVE = 111 + HAT_UNICORN_HORN = 112 + HAT_ZIPPER = 113 + HAT_RIGHT_HAND_MAN_HAT = 114 +) diff --git a/src/enum/killdistance.go b/src/enum/killdistance.go new file mode 100644 index 0000000..62cf016 --- /dev/null +++ b/src/enum/killdistance.go @@ -0,0 +1,16 @@ +package enum + +const ( + KILLDISTANCE_SHORT = 0 + KILLDISTANCE_NORMAL = 1 + KILLDISTANCE_LONG = 2 +) + +var distanceMap = []float64{1, 1.8, 2.5} + +func GetKillDistanceMagnitude(a int) float64 { + if a < 0 || a > len(distanceMap) { + return 0 + } + return distanceMap[a] +} diff --git a/src/enum/limbostate.go b/src/enum/limbostate.go new file mode 100644 index 0000000..0240b1d --- /dev/null +++ b/src/enum/limbostate.go @@ -0,0 +1,7 @@ +package enum + +const ( + LIMBOSTATE_PRE_SPAWN = 0 + LIMBOSTATE_NOT_LIMBO = 1 + LIMBOSTATE_WAITING_FOR_HOST = 2 +) diff --git a/src/enum/map.go b/src/enum/map.go new file mode 100644 index 0000000..eb5f679 --- /dev/null +++ b/src/enum/map.go @@ -0,0 +1,20 @@ +package enum + +const ( + MAP_THE_SKELD = 0 + MAP_MIRA_HQ = 1 + MAP_POLUS = 2 + MAP_THE_AIRSHIP = 3 +) + +func MapBitfield(a []int) byte { + var b byte = 0 + for i := 0; i < len(a); i++ { + b |= 1 << a[i] + } + return b +} + +func BitfieldHasMap(b byte, a int) bool { + return b&(1< len(playerColorNames) { + return "Invalid Color" + } + return playerColorNames[a] +} + +func GetPlayerMainColor(a int) int { + if a < 0 || a > len(playerColors) { + return 0 + } + return playerColors[a] +} + +func GetPlayerShadedColor(a int) int { + if a < 0 || a > len(playerShadedColors) { + return 0 + } + return playerShadedColors[a] +} diff --git a/src/enum/playerspeed.go b/src/enum/playerspeed.go new file mode 100644 index 0000000..2bb6d19 --- /dev/null +++ b/src/enum/playerspeed.go @@ -0,0 +1,6 @@ +package enum + +const ( + PLAYER_SPEED = 4.5 + PLAYER_GHOST_SPEED = 3.0 +) diff --git a/src/enum/quickchatmode.go b/src/enum/quickchatmode.go new file mode 100644 index 0000000..f8f9450 --- /dev/null +++ b/src/enum/quickchatmode.go @@ -0,0 +1,6 @@ +package enum + +const ( + FREE_CHAT_OR_QUICK_CHAT = 1 + QUICK_CHAT_ONLY = 2 +) diff --git a/src/enum/reportoutcome.go b/src/enum/reportoutcome.go new file mode 100644 index 0000000..b9f5b68 --- /dev/null +++ b/src/enum/reportoutcome.go @@ -0,0 +1,9 @@ +package enum + +const ( + REPORTOUTCOME_NOT_REPORTED_UNKNOWN = 0 + REPORTOUTCOME_NOT_REPORTED_NO_ACCOUNT = 1 + REPORTOUTCOME_NOT_REPORTED_NOT_FOUND = 2 + REPORTOUTCOME_NOT_REPORTED_RATE_LIMIT = 3 + REPORTOUTCOME_REPORTED = 4 +) diff --git a/src/enum/reportreason.go b/src/enum/reportreason.go new file mode 100644 index 0000000..d9f126b --- /dev/null +++ b/src/enum/reportreason.go @@ -0,0 +1,8 @@ +package enum + +const ( + REPORTREASON_INAPPROPRIATE_NAME = 0 + REPORTREASON_INAPPROPRIATE_CHAT = 1 + REPORTREASON_CHEATING_HACKING = 2 + REPORTREASON_HARASSMENT_MISCONDUCT = 3 +) diff --git a/src/enum/rpccall.go b/src/enum/rpccall.go new file mode 100644 index 0000000..13c02ef --- /dev/null +++ b/src/enum/rpccall.go @@ -0,0 +1,36 @@ +package enum + +const ( + RPC_PLAY_ANIMATION = 0x00 + RPC_COMPLETE_TASK = 0x01 + RPC_SYNC_SETTINGS = 0x02 + RPC_SET_INFECTED = 0x03 + RPC_EXILED = 0x04 + RPC_CHECK_NAME = 0x05 + RPC_SET_NAME = 0x06 + RPC_CHECK_COLOR = 0x07 + RPC_SET_COLOR = 0x08 + RPC_SET_HAT = 0x09 + RPC_SET_SKIN = 0x0a + RPC_REPORT_DEAD_BODY = 0x0b + RPC_MURDER_PLAYER = 0x0c + RPC_SEND_CHAT = 0x0d + RPC_START_MEETING = 0x0e + RPC_SET_SCANNER = 0x0f + RPC_SEND_CHAT_NOTE = 0x10 + RPC_SET_PET = 0x11 + RPC_SET_START_COUNTER = 0x12 + RPC_ENTER_VENT = 0x13 + RPC_EXIT_VENT = 0x14 + RPC_SNAP_TO = 0x15 + RPC_CLOSE = 0x16 + RPC_VOTING_COMPLETE = 0x17 + RPC_CAST_VOTE = 0x18 + RPC_CLEAR_VOTE = 0x19 + RPC_ADD_VOTE = 0x1a + RPC_CLOSE_DOORS_OF_TYPE = 0x1b + RPC_REPAIR_SYSTEM = 0x1c + RPC_SET_TASKS = 0x1d + RPC_CLIMB_LADDER = 0x1f + RPC_USE_PLATFORM = 0x20 +) diff --git a/src/enum/screenstate.go b/src/enum/screenstate.go new file mode 100644 index 0000000..7df859f --- /dev/null +++ b/src/enum/screenstate.go @@ -0,0 +1,9 @@ +package enum + +const ( + SCREEN_TITLE = 0 + SCREEN_WAITING = 1 + SCREEN_LOBBY = 2 + SCREEN_INGAME = 3 + SCREEN_ERROR = 4 +) diff --git a/src/enum/skin.go b/src/enum/skin.go new file mode 100644 index 0000000..2d4e89f --- /dev/null +++ b/src/enum/skin.go @@ -0,0 +1,23 @@ +package enum + +const ( + SKIN_NONE = 0 + SKIN_ASTRONAUT = 1 + SKIN_CAPTAIN = 2 + SKIN_MECHANIC = 3 + SKIN_MILITARY = 4 + SKIN_POLICE = 5 + SKIN_SCIENTIST = 6 + SKIN_SUIT_BLACK = 7 + SKIN_SUIT_WHITE = 8 + SKIN_WALL_GUARD = 9 + SKIN_HAZMAT = 10 + SKIN_SECURITY_GUARD = 11 + SKIN_TARMAC = 12 + SKIN_MINER = 13 + SKIN_WINTER = 14 + SKIN_ARCHAEOLOGIST = 15 + SKIN_PRISONER = 16 + SKIN_CCC = 17 + SKIN_RIGHT_HAND_MAN_REBORN = 18 +) diff --git a/src/enum/spawnflag.go b/src/enum/spawnflag.go new file mode 100644 index 0000000..0440305 --- /dev/null +++ b/src/enum/spawnflag.go @@ -0,0 +1,6 @@ +package enum + +const ( + SPAWNFLAG_NONE = 0 + SPAWNFLAG_IS_CLIENT_CHARACTER = 1 +) diff --git a/src/enum/spawntype.go b/src/enum/spawntype.go new file mode 100644 index 0000000..50a0d7e --- /dev/null +++ b/src/enum/spawntype.go @@ -0,0 +1,13 @@ +package enum + +const ( + SPAWNTYPE_SKELD_SHIP_STATUS = 0 + SPAWNTYPE_MEETING_HUD = 1 + SPAWNTYPE_LOBBY_BEHAVIOUR = 2 + SPAWNTYPE_GAME_DATA = 3 + SPAWNTYPE_PLAYER_CONTROL = 4 + SPAWNTYPE_MIRA_SHIP_STATUS = 5 + SPAWNTYPE_POLUS_SHIP_STATUS = 6 + SPAWNTYPE_DLEKS_SHIP_STATUS = 7 + SPAWNTYPE_AIRSHIP_STATUS = 8 +) diff --git a/src/enum/systemtype.go b/src/enum/systemtype.go new file mode 100644 index 0000000..839211f --- /dev/null +++ b/src/enum/systemtype.go @@ -0,0 +1,53 @@ +package enum + +const ( + SYSTEMTYPE_HALLWAY = 0 + SYSTEMTYPE_STORAGE = 1 + SYSTEMTYPE_CAFETERIA = 2 + SYSTEMTYPE_REACTOR = 3 + SYSTEMTYPE_UPPER_ENGINE = 4 + SYSTEMTYPE_NAVIGATION = 5 + SYSTEMTYPE_ADMIN = 6 + SYSTEMTYPE_ELECTRICAL = 7 + SYSTEMTYPE_O2 = 8 + SYSTEMTYPE_SHIELDS = 9 + SYSTEMTYPE_MEDBAY = 10 + SYSTEMTYPE_SECURITY = 11 + SYSTEMTYPE_WEAPONS = 12 + SYSTEMTYPE_LOWER_ENGINE = 13 + SYSTEMTYPE_COMMUNICATIONS = 14 + SYSTEMTYPE_SHIP_TASKS = 15 + SYSTEMTYPE_DOORS = 16 + SYSTEMTYPE_SABOTAGE = 17 + SYSTEMTYPE_DECONTAMINATION = 18 + SYSTEMTYPE_LAUNCHPAD = 19 + SYSTEMTYPE_LOCKER_ROOM = 20 + SYSTEMTYPE_LABORATORY = 21 + SYSTEMTYPE_BALCONY = 22 + SYSTEMTYPE_OFFICE = 23 + SYSTEMTYPE_GREENHOUSE = 24 + SYSTEMTYPE_DROPSHIP = 25 + SYSTEMTYPE_DECONTAMINATION_2 = 26 + SYSTEMTYPE_OUTSIDE = 27 + SYSTEMTYPE_SPECIMENS = 28 + SYSTEMTYPE_BOILER_ROOM = 29 + SYSTEMTYPE_VAULT_ROOM = 30 + SYSTEMTYPE_COCKPIT = 31 + SYSTEMTYPE_ARMORY = 32 + SYSTEMTYPE_KITCHEN = 33 + SYSTEMTYPE_VIEWING_DECK = 34 + SYSTEMTYPE_HALL_OF_PORTRAITS = 35 + SYSTEMTYPE_CARGO_BAY = 36 + SYSTEMTYPE_VENTILATION = 37 + SYSTEMTYPE_SHOWERS = 38 + SYSTEMTYPE_ENGINE = 39 + SYSTEMTYPE_BRIG = 40 + SYSTEMTYPE_MEETING_ROOM = 41 + SYSTEMTYPE_RECORDS = 42 + SYSTEMTYPE_LOUNGE = 43 + SYSTEMTYPE_GAP_ROOM = 44 + SYSTEMTYPE_MAIN_HALL = 45 + SYSTEMTYPE_MEDICAL = 46 + + TOTAL_SYSTEMTYPE = 47 +) diff --git a/src/enum/taskbarmode.go b/src/enum/taskbarmode.go new file mode 100644 index 0000000..1bee2ea --- /dev/null +++ b/src/enum/taskbarmode.go @@ -0,0 +1,7 @@ +package enum + +const ( + TASKBARMODE_ALWAYS = 0 + TASKBARMODE_MEETINGS = 1 + TASKBARMODE_NEVER = 2 +) diff --git a/src/enum/tasktypes.go b/src/enum/tasktypes.go new file mode 100644 index 0000000..dc2f67f --- /dev/null +++ b/src/enum/tasktypes.go @@ -0,0 +1,64 @@ +package enum + +const ( + TASKTYPES_SUBMIT_SCAN = 0 + TASKTYPES_PRIME_SHIELDS = 1 + TASKTYPES_FUEL_ENGINES = 2 + TASKTYPES_CHART_COURSE = 3 + TASKTYPES_START_REACTOR = 4 + TASKTYPES_SWIPE_CARD = 5 + TASKTYPES_CLEAR_ASTEROIDS = 6 + TASKTYPES_UPLOAD_DATA = 7 + TASKTYPES_INSPECT_SAMPLE = 8 + TASKTYPES_EMPTY_CHUTE = 9 + TASKTYPES_EMPTY_GARBAGE = 10 + TASKTYPES_ALIGN_ENGINE_OUTPUT = 11 + TASKTYPES_FIX_WIRING = 12 + TASKTYPES_CALIBRATE_DISTRIBUTOR = 13 + TASKTYPES_DIVERT_POWER = 14 + TASKTYPES_UNLOCK_MANIFOLDS = 15 + TASKTYPES_RESET_REACTOR = 16 + TASKTYPES_FIX_LIGHTS = 17 + TASKTYPES_CLEAN_O2_FILTER = 18 + TASKTYPES_FIX_COMMS = 19 + TASKTYPES_RESTORE_O2 = 20 + TASKTYPES_STABILIZE_STEERING = 21 + TASKTYPES_ASSEMBLE_ARTIFACT = 22 + TASKTYPES_SORT_SAMPLES = 23 + TASKTYPES_MEASURE_WEATHER = 24 + TASKTYPES_ENTER_ID_CODE = 25 + TASKTYPES_BUY_BEVERAGE = 26 + TASKTYPES_PROCESS_DATA = 27 + TASKTYPES_RUN_DIAGNOSTICS = 28 + TASKTYPES_WATER_PLANTS = 29 + TASKTYPES_MONITOR_O2 = 30 + TASKTYPES_STORE_ARTIFACTS = 31 + TASKTYPES_FILL_CANISTERS = 32 + TASKTYPES_ACTIVATE_WEATHER_NODES = 33 + TASKTYPES_INSERT_KEYS = 34 + TASKTYPES_RESET_SEISMIC = 35 + TASKTYPES_SCAN_BOARDING_PASS = 36 + TASKTYPES_OPEN_WATERWAYS = 37 + TASKTYPES_REPLACE_WATER_JUG = 38 + TASKTYPES_REPAIR_DRILL = 39 + TASKTYPES_ALIGN_TELESCOPE = 40 + TASKTYPES_RECORD_TEMPERATURE = 41 + TASKTYPES_REBOOT_WIFI = 42 + TASKTYPES_POLISH_RUBY = 43 + TASKTYPES_RESET_BREAKERS = 44 + TASKTYPES_DECONTAMINATE = 45 + TASKTYPES_MAKE_BURGER = 46 + TASKTYPES_UNLOCK_SAFE = 47 + TASKTYPES_SORT_RECORDS = 48 + TASKTYPES_PUT_AWAY_PISTOLS = 49 + TASKTYPES_FIX_SHOWER = 50 + TASKTYPES_CLEAN_TOILET = 51 + TASKTYPES_DRESS_MANNEQUIN = 52 + TASKTYPES_PICK_UP_TOWELS = 53 + TASKTYPES_REWIND_TAPES = 54 + TASKTYPES_START_FANS = 55 + TASKTYPES_DEVELOP_PHOTOS = 56 + TASKTYPES_GET_BIGGOL_SWORD = 57 + TASKTYPES_PUT_AWAY_RIFLES = 58 + TASKTYPES_STOP_CHARLES = 59 +) diff --git a/src/gamedata/01_data.go b/src/gamedata/01_data.go new file mode 100644 index 0000000..fe12d09 --- /dev/null +++ b/src/gamedata/01_data.go @@ -0,0 +1,23 @@ +package gamedata + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type DataH2G struct { + Component *protocol.UpdateComponent +} + +func (d *DataH2G) AsHazel() *protocol.Hazel { + var a protocol.HazelPayload = d + return protocol.NewHazelFromPayload(&a) +} + +func (d *DataH2G) TypeCode() byte { return 0x01 } +func (d *DataH2G) Write(packet *protocol.Packet) { + d.Component.Write(packet) +} +func (d *DataH2G) Read(packet *protocol.Packet) { + d.Component = &protocol.UpdateComponent{} + d.Component.Read(packet) +} diff --git a/src/gamedata/02_rpc.go b/src/gamedata/02_rpc.go new file mode 100644 index 0000000..7c03087 --- /dev/null +++ b/src/gamedata/02_rpc.go @@ -0,0 +1,501 @@ +package gamedata + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/util" +) + +// +// RPC base +// + +type Rpc struct { + SenderNetID uint32 + RPCCallID byte + Sub *RpcSub + UnderlyingHazel *protocol.Hazel +} + +func NewRpcFromHazel(h *protocol.Hazel) *Rpc { + a := &Rpc{} + a.SenderNetID = h.GetUnderlyingPacket().ReadPackedUInt32() + a.RPCCallID = h.GetUnderlyingPacket().Read() + a.UnderlyingHazel = h + return a +} + +func NewRpcFromRpcSub(senderNetID uint32, sub *RpcSub) *Rpc { + return &Rpc{ + SenderNetID: senderNetID, + RPCCallID: (*sub).CallCode(), + Sub: sub, + } +} + +func (d *Rpc) GetUnderlyingHazel() *protocol.Hazel { + return d.UnderlyingHazel +} + +func (d *Rpc) AsHazel() *protocol.Hazel { + var a protocol.HazelPayload = d + return protocol.NewHazelFromPayload(&a) +} + +func (d *Rpc) TypeCode() byte { return 0x02 } +func (d *Rpc) Write(packet *protocol.Packet) { + packet.WritePackedUInt32(d.SenderNetID) + packet.Write(d.RPCCallID) + (*d.Sub).WriteToPacket(packet) +} +func (d *Rpc) Read(packet *protocol.Packet) { + d.SenderNetID = packet.ReadPackedUInt32() + d.RPCCallID = packet.Read() + // TODO: finish this +} + +type RpcSub interface { + CallCode() byte + WriteToPacket(packet *protocol.Packet) + ReadFromPacket(packet *protocol.Packet) +} + +// +// 0x00 RpcPlayAnimation +// + +type RpcPlayAnimation struct{ TaskID byte } + +func (d *RpcPlayAnimation) CallCode() byte { return enum.RPC_PLAY_ANIMATION } +func (d *RpcPlayAnimation) WriteToPacket(packet *protocol.Packet) { packet.Write(d.TaskID) } +func (d *RpcPlayAnimation) ReadFromPacket(packet *protocol.Packet) { d.TaskID = packet.Read() } + +// +// 0x01 RpcCompleteTask +// + +type RpcCompleteTask struct{ TaskIndex uint32 } + +func (d *RpcCompleteTask) CallCode() byte { return enum.RPC_COMPLETE_TASK } +func (d *RpcCompleteTask) WriteToPacket(packet *protocol.Packet) { + packet.WritePackedUInt32(d.TaskIndex) +} +func (d *RpcCompleteTask) ReadFromPacket(packet *protocol.Packet) { + d.TaskIndex = packet.ReadPackedUInt32() +} + +// +// 0x02 RpcSyncSettings +// + +type RpcSyncSettings struct{ Options *protocol.GameOptionsData } + +func (d *RpcSyncSettings) CallCode() byte { return enum.RPC_SYNC_SETTINGS } +func (d *RpcSyncSettings) WriteToPacket(packet *protocol.Packet) { d.Options.Write(packet) } +func (d *RpcSyncSettings) ReadFromPacket(packet *protocol.Packet) { + d.Options = protocol.NewGameOptionsDataFromPacket(packet) +} + +// +// 0x03 RpcSetInfected +// + +type RpcSetInfected struct{ Impostors []byte } + +func (d *RpcSetInfected) CallCode() byte { return enum.RPC_SET_INFECTED } +func (d *RpcSetInfected) WriteToPacket(packet *protocol.Packet) { + packet.Write(byte(len(d.Impostors))) + for i := 0; i < len(d.Impostors); i++ { + packet.Write(d.Impostors[i]) + } +} +func (d *RpcSetInfected) ReadFromPacket(packet *protocol.Packet) { + d.Impostors = make([]byte, packet.Read()) + for i := 0; i < len(d.Impostors); i++ { + d.Impostors[i] = packet.Read() + } +} + +// +// 0x04 RpcExiled +// + +type RpcExiled struct{} + +func (d *RpcExiled) CallCode() byte { return enum.RPC_EXILED } +func (d *RpcExiled) WriteToPacket(packet *protocol.Packet) {} +func (d *RpcExiled) ReadFromPacket(packet *protocol.Packet) {} + +// +// 0x05 RpcCheckName +// + +type RpcCheckName struct{ Name string } + +func (d *RpcCheckName) CallCode() byte { return enum.RPC_CHECK_NAME } +func (d *RpcCheckName) WriteToPacket(packet *protocol.Packet) { packet.WriteString(d.Name) } +func (d *RpcCheckName) ReadFromPacket(packet *protocol.Packet) { d.Name = packet.ReadString() } + +// +// 0x06 RpcSetName +// + +type RpcSetName struct{ Name string } + +func (d *RpcSetName) CallCode() byte { return enum.RPC_SET_NAME } +func (d *RpcSetName) WriteToPacket(packet *protocol.Packet) { packet.WriteString(d.Name) } +func (d *RpcSetName) ReadFromPacket(packet *protocol.Packet) { d.Name = packet.ReadString() } + +// +// 0x07 RpcCheckColor +// + +type RpcCheckColor struct{ Color byte } + +func (d *RpcCheckColor) CallCode() byte { return enum.RPC_CHECK_COLOR } +func (d *RpcCheckColor) WriteToPacket(packet *protocol.Packet) { packet.Write(d.Color) } +func (d *RpcCheckColor) ReadFromPacket(packet *protocol.Packet) { d.Color = packet.Read() } + +// +// 0x08 RpcSetColor +// + +type RpcSetColor struct{ Color byte } + +func (d *RpcSetColor) CallCode() byte { return enum.RPC_SET_COLOR } +func (d *RpcSetColor) WriteToPacket(packet *protocol.Packet) { packet.Write(d.Color) } +func (d *RpcSetColor) ReadFromPacket(packet *protocol.Packet) { d.Color = packet.Read() } + +// +// 0x09 RpcSetHat +// + +type RpcSetHat struct{ HatID uint32 } + +func (d *RpcSetHat) CallCode() byte { return enum.RPC_SET_HAT } +func (d *RpcSetHat) WriteToPacket(packet *protocol.Packet) { packet.WritePackedUInt32(d.HatID) } +func (d *RpcSetHat) ReadFromPacket(packet *protocol.Packet) { d.HatID = packet.ReadPackedUInt32() } + +// +// 0x0a RpcSetSkin +// + +type RpcSetSkin struct{ SkinID uint32 } + +func (d *RpcSetSkin) CallCode() byte { return enum.RPC_SET_SKIN } +func (d *RpcSetSkin) WriteToPacket(packet *protocol.Packet) { packet.WritePackedUInt32(d.SkinID) } +func (d *RpcSetSkin) ReadFromPacket(packet *protocol.Packet) { d.SkinID = packet.ReadPackedUInt32() } + +// +// 0x0b RpcReportDeadBody +// + +type RpcReportDeadBody struct{ VictimPlayerID byte } + +func (d *RpcReportDeadBody) CallCode() byte { return enum.RPC_REPORT_DEAD_BODY } +func (d *RpcReportDeadBody) WriteToPacket(packet *protocol.Packet) { packet.Write(d.VictimPlayerID) } +func (d *RpcReportDeadBody) ReadFromPacket(packet *protocol.Packet) { d.VictimPlayerID = packet.Read() } +func (d *RpcReportDeadBody) IsEmergencyMeetingButton() bool { return d.VictimPlayerID == 255 } + +// +// 0x0c RpcMurderPlayer +// + +type RpcMurderPlayer struct{ VictimNetID uint32 } + +func (d *RpcMurderPlayer) CallCode() byte { return enum.RPC_MURDER_PLAYER } +func (d *RpcMurderPlayer) WriteToPacket(packet *protocol.Packet) { + packet.WritePackedUInt32(d.VictimNetID) +} +func (d *RpcMurderPlayer) ReadFromPacket(packet *protocol.Packet) { + d.VictimNetID = packet.ReadPackedUInt32() +} + +// +// 0x0d RpcSendChat +// + +type RpcSendChat struct{ Message string } + +func (d *RpcSendChat) CallCode() byte { return enum.RPC_SEND_CHAT } +func (d *RpcSendChat) WriteToPacket(packet *protocol.Packet) { packet.WriteString(d.Message) } +func (d *RpcSendChat) ReadFromPacket(packet *protocol.Packet) { d.Message = packet.ReadString() } + +// +// 0x0e RpcStartMeeting +// + +type RpcStartMeeting struct{ VictimPlayerID byte } + +func (d *RpcStartMeeting) CallCode() byte { return enum.RPC_START_MEETING } +func (d *RpcStartMeeting) WriteToPacket(packet *protocol.Packet) { packet.Write(d.VictimPlayerID) } +func (d *RpcStartMeeting) ReadFromPacket(packet *protocol.Packet) { d.VictimPlayerID = packet.Read() } +func (d *RpcStartMeeting) IsEmergencyMeetingButton() bool { return d.VictimPlayerID == 255 } + +// +// 0x0f RpcSetScanner +// + +type RpcSetScanner struct { + IsScanning bool + SequenceID byte +} + +func (d *RpcSetScanner) CallCode() byte { return enum.RPC_SET_SCANNER } +func (d *RpcSetScanner) WriteToPacket(packet *protocol.Packet) { + packet.WriteBool(d.IsScanning) + packet.Write(d.SequenceID) +} +func (d *RpcSetScanner) ReadFromPacket(packet *protocol.Packet) { + d.IsScanning = packet.ReadBool() + d.SequenceID = packet.Read() +} + +// +// 0x10 RpcSendChatNote +// + +type RpcSendChatNote struct { + PlayerID byte + ChatNoteType byte +} + +func (d *RpcSendChatNote) CallCode() byte { return enum.RPC_SEND_CHAT_NOTE } +func (d *RpcSendChatNote) WriteToPacket(packet *protocol.Packet) { + packet.Write(d.PlayerID) + packet.Write(d.ChatNoteType) +} +func (d *RpcSendChatNote) ReadFromPacket(packet *protocol.Packet) { + d.PlayerID = packet.Read() + d.ChatNoteType = packet.Read() +} + +// +// 0x11 RpcSetPet +// + +type RpcSetPet struct{ PetID uint32 } + +func (d *RpcSetPet) CallCode() byte { return enum.RPC_SET_PET } +func (d *RpcSetPet) WriteToPacket(packet *protocol.Packet) { packet.WritePackedUInt32(d.PetID) } +func (d *RpcSetPet) ReadFromPacket(packet *protocol.Packet) { d.PetID = packet.ReadPackedUInt32() } + +// +// 0x12 RpcSetStartCounter +// + +type RpcSetStartCounter struct { + SequenceID uint32 + TimeRemaining byte +} + +func (d *RpcSetStartCounter) CallCode() byte { return enum.RPC_SET_START_COUNTER } +func (d *RpcSetStartCounter) WriteToPacket(packet *protocol.Packet) { + packet.WritePackedUInt32(d.SequenceID) + packet.Write(d.TimeRemaining) +} +func (d *RpcSetStartCounter) ReadFromPacket(packet *protocol.Packet) { + d.SequenceID = packet.ReadPackedUInt32() + d.TimeRemaining = packet.Read() +} +func (d *RpcSetStartCounter) ShouldResetTimer() bool { return d.TimeRemaining == 0xff } + +// +// 0x13 RpcEnterVent +// + +type RpcEnterVent struct{ VentID uint32 } + +func (d *RpcEnterVent) CallCode() byte { return enum.RPC_ENTER_VENT } +func (d *RpcEnterVent) WriteToPacket(packet *protocol.Packet) { packet.WritePackedUInt32(d.VentID) } +func (d *RpcEnterVent) ReadFromPacket(packet *protocol.Packet) { d.VentID = packet.ReadPackedUInt32() } + +// +// 0x14 RpcExitVent +// + +type RpcExitVent struct{ VentID uint32 } + +func (d *RpcExitVent) CallCode() byte { return enum.RPC_EXIT_VENT } +func (d *RpcExitVent) WriteToPacket(packet *protocol.Packet) { packet.WritePackedUInt32(d.VentID) } +func (d *RpcExitVent) ReadFromPacket(packet *protocol.Packet) { d.VentID = packet.ReadPackedUInt32() } + +// +// 0x15 RpcSnapTo +// + +type RpcSnapTo struct { + Position util.Vec2 + LastSequenceID uint16 +} + +func (d *RpcSnapTo) CallCode() byte { return enum.RPC_SNAP_TO } +func (d *RpcSnapTo) WriteToPacket(packet *protocol.Packet) { + packet.WriteVector2(d.Position) + packet.WriteUInt16(d.LastSequenceID) +} +func (d *RpcSnapTo) ReadFromPacket(packet *protocol.Packet) { + d.Position = packet.ReadVector2() + d.LastSequenceID = packet.ReadUInt16() +} + +// +// 0x16 RpcClose +// + +type RpcClose struct{} + +func (d *RpcClose) CallCode() byte { return enum.RPC_CLOSE } +func (d *RpcClose) WriteToPacket(packet *protocol.Packet) {} +func (d *RpcClose) ReadFromPacket(packet *protocol.Packet) {} + +// +// 0x17 RpcVotingComplete +// + +type RpcVotingComplete struct { + VoteStates []byte + ExiledPlayerID byte + IsTie bool +} + +// TODO: write serializer for voting complete +func (d *RpcVotingComplete) CallCode() byte { return enum.RPC_VOTING_COMPLETE } +func (d *RpcVotingComplete) WriteToPacket(packet *protocol.Packet) {} +func (d *RpcVotingComplete) ReadFromPacket(packet *protocol.Packet) {} + +// +// 0x18 RpcCastVote +// + +type RpcCastVote struct { + VotingPlayerID byte + SuspectPlayerID byte +} + +func (d *RpcCastVote) CallCode() byte { return enum.RPC_CAST_VOTE } +func (d *RpcCastVote) WriteToPacket(packet *protocol.Packet) { + packet.Write(d.VotingPlayerID) + packet.Write(d.SuspectPlayerID) +} +func (d *RpcCastVote) ReadFromPacket(packet *protocol.Packet) { + d.VotingPlayerID = packet.Read() + d.SuspectPlayerID = packet.Read() +} + +// +// 0x19 RpcClearVote +// + +type RpcClearVote struct{} + +func (d *RpcClearVote) CallCode() byte { return enum.RPC_CLEAR_VOTE } +func (d *RpcClearVote) WriteToPacket(packet *protocol.Packet) {} +func (d *RpcClearVote) ReadFromPacket(packet *protocol.Packet) {} + +// +// 0x1a RpcAddVote +// + +type RpcAddVote struct { + VotingClientID uint32 + TargetClientID uint32 +} + +func (d *RpcAddVote) CallCode() byte { return enum.RPC_ADD_VOTE } +func (d *RpcAddVote) WriteToPacket(packet *protocol.Packet) { + packet.WriteUInt32(d.VotingClientID) + packet.WriteUInt32(d.TargetClientID) +} +func (d *RpcAddVote) ReadFromPacket(packet *protocol.Packet) { + d.VotingClientID = packet.ReadUInt32() + d.TargetClientID = packet.ReadUInt32() +} + +// +// 0x1b RpcCloseDoorsOfType +// + +type RpcCloseDoorsOfType struct { + SystemID byte +} + +func (d *RpcCloseDoorsOfType) CallCode() byte { return enum.RPC_CLOSE_DOORS_OF_TYPE } +func (d *RpcCloseDoorsOfType) WriteToPacket(packet *protocol.Packet) { packet.Write(d.SystemID) } +func (d *RpcCloseDoorsOfType) ReadFromPacket(packet *protocol.Packet) { d.SystemID = packet.Read() } + +// +// 0x1c RpcRepairSystem +// + +type RpcRepairSystem struct { + SystemID byte + PlayerControlNetID uint32 + Amount byte +} + +func (d *RpcRepairSystem) CallCode() byte { return enum.RPC_REPAIR_SYSTEM } +func (d *RpcRepairSystem) WriteToPacket(packet *protocol.Packet) { + packet.Write(d.SystemID) + packet.WritePackedUInt32(d.PlayerControlNetID) + packet.Write(d.Amount) +} +func (d *RpcRepairSystem) ReadFromPacket(packet *protocol.Packet) { + d.SystemID = packet.Read() + d.PlayerControlNetID = packet.ReadPackedUInt32() + d.Amount = packet.Read() +} + +// +// 0x1d RpcSetTasks +// + +type RpcSetTasks struct { + PlayerID byte + Tasks []byte +} + +func (d *RpcSetTasks) CallCode() byte { return enum.RPC_SET_TASKS } +func (d *RpcSetTasks) WriteToPacket(packet *protocol.Packet) { + packet.Write(d.PlayerID) + packet.WritePackedUInt32(uint32(len(d.Tasks))) + for i := 0; i < len(d.Tasks); i++ { + packet.Write(d.Tasks[i]) + } +} +func (d *RpcSetTasks) ReadFromPacket(packet *protocol.Packet) { + d.PlayerID = packet.Read() + d.Tasks = make([]byte, packet.ReadPackedUInt32()) + for i := 0; i < len(d.Tasks); i++ { + d.Tasks[i] = packet.Read() + } +} + +// +// 0x1f RpcClimbLadder +// + +type RpcClimbLadder struct { + LadderID byte + SequenceID byte +} + +func (d *RpcClimbLadder) CallCode() byte { return enum.RPC_CLIMB_LADDER } +func (d *RpcClimbLadder) WriteToPacket(packet *protocol.Packet) { + packet.Write(d.LadderID) + packet.Write(d.SequenceID) +} +func (d *RpcClimbLadder) ReadFromPacket(packet *protocol.Packet) { + d.LadderID = packet.Read() + d.SequenceID = packet.Read() +} + +// +// 0x1f RpcUsePlatform +// + +type RpcUsePlatform struct{} + +func (d *RpcUsePlatform) CallCode() byte { return enum.RPC_CLIMB_LADDER } +func (d *RpcUsePlatform) WriteToPacket(packet *protocol.Packet) {} +func (d *RpcUsePlatform) ReadFromPacket(packet *protocol.Packet) {} diff --git a/src/gamedata/04_spawn.go b/src/gamedata/04_spawn.go new file mode 100644 index 0000000..0edf575 --- /dev/null +++ b/src/gamedata/04_spawn.go @@ -0,0 +1,36 @@ +package gamedata + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type SpawnH2G struct { + SpawnType uint32 + OwnerID uint32 + SpawnFlags byte + Components []*protocol.SpawnComponent +} + +func (d *SpawnH2G) AsHazel() *protocol.Hazel { + var a protocol.HazelPayload = d + return protocol.NewHazelFromPayload(&a) +} + +func (d *SpawnH2G) TypeCode() byte { return 0x04 } + +func (d *SpawnH2G) Write(packet *protocol.Packet) { + packet.WritePackedUInt32(d.SpawnType) + packet.WritePackedUInt32(d.OwnerID) + packet.Write(d.SpawnFlags) + for i := 0; i < len(d.Components); i++ { + d.Components[i].Write(packet) + } +} +func (d *SpawnH2G) Read(packet *protocol.Packet) { + d.SpawnType = packet.ReadPackedUInt32() + d.OwnerID = packet.ReadPackedUInt32() + d.SpawnFlags = packet.Read() + d.Components = make([]*protocol.SpawnComponent, packet.Read()) + for i := 0; i < len(d.Components); i++ { + d.Components[i] = &protocol.SpawnComponent{} + d.Components[i].Read(packet) + } +} diff --git a/src/gamedata/05_despawn.go b/src/gamedata/05_despawn.go new file mode 100644 index 0000000..facb952 --- /dev/null +++ b/src/gamedata/05_despawn.go @@ -0,0 +1,16 @@ +package gamedata + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type DespawnH2G struct { + NetID uint32 +} + +func (d *DespawnH2G) AsHazel() *protocol.Hazel { + var a protocol.HazelPayload = d + return protocol.NewHazelFromPayload(&a) +} + +func (d *DespawnH2G) TypeCode() byte { return 0x05 } +func (d *DespawnH2G) Write(packet *protocol.Packet) { packet.WritePackedUInt32(d.NetID) } +func (d *DespawnH2G) Read(packet *protocol.Packet) { d.NetID = packet.ReadPackedUInt32() } diff --git a/src/gamedata/06_scenechange.go b/src/gamedata/06_scenechange.go new file mode 100644 index 0000000..2c65929 --- /dev/null +++ b/src/gamedata/06_scenechange.go @@ -0,0 +1,23 @@ +package gamedata + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type SceneChangeC2H struct { + ClientID uint32 + SceneName string +} + +func (d *SceneChangeC2H) AsHazel() *protocol.Hazel { + var a protocol.HazelPayload = d + return protocol.NewHazelFromPayload(&a) +} + +func (d *SceneChangeC2H) TypeCode() byte { return 0x06 } +func (d *SceneChangeC2H) Write(packet *protocol.Packet) { + packet.WritePackedUInt32(d.ClientID) + packet.WriteString(d.SceneName) +} +func (d *SceneChangeC2H) Read(packet *protocol.Packet) { + d.ClientID = packet.ReadPackedUInt32() + d.SceneName = packet.ReadString() +} diff --git a/src/gamedata/07_ready.go b/src/gamedata/07_ready.go new file mode 100644 index 0000000..0172b7f --- /dev/null +++ b/src/gamedata/07_ready.go @@ -0,0 +1,16 @@ +package gamedata + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type ReadyC2H struct { + ReadyClientID uint32 +} + +func (d *ReadyC2H) AsHazel() *protocol.Hazel { + var a protocol.HazelPayload = d + return protocol.NewHazelFromPayload(&a) +} + +func (d *ReadyC2H) TypeCode() byte { return 0x07 } +func (d *ReadyC2H) Write(packet *protocol.Packet) { packet.WritePackedUInt32(d.ReadyClientID) } +func (d *ReadyC2H) Read(packet *protocol.Packet) { d.ReadyClientID = packet.ReadPackedUInt32() } diff --git a/src/gamedata/08_changesettings.go b/src/gamedata/08_changesettings.go new file mode 100644 index 0000000..5955085 --- /dev/null +++ b/src/gamedata/08_changesettings.go @@ -0,0 +1,5 @@ +package gamedata + +// ChangeSettingsAll is never used +type ChangeSettingsAll struct { +} diff --git a/src/gamedata/205_clientinfo.go b/src/gamedata/205_clientinfo.go new file mode 100644 index 0000000..66155fb --- /dev/null +++ b/src/gamedata/205_clientinfo.go @@ -0,0 +1,23 @@ +package gamedata + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type ClientInfoC2S struct { + ClientID uint32 + PlatformID uint32 +} + +func (d *ClientInfoC2S) AsHazel() *protocol.Hazel { + var a protocol.HazelPayload = d + return protocol.NewHazelFromPayload(&a) +} + +func (d *ClientInfoC2S) TypeCode() byte { return 0xcd } +func (d *ClientInfoC2S) Write(packet *protocol.Packet) { + packet.WritePackedUInt32(d.ClientID) + packet.WritePackedUInt32(d.PlatformID) +} +func (d *ClientInfoC2S) Read(packet *protocol.Packet) { + d.ClientID = packet.ReadPackedUInt32() + d.PlatformID = packet.ReadPackedUInt32() +} diff --git a/src/gamedata/go.mod b/src/gamedata/go.mod new file mode 100644 index 0000000..750f2df --- /dev/null +++ b/src/gamedata/go.mod @@ -0,0 +1,13 @@ +module codehub.onpointcoding.net/sean/go-susapp/gamedata + +go 1.17 + +replace ( + codehub.onpointcoding.net/sean/go-susapp/protocol => ../protocol + codehub.onpointcoding.net/sean/go-susapp/util => ../util +) + +require ( + codehub.onpointcoding.net/sean/go-susapp/protocol v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/util v1.0.0 +) diff --git a/src/innernetobjects/gamedata.go b/src/innernetobjects/gamedata.go new file mode 100644 index 0000000..a52efd7 --- /dev/null +++ b/src/innernetobjects/gamedata.go @@ -0,0 +1,35 @@ +package innernetobjects + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type GameData struct { + Players []*protocol.PlayerData +} + +func (data *GameData) Write(packet *protocol.Packet, isSpawn bool) { + if isSpawn { + packet.WriteUInt32(uint32(len(data.Players))) + } else { + packet.Write(byte(len(data.Players))) + } + for i := 0; i < len(data.Players); i++ { + data.Players[i].Write(packet) + } +} +func (data *GameData) Read(packet *protocol.Packet, isSpawn bool) { + packet.ReadBytes(4) + if packet.Dump()[0] == 0x0c { + return + } + if isSpawn { + data.Players = make([]*protocol.PlayerData, packet.ReadPackedUInt32()) + } else { + data.Players = make([]*protocol.PlayerData, packet.Read()) + } + for i := 0; i < len(data.Players); i++ { + data.Players[i] = &protocol.PlayerData{} + data.Players[i].Read(packet) + } +} diff --git a/src/innernetobjects/go.mod b/src/innernetobjects/go.mod new file mode 100644 index 0000000..2ed8e79 --- /dev/null +++ b/src/innernetobjects/go.mod @@ -0,0 +1,17 @@ +module codehub.onpointcoding.net/sean/go-susapp/innernetobjects + +go 1.17 + +replace ( + codehub.onpointcoding.net/sean/go-susapp/enum => ../enum + codehub.onpointcoding.net/sean/go-susapp/protocol => ../protocol + codehub.onpointcoding.net/sean/go-susapp/systemtypes => ../systemtypes + codehub.onpointcoding.net/sean/go-susapp/util => ../util +) + +require ( + codehub.onpointcoding.net/sean/go-susapp/enum v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/protocol v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/systemtypes v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/util v1.0.0 // indirect +) diff --git a/src/innernetobjects/innernetobject.go b/src/innernetobjects/innernetobject.go new file mode 100644 index 0000000..3ddc188 --- /dev/null +++ b/src/innernetobjects/innernetobject.go @@ -0,0 +1,8 @@ +package innernetobjects + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type InnerNetObject interface { + Write(packet *protocol.Packet, isSpawn bool) + Read(packet *protocol.Packet, isSpawn bool) +} diff --git a/src/innernetobjects/mirashipstatus.go b/src/innernetobjects/mirashipstatus.go new file mode 100644 index 0000000..cf15558 --- /dev/null +++ b/src/innernetobjects/mirashipstatus.go @@ -0,0 +1,33 @@ +package innernetobjects + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/systemtypes" +) + +type MiraShipStatus struct { + Systems map[int]*systemtypes.SystemType + SystemsMask uint32 +} + +func NewMiraShipStatus() *MiraShipStatus { + a := &MiraShipStatus{} + a.Systems = make(map[int]*systemtypes.SystemType) + a.Systems[enum.SYSTEMTYPE_REACTOR] = systemtypes.SystemTypeCast(&systemtypes.ReactorSystem{}) + a.Systems[enum.SYSTEMTYPE_ELECTRICAL] = systemtypes.SystemTypeCast(&systemtypes.SwitchSystem{}) + a.Systems[enum.SYSTEMTYPE_O2] = systemtypes.SystemTypeCast(&systemtypes.LifeSuppSystem{}) + a.Systems[enum.SYSTEMTYPE_MEDBAY] = systemtypes.SystemTypeCast(&systemtypes.MedScanSystem{}) + a.Systems[enum.SYSTEMTYPE_COMMUNICATIONS] = systemtypes.SystemTypeCast(&systemtypes.HqHudSystem{}) + a.Systems[enum.SYSTEMTYPE_SABOTAGE] = systemtypes.SystemTypeCast(&systemtypes.SabotageSystem{}) + a.Systems[enum.SYSTEMTYPE_DECONTAMINATION] = systemtypes.SystemTypeCast(&systemtypes.DeconSystem{}) + return a +} + +func (mira *MiraShipStatus) Write(packet *protocol.Packet, isSpawn bool) { + WriteShipStatus(mira.Systems, &mira.SystemsMask, packet, isSpawn) +} + +func (mira *MiraShipStatus) Read(packet *protocol.Packet, isSpawn bool) { + ReadShipStatus(mira.Systems, &mira.SystemsMask, packet, isSpawn) +} diff --git a/src/innernetobjects/playercontrol.go b/src/innernetobjects/playercontrol.go new file mode 100644 index 0000000..d80bada --- /dev/null +++ b/src/innernetobjects/playercontrol.go @@ -0,0 +1,32 @@ +package innernetobjects + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type PlayerControl struct { + IsNew bool + PlayerID byte +} + +func (control *PlayerControl) Write(packet *protocol.Packet, isSpawn bool) { + p := protocol.NewEmptyPacket() + if isSpawn { + p.WriteBool(control.IsNew) + p.Write(control.PlayerID) + h := protocol.NewHazelFromRawData(0, p.Dump()) + h.WriteToPacket(packet) + } else { + p.Write(control.PlayerID) + p.Copy(packet) + } +} + +func (control *PlayerControl) Read(packet *protocol.Packet, isSpawn bool) { + p := packet + if isSpawn { + p = packet.ReadHazel().GetUnderlyingPacket() + control.IsNew = p.ReadBool() + } + control.PlayerID = p.Read() +} diff --git a/src/innernetobjects/playernetworktransform.go b/src/innernetobjects/playernetworktransform.go new file mode 100644 index 0000000..1a7f43f --- /dev/null +++ b/src/innernetobjects/playernetworktransform.go @@ -0,0 +1,35 @@ +package innernetobjects + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/util" +) + +type PlayerNetworkTransform struct { + LastSequenceID uint16 + TargetPosition util.Vec2 + TargetVelocity util.Vec2 +} + +func (transform *PlayerNetworkTransform) Write(packet *protocol.Packet, isSpawn bool) { + p := protocol.NewEmptyPacket() + p.WriteUInt16(transform.LastSequenceID) + p.WriteVector2(transform.TargetPosition) + p.WriteVector2(transform.TargetVelocity) + if isSpawn { + h := protocol.NewHazelFromRawData(0, p.Dump()) + h.WriteToPacket(packet) + } else { + p.Copy(packet) + } +} + +func (transform *PlayerNetworkTransform) Read(packet *protocol.Packet, isSpawn bool) { + p := packet + if isSpawn { + p = packet.ReadHazel().GetUnderlyingPacket() + } + transform.LastSequenceID = p.ReadUInt16() + transform.TargetPosition = p.ReadVector2() + transform.TargetVelocity = p.ReadVector2() +} diff --git a/src/innernetobjects/playerphysics.go b/src/innernetobjects/playerphysics.go new file mode 100644 index 0000000..b733ae5 --- /dev/null +++ b/src/innernetobjects/playerphysics.go @@ -0,0 +1,9 @@ +package innernetobjects + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +// PlayerPhysics has no data +type PlayerPhysics struct{} + +func (control *PlayerPhysics) Write(packet *protocol.Packet, isSpawn bool) {} +func (control *PlayerPhysics) Read(packet *protocol.Packet, isSpawn bool) {} diff --git a/src/innernetobjects/polusshipstatus.go b/src/innernetobjects/polusshipstatus.go new file mode 100644 index 0000000..cc0cf47 --- /dev/null +++ b/src/innernetobjects/polusshipstatus.go @@ -0,0 +1,35 @@ +package innernetobjects + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/systemtypes" +) + +type PolusShipStatus struct { + Systems map[int]*systemtypes.SystemType + SystemsMask uint32 +} + +func NewPolusShipStatus() *MiraShipStatus { + a := &MiraShipStatus{} + a.Systems = make(map[int]*systemtypes.SystemType) + a.Systems[enum.SYSTEMTYPE_ELECTRICAL] = systemtypes.SystemTypeCast(&systemtypes.SwitchSystem{}) + a.Systems[enum.SYSTEMTYPE_MEDBAY] = systemtypes.SystemTypeCast(&systemtypes.MedScanSystem{}) + a.Systems[enum.SYSTEMTYPE_SECURITY] = systemtypes.SystemTypeCast(&systemtypes.SecurityCameraSystem{}) + a.Systems[enum.SYSTEMTYPE_COMMUNICATIONS] = systemtypes.SystemTypeCast(&systemtypes.HudOverrideSystem{}) + a.Systems[enum.SYSTEMTYPE_DOORS] = systemtypes.SystemTypeCast(&systemtypes.DoorsSystem{}) + a.Systems[enum.SYSTEMTYPE_DECONTAMINATION] = systemtypes.SystemTypeCast(&systemtypes.DeconSystem{}) + a.Systems[enum.SYSTEMTYPE_DECONTAMINATION_2] = systemtypes.SystemTypeCast(&systemtypes.DeconSystem{}) + a.Systems[enum.SYSTEMTYPE_SABOTAGE] = systemtypes.SystemTypeCast(&systemtypes.SabotageSystem{}) + a.Systems[enum.SYSTEMTYPE_LABORATORY] = systemtypes.SystemTypeCast(&systemtypes.ReactorSystem{}) + return a +} + +func (polus *PolusShipStatus) Write(packet *protocol.Packet, isSpawn bool) { + WriteShipStatus(polus.Systems, &polus.SystemsMask, packet, isSpawn) +} + +func (polus *PolusShipStatus) Read(packet *protocol.Packet, isSpawn bool) { + ReadShipStatus(polus.Systems, &polus.SystemsMask, packet, isSpawn) +} diff --git a/src/innernetobjects/shipstatus.go b/src/innernetobjects/shipstatus.go new file mode 100644 index 0000000..6553e96 --- /dev/null +++ b/src/innernetobjects/shipstatus.go @@ -0,0 +1,50 @@ +package innernetobjects + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/systemtypes" +) + +func WriteShipStatus(Systems map[int]*systemtypes.SystemType, SystemsMask *uint32, packet *protocol.Packet, isSpawn bool) { + p := protocol.NewEmptyPacket() + for i := 0; i < enum.TOTAL_SYSTEMTYPE; i++ { + if isSpawn { + if val, ok := Systems[i]; ok { + (*val).Write(p, isSpawn) + } + } else { + if (*SystemsMask & (1 << i)) != 0 { + (*Systems[i]).Write(p, isSpawn) + } + } + } + if isSpawn { + h := protocol.NewHazelFromRawData(0, p.Dump()) + h.WriteToPacket(packet) + } else { + packet.WritePackedUInt32(*SystemsMask) + p.Copy(packet) + } +} + +func ReadShipStatus(Systems map[int]*systemtypes.SystemType, SystemsMask *uint32, packet *protocol.Packet, isSpawn bool) { + p := packet + if isSpawn { + p = packet.ReadHazel().GetUnderlyingPacket() + } else { + *SystemsMask = p.ReadPackedUInt32() + } + + for i := 0; i < enum.TOTAL_SYSTEMTYPE; i++ { + if isSpawn { + if val, ok := Systems[i]; ok { + (*val).Read(p, isSpawn) + } + } else { + if (*SystemsMask & (1 << i)) != 0 { + (*Systems[i]).Read(p, isSpawn) + } + } + } +} diff --git a/src/innernetobjects/skeldshipstatus.go b/src/innernetobjects/skeldshipstatus.go new file mode 100644 index 0000000..d776a53 --- /dev/null +++ b/src/innernetobjects/skeldshipstatus.go @@ -0,0 +1,34 @@ +package innernetobjects + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/systemtypes" +) + +type SkeldShipStatus struct { + Systems map[int]*systemtypes.SystemType + SystemsMask uint32 +} + +func NewSkeldShipStatus() *SkeldShipStatus { + a := &SkeldShipStatus{} + a.Systems = make(map[int]*systemtypes.SystemType) + a.Systems[enum.SYSTEMTYPE_REACTOR] = systemtypes.SystemTypeCast(&systemtypes.ReactorSystem{}) + a.Systems[enum.SYSTEMTYPE_ELECTRICAL] = systemtypes.SystemTypeCast(&systemtypes.SwitchSystem{}) + a.Systems[enum.SYSTEMTYPE_O2] = systemtypes.SystemTypeCast(&systemtypes.LifeSuppSystem{}) + a.Systems[enum.SYSTEMTYPE_MEDBAY] = systemtypes.SystemTypeCast(&systemtypes.MedScanSystem{}) + a.Systems[enum.SYSTEMTYPE_SECURITY] = systemtypes.SystemTypeCast(&systemtypes.SecurityCameraSystem{}) + a.Systems[enum.SYSTEMTYPE_COMMUNICATIONS] = systemtypes.SystemTypeCast(&systemtypes.HudOverrideSystem{}) + a.Systems[enum.SYSTEMTYPE_DOORS] = systemtypes.SystemTypeCast(&systemtypes.AutoDoorsSystem{}) + a.Systems[enum.SYSTEMTYPE_SABOTAGE] = systemtypes.SystemTypeCast(&systemtypes.SabotageSystem{}) + return a +} + +func (skeld *SkeldShipStatus) Write(packet *protocol.Packet, isSpawn bool) { + WriteShipStatus(skeld.Systems, &skeld.SystemsMask, packet, isSpawn) +} + +func (skeld *SkeldShipStatus) Read(packet *protocol.Packet, isSpawn bool) { + ReadShipStatus(skeld.Systems, &skeld.SystemsMask, packet, isSpawn) +} diff --git a/src/innernetobjects/votebansystem.go b/src/innernetobjects/votebansystem.go new file mode 100644 index 0000000..8986ad0 --- /dev/null +++ b/src/innernetobjects/votebansystem.go @@ -0,0 +1,46 @@ +package innernetobjects + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type VoteBanSystem struct { + VotedPlayers []*VoteBanItem +} + +type VoteBanItem struct { + Player uint32 + Votes [3]uint32 +} + +func (ban *VoteBanSystem) Write(packet *protocol.Packet, isSpawn bool) { + p := protocol.NewEmptyPacket() + p.Write(byte(len(ban.VotedPlayers))) + for i := 0; i < len(ban.VotedPlayers); i++ { + p.WriteUInt32(ban.VotedPlayers[i].Player) + for j := 0; j < 3; j++ { + p.WritePackedUInt32(ban.VotedPlayers[i].Votes[j]) + } + } + if isSpawn { + h := protocol.NewHazelFromRawData(0, p.Dump()) + h.WriteToPacket(packet) + } else { + p.Copy(packet) + } +} + +func (ban *VoteBanSystem) Read(packet *protocol.Packet, isSpawn bool) { + p := packet + if isSpawn { + p = packet.ReadHazel().GetUnderlyingPacket() + } + + ban.VotedPlayers = make([]*VoteBanItem, p.Read()) + for i := 0; i < len(ban.VotedPlayers); i++ { + ban.VotedPlayers[i].Player = p.ReadUInt32() + for j := 0; j < 3; j++ { + ban.VotedPlayers[i].Votes[j] = p.ReadPackedUInt32() + } + } +} diff --git a/src/mapdata/go.mod b/src/mapdata/go.mod new file mode 100644 index 0000000..78e9809 --- /dev/null +++ b/src/mapdata/go.mod @@ -0,0 +1,10 @@ +module codehub.onpointcoding.net/sean/go-susapp/mapdata + +go 1.17 + +replace ( + codehub.onpointcoding.net/sean/go-susapp/protocol => ../protocol + codehub.onpointcoding.net/sean/go-susapp/util => ../util +) + +require codehub.onpointcoding.net/sean/go-susapp/util v1.0.0 diff --git a/src/mapdata/lobby.go b/src/mapdata/lobby.go new file mode 100644 index 0000000..2f6aaeb --- /dev/null +++ b/src/mapdata/lobby.go @@ -0,0 +1,16 @@ +package mapdata + +import "codehub.onpointcoding.net/sean/go-susapp/util" + +var LobbySpawnPositions = []util.Vec2{ + util.NewVec2(-1.6, 2.4), + util.NewVec2(-1.3, 2.5), + util.NewVec2(-1.1, 2.5), + util.NewVec2(-0.8, 2.6), + util.NewVec2(-0.6, 2.7), + util.NewVec2(0.7, 2.8), + util.NewVec2(0.9, 2.6), + util.NewVec2(1.1, 2.6), + util.NewVec2(1.4, 2.6), + util.NewVec2(1.7, 2.4), +} diff --git a/src/packets/00_hostgameC2S.go b/src/packets/00_hostgameC2S.go new file mode 100644 index 0000000..70a00da --- /dev/null +++ b/src/packets/00_hostgameC2S.go @@ -0,0 +1,24 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type HostGameC2S struct { + Options *protocol.GameOptionsData + QuickChatMode byte +} + +func (d *HostGameC2S) TypeCode() byte { + return 0x00 +} + +func (d *HostGameC2S) Write(packet *protocol.Packet) { + d.Options.Write(packet) + packet.Write(d.QuickChatMode) +} + +func (d *HostGameC2S) Read(packet *protocol.Packet) { + d.Options = protocol.NewGameOptionsDataFromPacket(packet) + d.QuickChatMode = packet.Read() +} diff --git a/src/packets/00_hostgameS2C.go b/src/packets/00_hostgameS2C.go new file mode 100644 index 0000000..a076b54 --- /dev/null +++ b/src/packets/00_hostgameS2C.go @@ -0,0 +1,19 @@ +package packets + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type HostGameS2C struct { + GameID int32 +} + +func (d *HostGameS2C) TypeCode() int { + return 0x00 +} + +func (d *HostGameS2C) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) +} + +func (d *HostGameS2C) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() +} diff --git a/src/packets/01_joingameC2S.go b/src/packets/01_joingameC2S.go new file mode 100644 index 0000000..d809bbc --- /dev/null +++ b/src/packets/01_joingameC2S.go @@ -0,0 +1,19 @@ +package packets + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type JoinGameC2S struct { + GameID int32 +} + +func (d *JoinGameC2S) TypeCode() byte { + return 0x01 +} + +func (d *JoinGameC2S) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) +} + +func (d *JoinGameC2S) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() +} diff --git a/src/packets/01_joingameS2C.go b/src/packets/01_joingameS2C.go new file mode 100644 index 0000000..1336928 --- /dev/null +++ b/src/packets/01_joingameS2C.go @@ -0,0 +1,19 @@ +package packets + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type JoinGameS2C struct { + DisconnectReason int32 +} + +func (d *JoinGameS2C) TypeCode() byte { + return 0x01 +} + +func (d *JoinGameS2C) Write(packet *protocol.Packet) { + packet.WriteInt32(d.DisconnectReason) +} + +func (d *JoinGameS2C) Read(packet *protocol.Packet) { + d.DisconnectReason = packet.ReadInt32() +} diff --git a/src/packets/01_joingameS2G.go b/src/packets/01_joingameS2G.go new file mode 100644 index 0000000..bf4b145 --- /dev/null +++ b/src/packets/01_joingameS2G.go @@ -0,0 +1,25 @@ +package packets + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type JoinGameS2G struct { + GameID int32 + JoiningClientID uint32 + HostClientID uint32 +} + +func (d *JoinGameS2G) TypeCode() byte { + return 0x01 +} + +func (d *JoinGameS2G) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.WriteUInt32(d.JoiningClientID) + packet.WriteUInt32(d.HostClientID) +} + +func (d *JoinGameS2G) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.JoiningClientID = packet.ReadUInt32() + d.HostClientID = packet.ReadUInt32() +} diff --git a/src/packets/02_startgameH2G.go b/src/packets/02_startgameH2G.go new file mode 100644 index 0000000..4096f85 --- /dev/null +++ b/src/packets/02_startgameH2G.go @@ -0,0 +1,19 @@ +package packets + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type StartGameH2G struct { + GameID int32 +} + +func (d *StartGameH2G) TypeCode() byte { + return 0x02 +} + +func (d *StartGameH2G) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) +} + +func (d *StartGameH2G) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() +} diff --git a/src/packets/03_removegameS2C.go b/src/packets/03_removegameS2C.go new file mode 100644 index 0000000..8b0c19e --- /dev/null +++ b/src/packets/03_removegameS2C.go @@ -0,0 +1,22 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type RemoveGameS2C struct { + DisconnectReason byte +} + +func (d *RemoveGameS2C) TypeCode() byte { + return 0x03 +} + +func (d *RemoveGameS2C) Write(packet *protocol.Packet) { + packet.Write(d.DisconnectReason) +} + +func (d *RemoveGameS2C) Read(packet *protocol.Packet) { + d.DisconnectReason = packet.ReadDefault(enum.DISCONNECT_SERVER_REQUEST) +} diff --git a/src/packets/04_removeplayerH2C.go b/src/packets/04_removeplayerH2C.go new file mode 100644 index 0000000..a15f0b4 --- /dev/null +++ b/src/packets/04_removeplayerH2C.go @@ -0,0 +1,28 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type RemovePlayerH2C struct { + GameID int32 + DisconnectedClientID uint32 + DisconnectReason byte +} + +func (d *RemovePlayerH2C) TypeCode() byte { + return 0x04 +} + +func (d *RemovePlayerH2C) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.WritePackedUInt32(d.DisconnectedClientID) + packet.Write(d.DisconnectReason) +} + +func (d *RemovePlayerH2C) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.DisconnectedClientID = packet.ReadPackedUInt32() + d.DisconnectReason = packet.ReadDefault(enum.DISCONNECT_SERVER_REQUEST) +} diff --git a/src/packets/04_removeplayerS2G.go b/src/packets/04_removeplayerS2G.go new file mode 100644 index 0000000..806eee7 --- /dev/null +++ b/src/packets/04_removeplayerS2G.go @@ -0,0 +1,31 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type RemovePlayerS2G struct { + GameID int32 + DisconnectedClientID uint32 + HostClientID uint32 + DisconnectReason byte +} + +func (d *RemovePlayerS2G) TypeCode() byte { + return 0x04 +} + +func (d *RemovePlayerS2G) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.WriteUInt32(d.DisconnectedClientID) + packet.WriteUInt32(d.HostClientID) + packet.Write(d.DisconnectReason) +} + +func (d *RemovePlayerS2G) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.DisconnectedClientID = packet.ReadUInt32() + d.HostClientID = packet.ReadUInt32() + d.DisconnectReason = packet.ReadDefault(enum.DISCONNECT_SERVER_REQUEST) +} diff --git a/src/packets/05_gamedataall.go b/src/packets/05_gamedataall.go new file mode 100644 index 0000000..16ec01f --- /dev/null +++ b/src/packets/05_gamedataall.go @@ -0,0 +1,36 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type GameDataAll struct { + GameID int32 + Subpackets []*protocol.Hazel +} + +func NewGameDataSingleSubpacket(GameID int32, h *GameDataSubpacket) *GameDataAll { + return &GameDataAll{ + GameID, + []*protocol.Hazel{(*h).AsHazel()}, + } +} + +func (d *GameDataAll) TypeCode() byte { return 0x05 } +func (d *GameDataAll) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + for i := 0; i < len(d.Subpackets); i++ { + d.Subpackets[i].WriteToPacket(packet) + } +} +func (d *GameDataAll) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + for packet.Remaining() > 0 { + a := protocol.NewHazel(packet) + d.Subpackets = append(d.Subpackets, a) + } +} + +type GameDataSubpacket interface { + AsHazel() *protocol.Hazel +} diff --git a/src/packets/06_gamedatato.go b/src/packets/06_gamedatato.go new file mode 100644 index 0000000..ef2509c --- /dev/null +++ b/src/packets/06_gamedatato.go @@ -0,0 +1,38 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type GameDataTo struct { + GameID int32 + TargetClientID uint32 + Subpackets []*protocol.Hazel +} + +func NewGameDataToSingleSubpacket(GameID int32, TargetClientID uint32, h *GameDataSubpacket) *GameDataTo { + return &GameDataTo{ + GameID, + TargetClientID, + []*protocol.Hazel{(*h).AsHazel()}, + } +} + +func (d *GameDataTo) TypeCode() byte { return 0x06 } + +func (d *GameDataTo) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.WritePackedUInt32(d.TargetClientID) + for i := 0; i < len(d.Subpackets); i++ { + d.Subpackets[i].WriteToPacket(packet) + } +} + +func (d *GameDataTo) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.TargetClientID = packet.ReadPackedUInt32() + for packet.Remaining() > 0 { + a := protocol.NewHazel(packet) + d.Subpackets = append(d.Subpackets, a) + } +} diff --git a/src/packets/07_joinedgameH2C.go b/src/packets/07_joinedgameH2C.go new file mode 100644 index 0000000..2c4236a --- /dev/null +++ b/src/packets/07_joinedgameH2C.go @@ -0,0 +1,37 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type JoinedGameH2C struct { + GameID int32 + JoinedClientID uint32 + HostClientID uint32 + OtherClientIDs []uint32 +} + +func (d *JoinedGameH2C) TypeCode() byte { + return 0x07 +} + +func (d *JoinedGameH2C) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.WriteUInt32(d.JoinedClientID) + packet.WriteUInt32(d.HostClientID) + packet.WritePackedUInt32(uint32(len(d.OtherClientIDs))) + for i := 0; i < len(d.OtherClientIDs); i++ { + packet.WritePackedUInt32(d.OtherClientIDs[i]) + } +} + +func (d *JoinedGameH2C) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.JoinedClientID = packet.ReadUInt32() + d.HostClientID = packet.ReadUInt32() + l := int(packet.ReadUInt32()) + d.OtherClientIDs = make([]uint32, l) + for i := 0; i < l; i++ { + d.OtherClientIDs[i] = packet.ReadUInt32() + } +} diff --git a/src/packets/08_endgameH2G.go b/src/packets/08_endgameH2G.go new file mode 100644 index 0000000..6ccb21d --- /dev/null +++ b/src/packets/08_endgameH2G.go @@ -0,0 +1,27 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type EndGameH2G struct { + GameID int32 + GameOverReason byte + ShowAd bool +} + +func (d *EndGameH2G) TypeCode() byte { + return 0x08 +} + +func (d *EndGameH2G) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.Write(d.GameOverReason) + packet.WriteBool(d.ShowAd) +} + +func (d *EndGameH2G) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.GameOverReason = packet.Read() + d.ShowAd = packet.ReadBool() +} diff --git a/src/packets/09_getgamelist.go b/src/packets/09_getgamelist.go new file mode 100644 index 0000000..669e813 --- /dev/null +++ b/src/packets/09_getgamelist.go @@ -0,0 +1,5 @@ +package packets + +// GetGameListAll is a legacy packet +type GetGameListAll struct { +} diff --git a/src/packets/10_altergame.go b/src/packets/10_altergame.go new file mode 100644 index 0000000..b1c42e8 --- /dev/null +++ b/src/packets/10_altergame.go @@ -0,0 +1,27 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type AlterGameH2G struct { + GameID int32 + TagID byte + TagValue byte +} + +func (d *AlterGameH2G) TypeCode() byte { + return 0x0a +} + +func (d *AlterGameH2G) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.Write(d.TagID) + packet.Write(d.TagValue) +} + +func (d *AlterGameH2G) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.TagID = packet.Read() + d.TagValue = packet.Read() +} diff --git a/src/packets/11_kickplayer.go b/src/packets/11_kickplayer.go new file mode 100644 index 0000000..21389d2 --- /dev/null +++ b/src/packets/11_kickplayer.go @@ -0,0 +1,30 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type KickPlayerH2G struct { + GameID int32 + KickedClientID uint32 + IsBanned bool + DisconnectReason byte +} + +func (d *KickPlayerH2G) TypeCode() byte { + return 0x0b +} + +func (d *KickPlayerH2G) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.WritePackedUInt32(d.KickedClientID) + packet.WriteBool(d.IsBanned) + packet.Write(d.DisconnectReason) +} + +func (d *KickPlayerH2G) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.KickedClientID = packet.ReadPackedUInt32() + d.IsBanned = packet.ReadBool() + d.DisconnectReason = packet.Read() +} diff --git a/src/packets/12_waitforhostC2H.go b/src/packets/12_waitforhostC2H.go new file mode 100644 index 0000000..6c50a3c --- /dev/null +++ b/src/packets/12_waitforhostC2H.go @@ -0,0 +1,24 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type WaitForHostC2H struct { + GameID int32 + RejoiningClientID uint32 +} + +func (d *WaitForHostC2H) TypeCode() byte { + return 0x0c +} + +func (d *WaitForHostC2H) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.WriteUInt32(d.RejoiningClientID) +} + +func (d *WaitForHostC2H) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.RejoiningClientID = packet.ReadUInt32() +} diff --git a/src/packets/13_redirect.go b/src/packets/13_redirect.go new file mode 100644 index 0000000..d32f5ac --- /dev/null +++ b/src/packets/13_redirect.go @@ -0,0 +1,26 @@ +package packets + +import ( + "net" + + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type RedirectS2C struct { + IPAddress net.IP + Port uint16 +} + +func (d *RedirectS2C) TypeCode() byte { + return 0x0d +} + +func (d *RedirectS2C) Write(packet *protocol.Packet) { + packet.WriteIP(d.IPAddress) + packet.WriteUInt16(d.Port) +} + +func (d *RedirectS2C) Read(packet *protocol.Packet) { + d.IPAddress = packet.ReadIP() + d.Port = packet.ReadUInt16() +} diff --git a/src/packets/14_reselectserverS2C.go b/src/packets/14_reselectserverS2C.go new file mode 100644 index 0000000..0b1e392 --- /dev/null +++ b/src/packets/14_reselectserverS2C.go @@ -0,0 +1,42 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/util" +) + +type ReselectServerS2C struct { + Version byte + MasterServers []*util.MasterServer +} + +var currentReselectServerVerion byte = 1 + +func (d *ReselectServerS2C) TypeCode() byte { + return 0x0e +} + +func (d *ReselectServerS2C) Write(packet *protocol.Packet) { + packet.Write(currentReselectServerVerion) + packet.WritePackedUInt32(uint32(len(d.MasterServers))) + for i := 0; i < len(d.MasterServers); i++ { + packet.WriteString(d.MasterServers[i].Name) + packet.WriteIP(d.MasterServers[i].IPAddress) + packet.WriteUInt16(d.MasterServers[i].Port) + packet.WritePackedUInt32(d.MasterServers[i].NumberOfConnections) + } +} + +func (d *ReselectServerS2C) Read(packet *protocol.Packet) { + d.Version = packet.Read() + l := int(packet.ReadPackedUInt32()) + d.MasterServers = make([]*util.MasterServer, l) + for i := 0; i < l; i++ { + d.MasterServers[i] = &util.MasterServer{ + Name: packet.ReadString(), + IPAddress: packet.ReadIP(), + Port: packet.ReadUInt16(), + NumberOfConnections: packet.ReadPackedUInt32(), + } + } +} diff --git a/src/packets/16_GetGameListV2C2S.go b/src/packets/16_GetGameListV2C2S.go new file mode 100644 index 0000000..341a40e --- /dev/null +++ b/src/packets/16_GetGameListV2C2S.go @@ -0,0 +1,29 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type GetGameListV2C2S struct { + Unknown02 int32 + Options *protocol.GameOptionsData + QuickChatMode byte +} + +var currentUnknown02Value int32 = 0x02 + +func (d *GetGameListV2C2S) TypeCode() byte { + return 0x10 +} + +func (d *GetGameListV2C2S) Write(packet *protocol.Packet) { + packet.WritePackedInt32(currentUnknown02Value) + d.Options.Write(packet) + packet.Write(d.QuickChatMode) +} + +func (d *GetGameListV2C2S) Read(packet *protocol.Packet) { + d.Unknown02 = packet.ReadInt32() + d.Options = protocol.NewGameOptionsDataFromPacket(packet) + d.QuickChatMode = packet.Read() +} diff --git a/src/packets/16_GetGameListV2S2C.go b/src/packets/16_GetGameListV2S2C.go new file mode 100644 index 0000000..58261cf --- /dev/null +++ b/src/packets/16_GetGameListV2S2C.go @@ -0,0 +1,30 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type GetGameListV2S2C struct { + GameCounts *protocol.Hazel // tag = 0x01 + GameList *protocol.Hazel // tag = 0x00 +} + +func (d *GetGameListV2S2C) TypeCode() byte { + return 0x10 +} + +func (d *GetGameListV2S2C) Write(packet *protocol.Packet) { + d.GameCounts.WriteToPacket(packet) + d.GameList.WriteToPacket(packet) +} + +func (d *GetGameListV2S2C) Read(packet *protocol.Packet) { + for packet.Remaining() > 0 { + h := packet.ReadHazel() + if h.GetTag() == 0x01 { + d.GameCounts = h + } else if h.GetTag() == 0x00 { + d.GameList = h + } + } +} diff --git a/src/packets/17_reportplayerC2S.go b/src/packets/17_reportplayerC2S.go new file mode 100644 index 0000000..455a845 --- /dev/null +++ b/src/packets/17_reportplayerC2S.go @@ -0,0 +1,27 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type ReportPlayerC2S struct { + GameID int32 + ReportedClientID uint32 + ReportReason byte +} + +func (d *ReportPlayerC2S) TypeCode() byte { + return 0x11 +} + +func (d *ReportPlayerC2S) Write(packet *protocol.Packet) { + packet.WriteInt32(d.GameID) + packet.WritePackedUInt32(d.ReportedClientID) + packet.Write(d.ReportReason) +} + +func (d *ReportPlayerC2S) Read(packet *protocol.Packet) { + d.GameID = packet.ReadInt32() + d.ReportedClientID = packet.ReadPackedUInt32() + d.ReportReason = packet.Read() +} diff --git a/src/packets/17_reportplayerS2C.go b/src/packets/17_reportplayerS2C.go new file mode 100644 index 0000000..cbdc804 --- /dev/null +++ b/src/packets/17_reportplayerS2C.go @@ -0,0 +1,30 @@ +package packets + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type ReportPlayerS2C struct { + ReportedClientID uint32 + ReportReason uint32 + ReportOutcome byte + ReportedPlayerName string +} + +func (d *ReportPlayerS2C) TypeCode() byte { + return 0x11 +} + +func (d *ReportPlayerS2C) Write(packet *protocol.Packet) { + packet.WritePackedUInt32(d.ReportedClientID) + packet.WriteUInt32(d.ReportReason) + packet.Write(d.ReportOutcome) + packet.WriteString(d.ReportedPlayerName) +} + +func (d *ReportPlayerS2C) Read(packet *protocol.Packet) { + d.ReportedClientID = packet.ReadPackedUInt32() + d.ReportReason = packet.ReadUInt32() + d.ReportOutcome = packet.Read() + d.ReportedPlayerName = packet.ReadString() +} diff --git a/src/packets/gamepacket.go b/src/packets/gamepacket.go new file mode 100644 index 0000000..edd7f9b --- /dev/null +++ b/src/packets/gamepacket.go @@ -0,0 +1,16 @@ +package packets + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +// PacketBase PacketType + +// C2S = client to server +// S2C = server to client +// S2G = server to game (broadcast to all clients from the server) +// H2G = host to game (broadcast to all clients from the host) + +type GamePacket interface { + Write(packet *protocol.Packet) + Read(packet *protocol.Packet) + TypeCode() byte +} diff --git a/src/packets/go.mod b/src/packets/go.mod new file mode 100644 index 0000000..15600b2 --- /dev/null +++ b/src/packets/go.mod @@ -0,0 +1,15 @@ +module codehub.onpointcoding.net/sean/go-susapp/packets + +go 1.17 + +replace ( + codehub.onpointcoding.net/sean/go-susapp/enum => ../enum + codehub.onpointcoding.net/sean/go-susapp/protocol => ../protocol + codehub.onpointcoding.net/sean/go-susapp/util => ../util +) + +require ( + codehub.onpointcoding.net/sean/go-susapp/enum v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/protocol v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/util v1.0.0 +) diff --git a/src/protocol/gamelist.go b/src/protocol/gamelist.go new file mode 100644 index 0000000..864f453 --- /dev/null +++ b/src/protocol/gamelist.go @@ -0,0 +1,60 @@ +package protocol + +import ( + "net" +) + +type GameList struct { + Games []GameListItem +} + +func NewGameList(packet *Packet) *GameList { + games := make([]GameListItem, 0) + for packet.Remaining() > 0 { + games = append(games, *NewGameListItem(packet)) + } + return &GameList{games} +} + +func (list *GameList) WriteToPacket(packet *Packet) { + for i := 0; i < len(list.Games); i++ { + list.Games[i].WriteToPacket(packet) + } +} + +type GameListItem struct { + IPAddress net.IP + Port uint16 + GameID int32 + HostPlayer string + NumberOfPlayers byte + Age uint32 + MapID byte + NumberOfImpostors byte + MaxNumberOfPlayers byte +} + +func NewGameListItem(packet *Packet) *GameListItem { + ip := packet.ReadIP() + port := packet.ReadUInt16() + gameId := packet.ReadInt32() + hostPlayer := packet.ReadString() + numberOfPlayers := packet.Read() + age := packet.ReadPackedUInt32() + mapId := packet.Read() + numberOfImpostors := packet.Read() + maxNumberOfPlayers := packet.Read() + return &GameListItem{ip, port, gameId, hostPlayer, numberOfPlayers, age, mapId, numberOfImpostors, maxNumberOfPlayers} +} + +func (item *GameListItem) WriteToPacket(packet *Packet) { + packet.WriteIP(item.IPAddress) + packet.WriteUInt16(item.Port) + packet.WriteInt32(item.GameID) + packet.WriteString(item.HostPlayer) + packet.Write(item.NumberOfPlayers) + packet.WritePackedUInt32(item.Age) + packet.Write(item.MapID) + packet.Write(item.NumberOfImpostors) + packet.Write(item.MaxNumberOfPlayers) +} diff --git a/src/protocol/gameoptionsdata.go b/src/protocol/gameoptionsdata.go new file mode 100644 index 0000000..7e4675f --- /dev/null +++ b/src/protocol/gameoptionsdata.go @@ -0,0 +1,96 @@ +package protocol + +type GameOptionsData struct { + Version byte + MaxNumberOfPlayers byte + Keywords uint32 + Maps byte + PlayerSpeedModifier float32 + CrewmateLightModifier float32 + ImpostorLightModifier float32 + KillCooldown float32 + NumberOfCommonTasks byte + NumberOfLongTasks byte + NumberOfShortTasks byte + NumberOfEmergencyMeetings uint32 + NumberOfImpostors byte + KillDistance byte + DiscussionTime uint32 + VotingTime uint32 + IsDefaults bool + EmergencyCooldown byte + ConfirmEjects bool + VisualTasks bool + AnonymousVotes bool + TaskBarUpdates byte +} + +var currentGameOptionsVersion byte = 4 + +// Create new default options +func NewDefaultGameOptionsData() *GameOptionsData { + return &GameOptionsData{currentGameOptionsVersion, 10, 1, 1, 1, 1, 1.5, 45, 1, 1, 2, 1, 1, 1, 15, 120, true, 15, true, true, false, 0} +} + +// Create new options from packet data +func NewGameOptionsDataFromPacket(packet *Packet) *GameOptionsData { + a := NewDefaultGameOptionsData() + a.Read(packet) + return a +} + +func (data *GameOptionsData) Write(packet *Packet) { + p := NewEmptyPacket() + p.Write(data.Version) + p.Write(data.MaxNumberOfPlayers) + p.WriteUInt32(data.Keywords) + p.Write(data.Maps) + p.WriteFloat(data.PlayerSpeedModifier) + p.WriteFloat(data.CrewmateLightModifier) + p.WriteFloat(data.ImpostorLightModifier) + p.WriteFloat(data.KillCooldown) + p.Write(data.NumberOfCommonTasks) + p.Write(data.NumberOfLongTasks) + p.Write(data.NumberOfShortTasks) + p.WritePackedUInt32(data.NumberOfEmergencyMeetings) + p.Write(data.NumberOfImpostors) + p.Write(data.KillDistance) + p.WriteUInt32(data.DiscussionTime) + p.WriteUInt32(data.VotingTime) + p.WriteBool(data.IsDefaults) + p.Write(data.EmergencyCooldown) + p.WriteBool(data.ConfirmEjects) + p.WriteBool(data.VisualTasks) + p.WriteBool(data.AnonymousVotes) + p.Write(data.TaskBarUpdates) + packet.WritePackedUInt32(uint32(p.Size())) + p.Copy(packet) +} + +func (data *GameOptionsData) Read(packet *Packet) { + l := packet.ReadPackedUInt32() + p := NewPacket(packet.ReadBytes(int(l))) + + data.Version = p.Read() + data.MaxNumberOfPlayers = p.Read() + data.Keywords = p.ReadUInt32() + data.Maps = p.Read() + data.PlayerSpeedModifier = p.ReadFloat() + data.CrewmateLightModifier = p.ReadFloat() + data.ImpostorLightModifier = p.ReadFloat() + data.KillCooldown = p.ReadFloat() + data.NumberOfCommonTasks = p.Read() + data.NumberOfLongTasks = p.Read() + data.NumberOfShortTasks = p.Read() + data.NumberOfEmergencyMeetings = p.ReadPackedUInt32() + data.NumberOfImpostors = p.Read() + data.KillDistance = p.Read() + data.DiscussionTime = p.ReadUInt32() + data.VotingTime = p.ReadUInt32() + data.IsDefaults = p.ReadBool() + data.EmergencyCooldown = p.ReadDefault(15) + data.ConfirmEjects = p.ReadBoolDefault(true) + data.VisualTasks = p.ReadBoolDefault(true) + data.AnonymousVotes = p.ReadBoolDefault(false) + data.TaskBarUpdates = p.ReadDefault(0) +} diff --git a/src/protocol/go.mod b/src/protocol/go.mod new file mode 100644 index 0000000..00c75a3 --- /dev/null +++ b/src/protocol/go.mod @@ -0,0 +1,6 @@ +module codehub.onpointcoding.net/sean/go-susapp/protocol + +go 1.17 + +replace codehub.onpointcoding.net/sean/go-susapp/util => ../util +require codehub.onpointcoding.net/sean/go-susapp/util v1.0.0 diff --git a/src/protocol/hazelmessage.go b/src/protocol/hazelmessage.go new file mode 100644 index 0000000..7fd191a --- /dev/null +++ b/src/protocol/hazelmessage.go @@ -0,0 +1,59 @@ +package protocol + +type Hazel struct { + p *Packet + l uint16 + t byte + payload *HazelPayload + readPacket bool +} + +func NewHazel(packet *Packet) *Hazel { + l := packet.ReadUInt16() + t := packet.Read() + p := NewPacket(packet.ReadBytes(int(l))) + return &Hazel{p, l, t, nil, true} +} + +func NewHazelFromPayload(payload *HazelPayload) *Hazel { + p := NewEmptyPacket() + return &Hazel{p, 0, (*payload).TypeCode(), payload, false} +} + +func NewHazelFromRawData(tag byte, b []byte) *Hazel { + p := NewEmptyPacket() + p.WriteBytes(b) + return &Hazel{p, 0, tag, nil, true} +} + +func (hazel *Hazel) WriteToPacket(packet *Packet) { + if hazel.readPacket { + packet.WriteUInt16(hazel.l) + packet.Write(hazel.t) + hazel.p.Copy(packet) + } else { + p := NewEmptyPacket() + (*hazel.payload).Write(p) + packet.WriteUInt16(uint16(p.Size())) + packet.Write((*hazel.payload).TypeCode()) + p.Copy(packet) + } +} + +func (hazel *Hazel) GetUnderlyingPacket() *Packet { + return hazel.p +} + +func (hazel *Hazel) Length() uint16 { + return hazel.l +} + +func (hazel *Hazel) GetTag() byte { + return hazel.t +} + +type HazelPayload interface { + Write(packet *Packet) + Read(packet *Packet) + TypeCode() byte +} diff --git a/src/protocol/netcomponent.go b/src/protocol/netcomponent.go new file mode 100644 index 0000000..9de5ef4 --- /dev/null +++ b/src/protocol/netcomponent.go @@ -0,0 +1,29 @@ +package protocol + +type SpawnComponent struct { + NetID uint32 + ComponentData *Hazel +} + +func (d *SpawnComponent) Write(packet *Packet) { + packet.WritePackedUInt32(d.NetID) + d.ComponentData.WriteToPacket(packet) +} +func (d *SpawnComponent) Read(packet *Packet) { + d.NetID = packet.ReadPackedUInt32() + d.ComponentData = NewHazel(packet) +} + +type UpdateComponent struct { + NetID uint32 + RawData *Packet +} + +func (d *UpdateComponent) Write(packet *Packet) { + packet.WritePackedUInt32(d.NetID) + d.RawData.Copy(packet) +} +func (d *UpdateComponent) Read(packet *Packet) { + d.NetID = packet.ReadPackedUInt32() + d.RawData = packet +} diff --git a/src/protocol/packet.go b/src/protocol/packet.go new file mode 100644 index 0000000..b43ecd9 --- /dev/null +++ b/src/protocol/packet.go @@ -0,0 +1,241 @@ +package protocol + +import ( + "encoding/binary" + "math" + "net" + + "codehub.onpointcoding.net/sean/go-susapp/util" +) + +type Packet struct { + data []byte + idx int + l int +} + +func NewPacket(d []byte) *Packet { + return &Packet{ + data: d, + l: len(d), + } +} + +func NewEmptyPacket() *Packet { + return &Packet{ + data: []byte{}, + l: 0, + } +} + +func (packet *Packet) Seek(v int) { + packet.idx = v +} + +func (packet *Packet) Size() int { + return len(packet.data) +} + +func (packet *Packet) Dump() []byte { + return packet.data +} + +func (packet *Packet) Remaining() int { + return packet.Size() - packet.idx +} + +func (packet *Packet) Copy(p *Packet) { + p.WriteBytes(packet.data) +} + +func (packet *Packet) Read() byte { + return packet.ReadDefault(0) +} + +func (packet *Packet) ReadDefault(d byte) byte { + if packet.idx >= len(packet.data) { + return d + } + v := packet.data[packet.idx] + packet.idx++ + return v +} + +func (packet *Packet) Write(v byte) { + packet.data = append(packet.data, v) +} + +func (packet *Packet) ReadBytes(n int) []byte { + b := make([]byte, n) + for i := 0; i < n; i++ { + b[i] = packet.Read() + } + return b +} + +func (packet *Packet) WriteBytes(v []byte) { + packet.data = append(packet.data, v...) +} + +func (packet *Packet) ReadInt16() int16 { + return int16(packet.ReadUInt16()) +} + +func (packet *Packet) WriteInt16(v int16) { + packet.WriteUInt16(uint16(v)) +} + +func (packet *Packet) ReadUInt16() uint16 { + return binary.LittleEndian.Uint16(packet.ReadBytes(2)) +} + +func (packet *Packet) WriteUInt16(v uint16) { + var b [2]byte + binary.LittleEndian.PutUint16(b[:], v) + packet.WriteBytes(b[:]) +} + +func (packet *Packet) ReadUInt16BigEndian() uint16 { + return binary.BigEndian.Uint16(packet.ReadBytes(2)) +} + +func (packet *Packet) WriteUInt16BigEndian(v uint16) { + var b [2]byte + binary.BigEndian.PutUint16(b[:], v) + packet.WriteBytes(b[:]) +} + +func (packet *Packet) ReadInt32() int32 { + return int32(packet.ReadUInt32()) +} + +func (packet *Packet) WriteInt32(v int32) { + packet.WriteUInt32(uint32(v)) +} + +func (packet *Packet) ReadUInt32() uint32 { + return binary.LittleEndian.Uint32(packet.ReadBytes(4)) +} + +func (packet *Packet) WriteUInt32(v uint32) { + var b [4]byte + binary.LittleEndian.PutUint32(b[:], v) + packet.WriteBytes(b[:]) +} + +func (packet *Packet) ReadFloat() float32 { + return math.Float32frombits(packet.ReadUInt32()) +} + +func (packet *Packet) WriteFloat(v float32) { + var b [4]byte + binary.LittleEndian.PutUint32(b[:], math.Float32bits(v)) + packet.WriteBytes(b[:]) +} + +func (packet *Packet) ReadBool() bool { + return packet.Read() == 1 +} + +func (packet *Packet) ReadBoolDefault(v bool) bool { + if v { + return packet.ReadDefault(1) == 1 + } else { + return packet.ReadDefault(0) == 1 + } +} + +func (packet *Packet) WriteBool(v bool) { + if v { + packet.Write(1) + } else { + packet.Write(0) + } +} + +func (packet *Packet) ParsePackedInt32() int32 { + return int32(packet.ReadPackedUInt32()) +} + +func (packet *Packet) WritePackedInt32(v int32) { + packet.WritePackedUInt32(uint32(v)) +} + +func (packet *Packet) ReadPackedUInt32() uint32 { + readMore := true + shift := 0 + var output uint32 = 0 + + for readMore { + b := packet.Read() + if b > 0x80 { + readMore = true + b ^= 0x80 + } else { + readMore = false + } + + output |= uint32(b) << shift + shift += 7 + } + + return output +} + +func (packet *Packet) WritePackedUInt32(v uint32) { + writeMore := true + for writeMore { + b := byte(v & 0xff) + if v >= 0x80 { + b |= 0x80 + } + packet.Write(b) + v >>= 7 + writeMore = v > 0 + } +} + +func (packet *Packet) ReadString() string { + l := int(packet.ReadPackedUInt32()) + return string(packet.ReadBytes(l)) +} + +func (packet *Packet) WriteString(v string) { + l := len(v) + packet.WritePackedUInt32(uint32(l)) + packet.WriteBytes([]byte(v)) +} + +func (packet *Packet) ReadHazel() *Hazel { + return NewHazel(packet) +} + +func (packet *Packet) WriteHazel(hazel *Hazel) { + hazel.WriteToPacket(packet) +} + +func (packet *Packet) ReadIP() net.IP { + b := packet.ReadBytes(4) + return net.IPv4(b[0], b[1], b[2], b[3]) +} + +func (packet *Packet) WriteIP(v net.IP) { + packet.Write(v[0]) + packet.Write(v[1]) + packet.Write(v[2]) + packet.Write(v[3]) +} + +func (packet *Packet) ReadVector2() util.Vec2 { + x := float32(packet.ReadUInt16()) / 65535 + y := float32(packet.ReadUInt16()) / 65535 + + return util.NewVec2(util.Lerp(-50, 50, x), util.Lerp(-50, 50, y)) +} + +func (packet *Packet) WriteVector2(v util.Vec2) { + x := uint16(util.LerpReverse(-50, 50, v.X) * 65535) + y := uint16(util.LerpReverse(-50, 50, v.Y) * 65535) + packet.WriteUInt16(x) + packet.WriteUInt16(y) +} diff --git a/src/protocol/packethandler.go b/src/protocol/packethandler.go new file mode 100644 index 0000000..756f8a1 --- /dev/null +++ b/src/protocol/packethandler.go @@ -0,0 +1,161 @@ +package protocol + +import ( + "fmt" + "net" + "sync" +) + +const ( + PacketNormal = 0x0 + PacketReliable = 0x1 + PacketHello = 0x8 + PacketDisconnect = 0x9 + PacketAcknowledgement = 0xa + PacketFragment = 0xb + PacketPing = 0xc +) + +type PacketHandler struct { + conn *net.UDPConn + addr *net.UDPAddr + HandleNormalPacket func(net *PacketHandler, h []*Hazel) + HandleReliablePacket func(net *PacketHandler, id uint16, h []*Hazel) + HandleForceDisconnectPacket func(net *PacketHandler, h *Hazel) + HandleNormalDisconnectPacket func(net *PacketHandler) + HandleAcknowledgementPacket func(net *PacketHandler, nonce uint16, missing byte) + HandlePingPacket func(net *PacketHandler, id uint16) + currentNonce uint16 + nonceMutex sync.Mutex + currentSequence uint16 + sequenceMutex sync.Mutex +} + +func NewPacketHandler(conn *net.UDPConn, addr *net.UDPAddr) *PacketHandler { + return &PacketHandler{conn: conn, addr: addr, currentNonce: 0} +} + +func (ph *PacketHandler) GetNonce() uint16 { + ph.nonceMutex.Lock() + a := ph.currentNonce + ph.currentNonce++ + ph.nonceMutex.Unlock() + return a +} + +func (ph *PacketHandler) GetSequence() uint16 { + ph.sequenceMutex.Lock() + a := ph.currentSequence + ph.currentSequence++ + ph.sequenceMutex.Unlock() + return a +} + +func (ph *PacketHandler) ReadPacket(data []byte) { + p := NewPacket(data) + b := p.Read() + if b == PacketNormal { + var h []*Hazel + for p.Remaining() > 0 { + h = append(h, p.ReadHazel()) + } + ph.HandleNormalPacket(ph, h) + } else if b == PacketReliable { + id := p.ReadUInt16BigEndian() + var h []*Hazel + for p.Remaining() > 0 { + h = append(h, p.ReadHazel()) + } + if len(h) > 0 { + ph.HandleReliablePacket(ph, id, h) + } + } else if b == PacketHello { + // Client doesn't need to read this + } else if b == PacketDisconnect { + isForced := p.ReadBool() + if isForced { + ph.HandleForceDisconnectPacket(ph, p.ReadHazel()) + } else { + ph.HandleNormalDisconnectPacket(ph) + } + } else if b == PacketAcknowledgement { + nonce := p.ReadUInt16BigEndian() + missing := p.Read() + ph.HandleAcknowledgementPacket(ph, nonce, missing) + } else if b == PacketFragment { + // Currently not used + } else if b == PacketPing { + id := p.ReadUInt16BigEndian() + ph.HandlePingPacket(ph, id) + } +} + +func (ph *PacketHandler) NewPacketFromType(packetType byte) *Packet { + return NewPacket([]byte{packetType}) +} + +func (ph *PacketHandler) SendPacket(packet *Packet) { + _, err := ph.conn.Write(packet.data) + if err != nil { + fmt.Printf("Failed to send packet to server %v\n", err) + } +} + +func (ph *PacketHandler) SendNormalPacket(h []*Hazel) { + p := ph.NewPacketFromType(0x00) + for i := 0; i < len(h); i++ { + p.WriteHazel(h[i]) + } + ph.SendPacket(p) +} + +func (ph *PacketHandler) SendReliablePacket(nonce uint16, h []*Hazel) { + p := ph.NewPacketFromType(0x01) + p.WriteUInt16BigEndian(nonce) + for i := 0; i < len(h); i++ { + p.WriteHazel(h[i]) + } + ph.SendPacket(p) +} + +func (ph *PacketHandler) SendHelloPacket(nonce uint16, hazelVersion byte, clientVersion int32, username string, authNonce uint32, keyword uint32, quickChatMode byte) { + p := ph.NewPacketFromType(0x08) + p.WriteUInt16BigEndian(nonce) + p.Write(hazelVersion) + p.WriteInt32(clientVersion) + p.WriteString(username) + p.WriteUInt32(authNonce) + p.WriteUInt32(keyword) + p.Write(quickChatMode) + ph.SendPacket(p) +} + +func (ph *PacketHandler) SendForceDisconnectPacket(h *Hazel) { + p := ph.NewPacketFromType(0x09) + p.Write(0x01) + p.WriteHazel(h) + ph.SendPacket(p) +} + +func (ph *PacketHandler) SendNormalDisconnectPacket() { + p := ph.NewPacketFromType(0x09) + p.Write(0x00) + ph.SendPacket(p) +} + +func (ph *PacketHandler) SendAcknowledgementPacket(nonce uint16, missing byte) { + p := ph.NewPacketFromType(0x0a) + p.WriteUInt16BigEndian(nonce) + p.Write(missing) + ph.SendPacket(p) +} + +func (ph *PacketHandler) SendPingPacket(id uint16) { + p := ph.NewPacketFromType(0x0c) + p.WriteUInt16(id) + ph.conn.WriteToUDP(p.data, ph.addr) +} + +func (ph *PacketHandler) Close() { + ph.conn.Close() +} diff --git a/src/protocol/playerdata.go b/src/protocol/playerdata.go new file mode 100644 index 0000000..d59c28a --- /dev/null +++ b/src/protocol/playerdata.go @@ -0,0 +1,53 @@ +package protocol + +type PlayerData struct { + PlayerID byte + Name string + ColorID uint32 + HatID uint32 + PetID uint32 + SkinID uint32 + Flags byte + Tasks []*TaskInfo +} + +func (player *PlayerData) Write(packet *Packet) { + packet.Write(player.PlayerID) + packet.WriteString(player.Name) + packet.WritePackedUInt32(player.ColorID) + packet.WritePackedUInt32(player.HatID) + packet.WritePackedUInt32(player.PetID) + packet.WritePackedUInt32(player.SkinID) + packet.Write(player.Flags) + packet.Write(byte(len(player.Tasks))) + for i := 0; i < len(player.Tasks); i++ { + player.Tasks[i].Write(packet) + } +} +func (player *PlayerData) Read(packet *Packet) { + player.PlayerID = packet.Read() + player.Name = packet.ReadString() + player.ColorID = packet.ReadPackedUInt32() + player.HatID = packet.ReadPackedUInt32() + player.PetID = packet.ReadPackedUInt32() + player.SkinID = packet.ReadPackedUInt32() + player.Flags = packet.Read() + player.Tasks = make([]*TaskInfo, packet.Read()) + for i := 0; i < len(player.Tasks); i++ { + player.Tasks[i].Read(packet) + } +} + +type TaskInfo struct { + TaskID uint32 + IsCompleted bool +} + +func (task *TaskInfo) Write(packet *Packet) { + packet.WritePackedUInt32(task.TaskID) + packet.WriteBool(task.IsCompleted) +} +func (task *TaskInfo) Read(packet *Packet) { + task.TaskID = packet.ReadPackedUInt32() + task.IsCompleted = packet.ReadBool() +} diff --git a/src/states/go.mod b/src/states/go.mod new file mode 100644 index 0000000..f9f5d58 --- /dev/null +++ b/src/states/go.mod @@ -0,0 +1,3 @@ +module codehub.onpointcoding.net/sean/go-susapp/states + +go 1.17 diff --git a/src/states/skeld.go b/src/states/skeld.go new file mode 100644 index 0000000..78239e4 --- /dev/null +++ b/src/states/skeld.go @@ -0,0 +1,5 @@ +package states + +type SkeldState struct { + Doors [13]bool +} diff --git a/src/systemtypes/autodoors.go b/src/systemtypes/autodoors.go new file mode 100644 index 0000000..96d5bee --- /dev/null +++ b/src/systemtypes/autodoors.go @@ -0,0 +1,42 @@ +package systemtypes + +import ( + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/util" +) + +type AutoDoorsSystem struct { + DoorsMask uint32 + States []bool +} + +func (system *AutoDoorsSystem) Write(packet *protocol.Packet, isSpawn bool) { + if isSpawn { + if len(system.States) == 13 { + for i := 0; i < 13; i++ { + packet.WriteBool(system.States[i]) + } + } else { + return + } + } else { + packet.WritePackedUInt32(uint32(system.DoorsMask)) + for i := 0; i < 13; i++ { + if (system.DoorsMask & (1 << i)) != 0 { + packet.WriteBool(system.States[i]) + } + } + } +} + +func (system *AutoDoorsSystem) Read(packet *protocol.Packet, isSpawn bool) { + if !isSpawn { + system.DoorsMask = packet.ReadPackedUInt32() + } + + l := util.CountSetBits(system.DoorsMask) + system.States = make([]bool, l) + for i := 0; i < l; i++ { + system.States[i] = packet.ReadBool() + } +} diff --git a/src/systemtypes/decon.go b/src/systemtypes/decon.go new file mode 100644 index 0000000..d923937 --- /dev/null +++ b/src/systemtypes/decon.go @@ -0,0 +1,28 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type DeconSystem struct { + Timer byte + State byte +} + +func (system *DeconSystem) Write(packet *protocol.Packet, isSpawn bool) { + if !isSpawn { + packet.Write(system.Timer) + packet.Write(system.State) + } +} + +func (system *DeconSystem) Read(packet *protocol.Packet, isSpawn bool) { + if !isSpawn { + system.Timer = packet.Read() + system.State = packet.Read() + } +} + +func (system *DeconSystem) IsIdle() bool { return system.State == 0 } +func (system *DeconSystem) IsEnter() bool { return system.State&1 != 0 } +func (system *DeconSystem) IsClosed() bool { return system.State&2 != 0 } +func (system *DeconSystem) IsExit() bool { return system.State&4 != 0 } +func (system *DeconSystem) IsHeadingUp() bool { return system.State&8 != 0 } diff --git a/src/systemtypes/doors.go b/src/systemtypes/doors.go new file mode 100644 index 0000000..310c635 --- /dev/null +++ b/src/systemtypes/doors.go @@ -0,0 +1,44 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type DoorsSystem struct { + DoorCooldowns []*DoorCooldown + Doors [12]bool +} + +func (system *DoorsSystem) Write(packet *protocol.Packet, isSpawn bool) { + packet.WritePackedUInt32(uint32(len(system.DoorCooldowns))) + for i := 0; i < len(system.DoorCooldowns); i++ { + system.DoorCooldowns[i].Write(packet) + } + for i := 0; i < 12; i++ { + packet.WriteBool(system.Doors[i]) + } +} + +func (system *DoorsSystem) Read(packet *protocol.Packet, isSpawn bool) { + system.DoorCooldowns = make([]*DoorCooldown, packet.ReadPackedUInt32()) + for i := 0; i < len(system.DoorCooldowns); i++ { + system.DoorCooldowns[i] = &DoorCooldown{} + system.DoorCooldowns[i].Read(packet) + } + for i := 0; i < 12; i++ { + system.Doors[i] = packet.ReadBool() + } +} + +type DoorCooldown struct { + DoorID byte + Cooldown float32 +} + +func (cooldown *DoorCooldown) Write(packet *protocol.Packet) { + packet.Write(cooldown.DoorID) + packet.WriteFloat(cooldown.Cooldown) +} + +func (cooldown *DoorCooldown) Read(packet *protocol.Packet) { + cooldown.DoorID = packet.Read() + cooldown.Cooldown = packet.ReadFloat() +} diff --git a/src/systemtypes/go.mod b/src/systemtypes/go.mod new file mode 100644 index 0000000..4289442 --- /dev/null +++ b/src/systemtypes/go.mod @@ -0,0 +1,13 @@ +module codehub.onpointcoding.net/sean/go-susapp/systemtypes + +go 1.17 + +replace ( + codehub.onpointcoding.net/sean/go-susapp/protocol => ../protocol + codehub.onpointcoding.net/sean/go-susapp/util => ../util +) + +require ( + codehub.onpointcoding.net/sean/go-susapp/protocol v1.0.0 + codehub.onpointcoding.net/sean/go-susapp/util v1.0.0 // indirect +) diff --git a/src/systemtypes/hqhud.go b/src/systemtypes/hqhud.go new file mode 100644 index 0000000..be228d2 --- /dev/null +++ b/src/systemtypes/hqhud.go @@ -0,0 +1,31 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type HqHudSystem struct { + ActiveConsoles []*UserConsolePair + CompletedConsoles []byte +} + +func (system *HqHudSystem) Write(packet *protocol.Packet, isSpawn bool) { + packet.WritePackedUInt32(uint32(len(system.ActiveConsoles))) + for i := 0; i < len(system.ActiveConsoles); i++ { + system.ActiveConsoles[i].Write(packet) + } + packet.WritePackedUInt32(uint32(len(system.CompletedConsoles))) + for i := 0; i < len(system.CompletedConsoles); i++ { + packet.Write(system.CompletedConsoles[i]) + } +} + +func (system *HqHudSystem) Read(packet *protocol.Packet, isSpawn bool) { + system.ActiveConsoles = make([]*UserConsolePair, packet.ReadPackedUInt32()) + for i := 0; i < len(system.ActiveConsoles); i++ { + system.ActiveConsoles[i] = &UserConsolePair{} + system.ActiveConsoles[i].Read(packet) + } + system.CompletedConsoles = make([]byte, packet.ReadPackedUInt32()) + for i := 0; i < len(system.CompletedConsoles); i++ { + system.CompletedConsoles[i] = packet.Read() + } +} diff --git a/src/systemtypes/hudoverride.go b/src/systemtypes/hudoverride.go new file mode 100644 index 0000000..f284eaa --- /dev/null +++ b/src/systemtypes/hudoverride.go @@ -0,0 +1,15 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type HudOverrideSystem struct { + IsSabotaged bool +} + +func (system *HudOverrideSystem) Write(packet *protocol.Packet, isSpawn bool) { + packet.WriteBool(system.IsSabotaged) +} + +func (system *HudOverrideSystem) Read(packet *protocol.Packet, isSpawn bool) { + system.IsSabotaged = packet.ReadBool() +} diff --git a/src/systemtypes/lifesupp.go b/src/systemtypes/lifesupp.go new file mode 100644 index 0000000..244e61f --- /dev/null +++ b/src/systemtypes/lifesupp.go @@ -0,0 +1,24 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type LifeSuppSystem struct { + Timer float32 + Consoles []uint32 +} + +func (system *LifeSuppSystem) Write(packet *protocol.Packet, isSpawn bool) { + packet.WriteFloat(system.Timer) + packet.WritePackedUInt32(uint32(len(system.Consoles))) + for i := 0; i < len(system.Consoles); i++ { + packet.WritePackedUInt32(system.Consoles[i]) + } +} + +func (system *LifeSuppSystem) Read(packet *protocol.Packet, isSpawn bool) { + system.Timer = packet.ReadFloat() + system.Consoles = make([]uint32, packet.ReadPackedUInt32()) + for i := 0; i < len(system.Consoles); i++ { + system.Consoles[i] = packet.ReadPackedUInt32() + } +} diff --git a/src/systemtypes/medscan.go b/src/systemtypes/medscan.go new file mode 100644 index 0000000..8b44834 --- /dev/null +++ b/src/systemtypes/medscan.go @@ -0,0 +1,21 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type MedScanSystem struct { + Players []byte +} + +func (system *MedScanSystem) Write(packet *protocol.Packet, isSpawn bool) { + packet.WritePackedUInt32(uint32(len(system.Players))) + for i := 0; i < len(system.Players); i++ { + packet.Write(system.Players[i]) + } +} + +func (system *MedScanSystem) Read(packet *protocol.Packet, isSpawn bool) { + system.Players = make([]byte, packet.ReadPackedUInt32()) + for i := 0; i < len(system.Players); i++ { + system.Players[i] = packet.Read() + } +} diff --git a/src/systemtypes/reactor.go b/src/systemtypes/reactor.go new file mode 100644 index 0000000..453fe32 --- /dev/null +++ b/src/systemtypes/reactor.go @@ -0,0 +1,25 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type ReactorSystem struct { + Timer float32 + Players []*UserConsolePair +} + +func (system *ReactorSystem) Write(packet *protocol.Packet, isSpawn bool) { + packet.WriteFloat(system.Timer) + packet.WritePackedUInt32(uint32(len(system.Players))) + for i := 0; i < len(system.Players); i++ { + system.Players[i].Write(packet) + } +} + +func (system *ReactorSystem) Read(packet *protocol.Packet, isSpawn bool) { + system.Timer = packet.ReadFloat() + system.Players = make([]*UserConsolePair, packet.ReadPackedUInt32()) + for i := 0; i < len(system.Players); i++ { + system.Players[i] = &UserConsolePair{} + system.Players[i].Read(packet) + } +} diff --git a/src/systemtypes/sabotage.go b/src/systemtypes/sabotage.go new file mode 100644 index 0000000..52ba3a1 --- /dev/null +++ b/src/systemtypes/sabotage.go @@ -0,0 +1,15 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type SabotageSystem struct { + Cooldown float32 +} + +func (system *SabotageSystem) Write(packet *protocol.Packet, isSpawn bool) { + packet.WriteFloat(system.Cooldown) +} + +func (system *SabotageSystem) Read(packet *protocol.Packet, isSpawn bool) { + system.Cooldown = packet.ReadFloat() +} diff --git a/src/systemtypes/securitycamera.go b/src/systemtypes/securitycamera.go new file mode 100644 index 0000000..c607415 --- /dev/null +++ b/src/systemtypes/securitycamera.go @@ -0,0 +1,21 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type SecurityCameraSystem struct { + Players []byte +} + +func (system *SecurityCameraSystem) Write(packet *protocol.Packet, isSpawn bool) { + packet.WritePackedUInt32(uint32(len(system.Players))) + for i := 0; i < len(system.Players); i++ { + packet.Write(system.Players[i]) + } +} + +func (system *SecurityCameraSystem) Read(packet *protocol.Packet, isSpawn bool) { + system.Players = make([]byte, packet.ReadPackedUInt32()) + for i := 0; i < len(system.Players); i++ { + system.Players[i] = packet.Read() + } +} diff --git a/src/systemtypes/switch.go b/src/systemtypes/switch.go new file mode 100644 index 0000000..d39b585 --- /dev/null +++ b/src/systemtypes/switch.go @@ -0,0 +1,21 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type SwitchSystem struct { + ExpectedSwitches byte + ActualSwitches byte + Value byte +} + +func (system *SwitchSystem) Write(packet *protocol.Packet, isSpawn bool) { + packet.Write(system.ExpectedSwitches) + packet.Write(system.ActualSwitches) + packet.Write(system.Value) +} + +func (system *SwitchSystem) Read(packet *protocol.Packet, isSpawn bool) { + system.ExpectedSwitches = packet.Read() + system.ActualSwitches = packet.Read() + system.Value = packet.Read() +} diff --git a/src/systemtypes/systemtype.go b/src/systemtypes/systemtype.go new file mode 100644 index 0000000..276be4a --- /dev/null +++ b/src/systemtypes/systemtype.go @@ -0,0 +1,16 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type SystemType interface { + Write(packet *protocol.Packet, isSpawn bool) + Read(packet *protocol.Packet, isSpawn bool) +} + +func SystemTypeCast(baz interface{}) *SystemType { + f, ok := baz.(*SystemType) + if !ok { + return nil + } + return f +} diff --git a/src/systemtypes/userconsolepair.go b/src/systemtypes/userconsolepair.go new file mode 100644 index 0000000..edda55e --- /dev/null +++ b/src/systemtypes/userconsolepair.go @@ -0,0 +1,18 @@ +package systemtypes + +import "codehub.onpointcoding.net/sean/go-susapp/protocol" + +type UserConsolePair struct { + PlayerID byte + ConsoleID byte +} + +func (pair *UserConsolePair) Write(packet *protocol.Packet) { + packet.Write(pair.PlayerID) + packet.Write(pair.ConsoleID) +} + +func (pair *UserConsolePair) Read(packet *protocol.Packet) { + pair.PlayerID = packet.Read() + pair.ConsoleID = packet.Read() +} diff --git a/src/util/collision.go b/src/util/collision.go new file mode 100644 index 0000000..dc27d59 --- /dev/null +++ b/src/util/collision.go @@ -0,0 +1,11 @@ +package util + +func CollisionIntersect(x float64, y float64, ax float64, ay float64, bx float64, by float64) bool { + return x > ax && y > ay && x < bx && y < by +} + +func CollisionIntersectCenter(x float64, y float64, ax float64, ay float64, w float64, h float64) bool { + hw := w / 2 + hh := h / 2 + return CollisionIntersect(x, y, ax-hw, ay-hh, ax+hw, ay+hh) +} diff --git a/src/util/countsetbits.go b/src/util/countsetbits.go new file mode 100644 index 0000000..fd2d874 --- /dev/null +++ b/src/util/countsetbits.go @@ -0,0 +1,13 @@ +package util + +func CountSetBits(n uint32) int { + a := 0 + var b uint32 = 1 + for i := 0; i < 32; i++ { + if (b & n) == b { + a++ + } + b = b << 1 + } + return a +} diff --git a/src/util/gamecode.go b/src/util/gamecode.go new file mode 100644 index 0000000..16b3d3f --- /dev/null +++ b/src/util/gamecode.go @@ -0,0 +1,58 @@ +package util + +import ( + "encoding/binary" + "errors" + "strings" +) + +var codeCharSet = []byte("QWXRTYLPESDFGHUJKZOCVBINMA") +var codeInverseSet = []byte{25, 21, 19, 10, 8, 11, 12, 13, 22, 15, 16, 6, 24, 23, 18, 7, 0, 3, 9, 4, 14, 20, 1, 2, 5, 17} + +var ErrGameCodeLettersOnly = errors.New("invalid code, expected letters only") +var ErrGameCodeLength = errors.New("invalid code length, expected 4 or 6 characters") + +func CodeFromGameID(gameID int32) string { + if gameID < 1 { + firstTwo := gameID & 0x3ff + lastFour := (gameID >> 10) & 0xfffff + + var a [6]byte + a[0] = codeCharSet[firstTwo%26] + a[1] = codeCharSet[firstTwo/26] + a[2] = codeCharSet[lastFour%26] + a[3] = codeCharSet[(lastFour/26)%26] + a[4] = codeCharSet[(lastFour/676)%26] + a[5] = codeCharSet[(lastFour/17576)%26] + + return string(a[:]) + } else { + var b [4]byte + binary.LittleEndian.PutUint32(b[:], uint32(gameID)) + return string(b[:]) + } +} + +func CodeToGameID(gameCode string) (int32, error) { + gameCode = strings.ToUpper(gameCode) + if !OnlyLetters(gameCode) { + return 0, ErrGameCodeLettersOnly + } + if len(gameCode) == 4 { + b := []byte(gameCode) + return int32(binary.LittleEndian.Uint32(b)), nil + } else if len(gameCode) != 6 { + return 0, ErrGameCodeLength + } else { + c1 := uint32(codeInverseSet[gameCode[0]-65]) + c2 := uint32(codeInverseSet[gameCode[1]-65]) + c3 := uint32(codeInverseSet[gameCode[2]-65]) + c4 := uint32(codeInverseSet[gameCode[3]-65]) + c5 := uint32(codeInverseSet[gameCode[4]-65]) + c6 := uint32(codeInverseSet[gameCode[5]-65]) + + firstTwo := (c1 + 26*c2) & 0x3ff + lastFour := (c3 + 26*(c4+26*(c5+26*c6))) + return int32(firstTwo | ((lastFour << 10) & 0x3ffffc00) | 0x80000000), nil + } +} diff --git a/src/util/go.mod b/src/util/go.mod new file mode 100644 index 0000000..8ade5f2 --- /dev/null +++ b/src/util/go.mod @@ -0,0 +1,7 @@ +module codehub.onpointcoding.net/sean/go-susapp/util + +go 1.17 + +replace codehub.onpointcoding.net/sean/go-susapp/protocol => ../protocol + +require codehub.onpointcoding.net/sean/go-susapp/protocol v1.0.0 diff --git a/src/util/lerp.go b/src/util/lerp.go new file mode 100644 index 0000000..c350971 --- /dev/null +++ b/src/util/lerp.go @@ -0,0 +1,21 @@ +package util + +func Lerp(a float32, b float32, t float32) float32 { + if t < 0 { + return 0 + } else if t > 1 { + return 1 + } + return a + ((b - a) * t) +} + +func LerpReverse(a float32, b float32, t float32) float32 { + v := (t - a) / (b - a) + + if v < 0 { + return 0 + } else if v > 1 { + return 1 + } + return v +} diff --git a/src/util/masterserver.go b/src/util/masterserver.go new file mode 100644 index 0000000..c57d3c4 --- /dev/null +++ b/src/util/masterserver.go @@ -0,0 +1,10 @@ +package util + +import "net" + +type MasterServer struct { + Name string + IPAddress net.IP + Port uint16 + NumberOfConnections uint32 +} diff --git a/src/util/onlyletters.go b/src/util/onlyletters.go new file mode 100644 index 0000000..660f128 --- /dev/null +++ b/src/util/onlyletters.go @@ -0,0 +1,10 @@ +package util + +func OnlyLetters(a string) bool { + for _, i := range a { + if i < 'A' || i > 'Z' { + return false + } + } + return true +} diff --git a/src/util/vec2.go b/src/util/vec2.go new file mode 100644 index 0000000..5288bf9 --- /dev/null +++ b/src/util/vec2.go @@ -0,0 +1,10 @@ +package util + +type Vec2 struct { + X float32 + Y float32 +} + +func NewVec2(x float32, y float32) Vec2 { + return Vec2{x, y} +} diff --git a/state.go b/state.go new file mode 100644 index 0000000..64219a4 --- /dev/null +++ b/state.go @@ -0,0 +1,111 @@ +package main + +import ( + "fmt" + "os" + + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/innernetobjects" + "codehub.onpointcoding.net/sean/go-susapp/protocol" +) + +type State struct { + Screen int + Version int32 + PosX float32 + PosY float32 + Settings *SettingsJSON + CurrentGame *CurrentGameData + TypedGameID string + IsTypingReady bool + IsLobbyMenuOpen bool + DisconnectReason byte + DisconnectStringReason string + HoldingUp bool + HoldingLeft bool + HoldingDown bool + HoldingRight bool + MovementX float32 + MovementY float32 +} + +type CurrentGameData struct { + GameID int32 + ClientID uint32 + ClientPlayerNetID uint32 + HostClientID uint32 + OtherClientIDs []uint32 + GamePrivacy byte + GameOptions *protocol.GameOptionsData + NetObjects map[uint32]*innernetobjects.InnerNetObject + PlayerNetObjects *PlayerNetObjectMaps + MapNetObjectNetID uint32 + GameDataNetID uint32 + StartGameTimer byte + CheckNameNonce uint16 + CheckColorNonce uint16 + CheckPetHatAndSkinNonce uint16 + CheckedNameAndColor byte + HasSetStartCounter bool +} + +type PlayerNetObjectMaps struct { + ByPlayerClientID map[uint32]*PlayerObject + ByPlayerNetID map[uint32]*PlayerObject +} + +type PlayerObject struct { + PlayerClientNetID uint32 + PlayerControllerNetID uint32 + PlayerPhysicsNetID uint32 + PlayerNetworkTransform uint32 + PlayerName string + PlayerColor byte + PlayerPet uint32 + PlayerHat uint32 + PlayerSkin uint32 +} + +var settingsFilename string = "SusSettings.json" + +func CreateAppState(version int32) *State { + forceSave := false + settings, err := readJson(settingsFilename) + if err != nil { + if os.IsNotExist(err) { + forceSave = true + } else { + fmt.Printf("Error loading settings: %v\n", err) + return nil + } + } + s := &State{ + Screen: enum.SCREEN_TITLE, + Version: version, + Settings: &settings, + } + if forceSave { + s.SaveSettings() + } + return s +} + +func (state *State) SaveSettings() error { + return writeJson(settingsFilename, *state.Settings) +} + +func readJson(filename string) (SettingsJSON, error) { + b, err := os.ReadFile(filename) + if err != nil { + return NewDefaultSettingsJSON(), err + } + return UnmarshalSettings(b) +} + +func writeJson(filename string, settings SettingsJSON) error { + b, err := settings.Marshal() + if err != nil { + return err + } + return os.WriteFile(filename, b, 0644) +} diff --git a/sus.go b/sus.go new file mode 100644 index 0000000..8fd3aad --- /dev/null +++ b/sus.go @@ -0,0 +1,295 @@ +package main + +import ( + "fmt" + "log" + "net" + "os" + "time" + + "github.com/gotk3/gotk3/cairo" + "github.com/gotk3/gotk3/gdk" + "github.com/gotk3/gotk3/glib" + "github.com/gotk3/gotk3/gtk" + + "codehub.onpointcoding.net/sean/go-susapp/enum" + "codehub.onpointcoding.net/sean/go-susapp/gamedata" + "codehub.onpointcoding.net/sean/go-susapp/packets" + "codehub.onpointcoding.net/sean/go-susapp/protocol" + "codehub.onpointcoding.net/sean/go-susapp/util" +) + +const appID = "net.onpointcoding.susapp" + +func main() { + fmt.Println("Booting Sus App") + sus := &SusApp{} + sus.startApp() + fmt.Println("Goodbye :(") +} + +type SusApp struct { + state *State + application *gtk.Application + appWindow *gtk.ApplicationWindow + renderer *Renderer + keyInput *KeyInputHandler + mouseInput *MouseInputHandler + packetProcessor *PacketProcessor + movementProcessor *MovementProcessor + networkHandler *protocol.PacketHandler +} + +func (sus *SusApp) startApp() { + // Create Gtk Application, change appID to your application domain name reversed. + var err error + sus.application, err = gtk.ApplicationNew(appID, glib.APPLICATION_NON_UNIQUE) + // Check to make sure no errors when creating Gtk Application + if err != nil { + log.Fatal("Could not create application.", err) + } + + renderTicker := time.NewTicker(50 * time.Millisecond) + renderTickerDone := make(chan bool) + defer func() { + fmt.Println("Stopping render ticks") + renderTicker.Stop() + renderTickerDone <- true + }() + + movementTicker := time.NewTicker(50 * time.Millisecond) + movementTickerDone := make(chan bool) + defer func() { + fmt.Println("Stopping movement ticks") + movementTicker.Stop() + movementTickerDone <- true + }() + + sus.state = CreateAppState(GenerateVersionNumber(2021, 6, 30, 0)) + if sus.state == nil { + fmt.Println("Error loading new app state") + return + } + sus.renderer = NewRenderer(sus.state) + sus.keyInput = NewKeyInputHandler(sus.state) + sus.mouseInput = NewMouseInputHandler(sus.state, sus.renderer, sus.playGame, sus.openSettings, sus.disconnectFromGame) + sus.packetProcessor = NewPacketProcessor(sus) + sus.movementProcessor = &MovementProcessor{sus: sus} + + fmt.Printf("Starting Sus App %s\n", GetVersionFormatted(sus.state.Version)) + + sus.application.Connect("activate", func() { + w := 800 + h := 450 + + // Create ApplicationWindow + sus.appWindow, err = gtk.ApplicationWindowNew(sus.application) + if err != nil { + log.Fatal("Could not create application window.", err) + } + // Set ApplicationWindow Properties + sus.appWindow.SetTitle("Sus App") + sus.appWindow.SetDefaultSize(w, h) + + layout, _ := gtk.LayoutNew(nil, nil) + sus.appWindow.Add(layout) + + da, _ := gtk.DrawingAreaNew() + da.SetHAlign(gtk.ALIGN_FILL) + da.SetVAlign(gtk.ALIGN_FILL) + da.SetHExpand(true) + da.SetVExpand(true) + da.SetSizeRequest(w, h) + layout.Add(da) + + // Listen for ticks from renderTicker + go func() { + for { + select { + case <-renderTickerDone: + return + case <-renderTicker.C: + da.QueueDraw() + } + } + }() + + // Listen for ticks from movementTicker + go func() { + z := time.Now().UnixMilli() + for { + select { + case <-movementTickerDone: + return + case a := <-movementTicker.C: + y := a.UnixMilli() - z + z = a.UnixMilli() + sus.movementProcessor.Tick(y) + } + } + }() + + r := sus.renderer + da.Connect("draw", func(da *gtk.DrawingArea, cr *cairo.Context) { + r.Draw(da, cr) + }) + + da.AddEvents(int(gdk.KEY_RELEASE_MASK) | int(gdk.KEY_PRESS_MASK) | int(gdk.EVENT_CONFIGURE) | int(gdk.EVENT_BUTTON_PRESS)) + da.Connect("event", sus.mouseInput.motionEvent) + sus.appWindow.Connect("button-press-event", sus.mouseInput.buttonPressEvent) + sus.appWindow.Connect("key-press-event", sus.keyInput.keyPressEvent) + sus.appWindow.Connect("key-release-event", sus.keyInput.keyReleaseEvent) + sus.appWindow.Connect("configure-event", func(window *gtk.ApplicationWindow, ev *gdk.Event) { + e := gdk.EventConfigureNewFromEvent(ev) + da.SetSizeRequest(e.Width(), e.Height()) + }) + sus.appWindow.Connect("destroy", func(window *gtk.ApplicationWindow) bool { + fmt.Println("Closing Sus App") + sus.disconnectFromGame() + sus.application.Quit() + return true + }) + + sus.appWindow.ShowAll() + }) + sus.application.Run(os.Args) +} + +func (sus *SusApp) setupPacketHandler(ip *net.IP, port int, firstPacket func(ph *protocol.PacketHandler)) { + addr := net.UDPAddr{ + Port: port, + IP: *ip, + } + ser, err := net.DialUDP("udp", nil, &addr) + if err != nil { + fmt.Printf("Some error %v\n", err) + return + } + defer ser.Close() + fmt.Printf("Starting connection: %v => %v\n", ser.LocalAddr().String(), ser.RemoteAddr().String()) + + nethandler := protocol.NewPacketHandler(ser, &addr) + nethandler.HandleNormalPacket = sus.handleNormalPacket + nethandler.HandleReliablePacket = sus.handleReliablePacket + nethandler.HandleForceDisconnectPacket = sus.handleForceDisconnectPacket + nethandler.HandleNormalDisconnectPacket = sus.handleNormalDisconnectPacket + nethandler.HandleAcknowledgementPacket = sus.handleAcknowledgementPacket + nethandler.HandlePingPacket = sus.handlePingPacket + + sus.networkHandler = nethandler + + p := make([]byte, 4096) + + firstPacket(nethandler) + for { + n, _, err := ser.ReadFromUDP(p) + if n != 0 { + if err != nil { + fmt.Printf("Some error %v\n", err) + continue + } + + go nethandler.ReadPacket(p[:n]) + } + } +} + +func (sus *SusApp) playGame() { + sus.state.Screen = enum.SCREEN_WAITING + sus.state.IsTypingReady = false + + addrstr := fmt.Sprintf("%v:%v", sus.state.Settings.ServerAddress, sus.state.Settings.ServerPort) + raddr, err := net.ResolveUDPAddr("udp", addrstr) + if err != nil { + fmt.Printf("Failed to resolve address: %v\n", addrstr) + return + } + go sus.setupPacketHandler(&raddr.IP, raddr.Port, func(ph *protocol.PacketHandler) { + fmt.Println("Sending first packet") + ph.SendHelloPacket(ph.GetNonce(), 0, sus.state.Version, sus.state.Settings.PlayerName, 0, sus.state.Settings.Language, sus.state.Settings.QuickChatMode) + y, _ := util.CodeToGameID(sus.state.TypedGameID) + var z protocol.HazelPayload = &packets.JoinGameC2S{GameID: y} + ph.SendReliablePacket(ph.GetNonce(), []*protocol.Hazel{ + protocol.NewHazelFromPayload(&z), + }) + }) +} + +func (sus *SusApp) openSettings() { + openSettingsWindow(sus.application, nil, sus.state) +} + +func (sus *SusApp) disconnectFromGame() { + sus.state.IsLobbyMenuOpen = false + if sus.networkHandler != nil { + sus.networkHandler.SendNormalDisconnectPacket() + } +} + +// TODO: implement all these functions +func (sus *SusApp) handleNormalPacket(net *protocol.PacketHandler, h []*protocol.Hazel) { + sus.packetProcessor.receiveNormalPacket(net, h) +} + +func (sus *SusApp) handleReliablePacket(net *protocol.PacketHandler, nonce uint16, h []*protocol.Hazel) { + sus.packetProcessor.receiveReliablePacket(net, nonce, h) +} + +func (sus *SusApp) handleForceDisconnectPacket(net *protocol.PacketHandler, h *protocol.Hazel) { + net.SendNormalDisconnectPacket() + p := h.GetUnderlyingPacket() + sus.state.DisconnectReason = p.Read() + if p.Remaining() > 0 { + sus.state.DisconnectStringReason = p.ReadString() + sus.state.DisconnectReason = 0xfe + } + sus.state.Screen = enum.SCREEN_ERROR + sus.state.IsLobbyMenuOpen = false + sus.state.DisconnectReason = 0xfe + sus.state.DisconnectStringReason = "This is a very long piece of text that must be completely read and understood thank you for reading :)" +} + +func (sus *SusApp) handleNormalDisconnectPacket(net *protocol.PacketHandler) { + net.SendNormalDisconnectPacket() + sus.state.DisconnectReason = 0xff + sus.state.Screen = enum.SCREEN_TITLE + sus.state.IsLobbyMenuOpen = false +} + +func (sus *SusApp) handleAcknowledgementPacket(net *protocol.PacketHandler, nonce uint16, missing byte) { + if sus.state.CurrentGame != nil && nonce != 0 { + if nonce == sus.state.CurrentGame.CheckNameNonce && sus.state.CurrentGame.CheckedNameAndColor&0b01 == 0 { + sus.state.CurrentGame.CheckedNameAndColor |= 0b01 + if sus.state.CurrentGame.CheckedNameAndColor == 0b11 && sus.state.CurrentGame.CheckedNameAndColor&0b10 == 0 { + sus.sendPetHatAndSkin(net) + } + } else if nonce == sus.state.CurrentGame.CheckColorNonce { + sus.state.CurrentGame.CheckedNameAndColor |= 0b10 + if sus.state.CurrentGame.CheckedNameAndColor == 0b11 { + sus.sendPetHatAndSkin(net) + } + } + } +} + +func (sus *SusApp) handlePingPacket(net *protocol.PacketHandler, nonce uint16) { + // TODO: setup acknowledgement tracking and remove hard coded 0xff + net.SendAcknowledgementPacket(nonce, 0xff) +} + +func (sus *SusApp) sendPetHatAndSkin(net *protocol.PacketHandler) { + // Send RPC SetPet, SetHat, SetSkin + sus.state.CurrentGame.CheckPetHatAndSkinNonce = net.GetNonce() + var f1a gamedata.RpcSub = &gamedata.RpcSetPet{PetID: 1} + var f2a packets.GameDataSubpacket = gamedata.NewRpcFromRpcSub(sus.state.CurrentGame.ClientPlayerNetID, &f1a) + var f1b gamedata.RpcSub = &gamedata.RpcSetHat{HatID: 1} + var f2b packets.GameDataSubpacket = gamedata.NewRpcFromRpcSub(sus.state.CurrentGame.ClientPlayerNetID, &f1b) + var f1c gamedata.RpcSub = &gamedata.RpcSetSkin{SkinID: 1} + var f2c packets.GameDataSubpacket = gamedata.NewRpcFromRpcSub(sus.state.CurrentGame.ClientPlayerNetID, &f1c) + var f3 protocol.HazelPayload = &packets.GameDataAll{GameID: sus.state.CurrentGame.GameID, Subpackets: []*protocol.Hazel{ + f2a.AsHazel(), + f2b.AsHazel(), + f2c.AsHazel(), + }} + net.SendReliablePacket(sus.state.CurrentGame.CheckPetHatAndSkinNonce, []*protocol.Hazel{protocol.NewHazelFromPayload(&f3)}) +} diff --git a/version.go b/version.go new file mode 100644 index 0000000..c986238 --- /dev/null +++ b/version.go @@ -0,0 +1,27 @@ +package main + +import "fmt" + +func GenerateVersionNumber(year int, month int, day int, revision int) int32 { + return int32(year*25000 + month*1800 + day*50 + revision) +} + +func GetVersionYear(version int32) int { + return int(version / 25000) +} + +func GetVersionMonth(version int32) int { + return int((version % 25000) / 1800) +} + +func GetVersionDay(version int32) int { + return int((version % 25000 % 1800) / 50) +} + +func GetVersionRevision(version int32) int { + return int((version % 50)) +} + +func GetVersionFormatted(v int32) string { + return fmt.Sprintf("v%v.%v.%v", GetVersionYear(v), GetVersionMonth(v), GetVersionDay(v)) +}