~starkingdoms/starkingdoms

42e744a2ddba94f4b64dfb3af3b495d3b97186f4 — ghostlyzsh 2 years ago 2b6612f
yay we have thrusters oh wait did i forget to push landing thruster handling (yes, yes i did)
M assets/dist/spritesheet-125.json => assets/dist/spritesheet-125.json +11 -11
@@ 36,7 36,7 @@
      "pivot": { "x": 128, "y": 128 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 256, "h": 256 }
    },
    "hearty_party.png": {
    "landingleg.png": {
      "frame": { "x": 0, "y": 1024, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 45,7 45,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "trackindicator.png": {
    "hearty_party.png": {
      "frame": { "x": 0, "y": 1088, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 54,7 54,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "autoplr_error.png": {
    "trackindicator.png": {
      "frame": { "x": 0, "y": 1152, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 63,7 63,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "autoplr_cfg.png": {
    "autoplr_error.png": {
      "frame": { "x": 0, "y": 1216, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 72,7 72,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "hearty.png": {
    "autoplr_cfg.png": {
      "frame": { "x": 0, "y": 1280, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 81,7 81,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "hearty_ferris.png": {
    "hearty.png": {
      "frame": { "x": 0, "y": 1344, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 90,7 90,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "superthruster_on.png": {
    "hearty_ferris.png": {
      "frame": { "x": 0, "y": 1408, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 99,7 99,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "ecothruster_on.png": {
    "superthruster_on.png": {
      "frame": { "x": 0, "y": 1472, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 108,7 108,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "landingthruster_on.png": {
    "ecothruster_on.png": {
      "frame": { "x": 0, "y": 1536, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 117,7 117,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "thruster_on.png": {
    "landingthruster_on.png": {
      "frame": { "x": 0, "y": 1600, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,


@@ 126,7 126,7 @@
      "pivot": { "x": 32, "y": 32 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
    },
    "landingleg.png": {
    "thruster_on.png": {
      "frame": { "x": 0, "y": 1664, "w": 64, "h": 64 },
      "rotated": false,
      "trimmed": false,

M assets/dist/spritesheet-125.png => assets/dist/spritesheet-125.png +0 -0
M assets/dist/spritesheet-375.json => assets/dist/spritesheet-375.json +10 -10
@@ 36,7 36,7 @@
      "pivot": { "x": 384, "y": 384 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 768, "h": 768 }
    },
    "hearty_party.png": {
    "landingleg.png": {
      "frame": { "x": 0, "y": 3072, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,


@@ 45,7 45,7 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "trackindicator.png": {
    "hearty_party.png": {
      "frame": { "x": 0, "y": 3264, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,


@@ 54,7 54,7 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "autoplr_error.png": {
    "trackindicator.png": {
      "frame": { "x": 0, "y": 3456, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,


@@ 72,7 72,7 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "hearty.png": {
    "autoplr_error.png": {
      "frame": { "x": 0, "y": 3840, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,


@@ 81,7 81,7 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "hearty_ferris.png": {
    "hearty.png": {
      "frame": { "x": 0, "y": 4032, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,


@@ 90,7 90,7 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "superthruster_on.png": {
    "hearty_ferris.png": {
      "frame": { "x": 0, "y": 4224, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,


@@ 99,7 99,7 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "ecothruster_on.png": {
    "superthruster_on.png": {
      "frame": { "x": 0, "y": 4416, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,


@@ 108,7 108,7 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "landingthruster_on.png": {
    "ecothruster_on.png": {
      "frame": { "x": 0, "y": 4608, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,


@@ 117,7 117,7 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "thruster_on.png": {
    "landingthruster_on.png": {
      "frame": { "x": 0, "y": 4800, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,


@@ 126,7 126,7 @@
      "pivot": { "x": 96, "y": 96 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
    },
    "landingleg.png": {
    "thruster_on.png": {
      "frame": { "x": 0, "y": 4992, "w": 192, "h": 192 },
      "rotated": false,
      "trimmed": false,

M assets/dist/spritesheet-375.png => assets/dist/spritesheet-375.png +0 -0
M assets/dist/spritesheet-full.json => assets/dist/spritesheet-full.json +12 -12
@@ 36,7 36,7 @@
      "pivot": { "x": 1024, "y": 1024 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 2048, "h": 2048 }
    },
    "hearty_party.png": {
    "landingleg.png": {
      "frame": { "x": 0, "y": 6144, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 45,7 45,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "trackindicator.png": {
    "hearty_party.png": {
      "frame": { "x": 0, "y": 6656, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 54,7 54,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "autoplr_error.png": {
    "trackindicator.png": {
      "frame": { "x": 0, "y": 7168, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 72,7 72,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "hearty.png": {
    "autoplr_error.png": {
      "frame": { "x": 512, "y": 6656, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 81,7 81,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "hearty_ferris.png": {
    "hearty.png": {
      "frame": { "x": 512, "y": 7168, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 90,7 90,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "superthruster_on.png": {
    "hearty_ferris.png": {
      "frame": { "x": 1024, "y": 6144, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 99,7 99,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "ecothruster_on.png": {
    "superthruster_on.png": {
      "frame": { "x": 1024, "y": 6656, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 108,7 108,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "landingthruster_on.png": {
    "ecothruster_on.png": {
      "frame": { "x": 1024, "y": 7168, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 117,7 117,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "thruster_on.png": {
    "landingthruster_on.png": {
      "frame": { "x": 1536, "y": 6144, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 126,7 126,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "landingleg.png": {
    "thruster_on.png": {
      "frame": { "x": 1536, "y": 6656, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 144,7 144,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "hub_on.png": {
    "powerhub_on.png": {
      "frame": { "x": 4096, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,


@@ 153,7 153,7 @@
      "pivot": { "x": 256, "y": 256 },
      "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
    },
    "powerhub_on.png": {
    "hub_on.png": {
      "frame": { "x": 4608, "y": 0, "w": 512, "h": 512 },
      "rotated": false,
      "trimmed": false,

M assets/dist/spritesheet-full.png => assets/dist/spritesheet-full.png +0 -0
M assets/final/125/landingleg.png => assets/final/125/landingleg.png +0 -0
M assets/final/375/landingleg.png => assets/final/375/landingleg.png +0 -0
M assets/final/full/landingleg.png => assets/final/full/landingleg.png +0 -0
M assets/src/landingleg.svg => assets/src/landingleg.svg +50 -33
@@ 10,8 10,8 @@
   inkscape:export-filename="/home/tm85/prj/stk_sprites/landingthruster_off.png"
   inkscape:export-xdpi="96"
   inkscape:export-ydpi="96"
   inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20)"
   sodipodi:docname="landingleg.ink.svg"
   inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
   sodipodi:docname="landingleg.svg"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns="http://www.w3.org/2000/svg"


@@ 32,19 32,22 @@
     inkscape:snap-object-midpoints="true"
     inkscape:snap-global="true"
     inkscape:guide-bbox="true"
     inkscape:zoom="2"
     inkscape:cx="287.75"
     inkscape:cy="227.25"
     inkscape:window-width="2548"
     inkscape:window-height="1412"
     inkscape:window-x="4"
     inkscape:window-y="20"
     inkscape:zoom="1.4142136"
     inkscape:cx="222.73864"
     inkscape:cy="255.26555"
     inkscape:window-width="1918"
     inkscape:window-height="1057"
     inkscape:window-x="0"
     inkscape:window-y="21"
     inkscape:window-maximized="1"
     inkscape:current-layer="layer1">
     inkscape:current-layer="layer1"
     inkscape:showpageshadow="2"
     inkscape:deskcolor="#d1d1d1">
    <sodipodi:guide
       position="67.733329,84.666669"
       orientation="1,0"
       id="guide352" />
       id="guide352"
       inkscape:locked="false" />
  </sodipodi:namedview>
  <defs
     id="defs2">


@@ 64,7 67,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,8.466667,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect7109"


@@ 81,7 85,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,8.466667,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect6855"


@@ 98,7 103,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect6816"


@@ 115,7 121,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect6122"


@@ 132,7 139,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect5994"


@@ 149,7 157,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect17083"


@@ 166,7 175,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect16803"


@@ 183,7 193,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect15774"


@@ 200,7 211,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,4.2333333,0,1 @ F,0,0,1,0,4.2333333,0,1 @ F,0,0,1,0,4.2333333,0,1 @ F,0,0,1,0,4.2333333,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect15612"


@@ 217,7 229,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect15006"


@@ 234,7 247,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,2.1166667,0,1 @ F,0,0,1,0,2.1166667,0,1" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect12397"


@@ 251,7 265,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,2.6458333,0,5 @ F,0,0,1,0,2.6458333,0,5 @ F,0,0,1,0,2.6458333,0,5 | F,0,0,1,0,2.6458333,0,5 @ F,0,0,1,0,2.6458333,0,5 @ F,0,0,1,0,2.6458333,0,5" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect12289"


@@ 268,7 283,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="C,0,0,1,0,2.6458333,0,5 @ C,0,0,1,0,2.6458333,0,5 @ C,0,0,1,0,2.6458333,0,5 | C,0,0,1,0,2.6458333,0,5 @ C,0,0,1,0,2.6458333,0,5 @ C,0,0,1,0,2.6458333,0,5" />
    <inkscape:path-effect
       effect="fillet_chamfer"
       id="path-effect12074"


@@ 285,7 301,8 @@
       apply_no_radius="true"
       apply_with_radius="true"
       only_selected="false"
       hide_knots="false" />
       hide_knots="false"
       nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1" />
  </defs>
  <g
     inkscape:groupmode="layer"


@@ 300,26 317,26 @@
    <path
       id="rect16402-3"
       style="display:inline;fill:#444444;stroke-width:0.0568435"
       d="m 95.25,135.46667 h 2.116668 A 2.1166667,2.1166667 135 0 0 99.483335,133.35 l 0,-65.616667 H 93.133333 V 133.35 A 2.1166667,2.1166667 45 0 0 95.25,135.46667 Z"
       d="m 95.25,67.733337 h 2.116668 A 2.1166667,2.1166667 135 0 0 99.483335,65.61667 L 99.483335,0 H 93.133333 V 65.61667 A 2.1166667,2.1166667 45 0 0 95.25,67.733337 Z"
       inkscape:path-effect="#path-effect5994"
       inkscape:original-d="m 93.133333,135.46667 h 6.350002 V 67.733333 h -6.350002 z" />
       inkscape:original-d="m 93.133333,67.733337 h 6.350002 V 0 h -6.350002 z" />
    <path
       id="rect16402-3-6-7"
       style="display:inline;fill:#444444;stroke-width:0.0568435"
       d="m 38.1,135.46667 h 2.116666 A 2.1166667,2.1166667 135 0 0 42.333333,133.35 V 67.733333 h -6.35 V 133.35 A 2.1166667,2.1166667 45 0 0 38.1,135.46667 Z"
       d="m 38.1,67.733337 h 2.116666 A 2.1166667,2.1166667 135 0 0 42.333333,65.61667 V 0 h -6.35 V 65.61667 A 2.1166667,2.1166667 45 0 0 38.1,67.733337 Z"
       inkscape:path-effect="#path-effect6816"
       inkscape:original-d="m 35.983333,135.46667 h 6.35 V 67.733333 h -6.35 z" />
       inkscape:original-d="m 35.983333,67.733337 h 6.35 V 0 h -6.35 z" />
    <path
       id="rect16402-3-6-7-5"
       style="display:inline;fill:#444444;stroke-width:0.0401944"
       d="m 25.4,76.2 16.933333,0 v -8.466667 h -25.4 A 8.466667,8.466667 45 0 0 25.4,76.2 Z"
       d="M 25.4,8.466667 H 42.333333 V 0 h -25.4 A 8.466667,8.466667 45 0 0 25.4,8.466667 Z"
       inkscape:path-effect="#path-effect7109"
       inkscape:original-d="m 16.933333,76.2 h 25.4 v -8.466667 h -25.4 z" />
       inkscape:original-d="m 16.933333,8.466667 h 25.4 V 0 h -25.4 z" />
    <path
       id="rect16402-3-6-7-5-3"
       style="display:inline;fill:#444444;stroke-width:0.0401944"
       d="M 110.06666,76.2 H 93.133333 V 67.733333 H 118.53333 A 8.466667,8.466667 135 0 1 110.06666,76.2 Z"
       d="M 110.06666,8.466667 H 93.133333 V 0 h 25.399997 a 8.466667,8.466667 135 0 1 -8.46667,8.466667 z"
       inkscape:path-effect="#path-effect7605"
       inkscape:original-d="M 118.53333,76.2 H 93.133333 v -8.466667 h 25.399997 z" />
       inkscape:original-d="M 118.53333,8.466667 H 93.133333 V 0 h 25.399997 z" />
  </g>
</svg>

M client/src/index.ts => client/src/index.ts +12 -6
@@ 154,6 154,10 @@ async function client_main(server: string, username: string, texture_quality: st
            }

            for (let i = 0; i < global.modules.length; i++) {
                if (global.modules[i].moduleType == ModuleType.LandingThrusterSuspension) {
                    console.log("hi");
                    continue;
                }
                let relativeX = global.modules[i].x - worldX;
                let relativeY = global.modules[i].y - worldY;
                let rot = -global.modules[i].rotation;


@@ 176,6 180,10 @@ async function client_main(server: string, username: string, texture_quality: st
                }
            }
            global.tree.forEach((value: AttachedModule, key: number) => {
                if (value.module_type == ModuleType.LandingThrusterSuspension) {
                    console.log("hi");
                    return;
                }
                let relativeX = value.x - worldX;
                let relativeY = value.y - worldY;
                let rot = -value.rotation;


@@ 187,8 195,6 @@ async function client_main(server: string, username: string, texture_quality: st
                }
                if (bound[0] < adjustedX && adjustedX < bound[1]) {
                    if (bound[2] < adjustedY && adjustedY < bound[3]) {
                        console.log("relative: " + relativeX + ", " + relativeY);
                        console.log("adjusted: " + adjustedX + ", " + adjustedY);
                        let msg = MessageC2SModuleDetach.encode({
                            moduleId: key,
                        }).finish();


@@ 359,7 365,6 @@ async function client_main(server: string, username: string, texture_quality: st
                    global.context.moveTo(global.me!.x - global.me!.x, global.me!.y - global.me!.y);
                    global.context.lineTo(planet.x - global.me!.x, planet.y - global.me!.y);
                    global.context.stroke();
                    console.log("moon: " + planet.x + ", " + planet.y);

                    document.getElementById("pos-moon")!.innerText = `Relative to Moon: ${Math.trunc(global.me!.x - planet.x)}, ${Math.trunc(global.me!.y - planet.y)}`
                }


@@ 543,14 548,11 @@ async function client_main(server: string, username: string, texture_quality: st
        }

        if (global.me !== null) {
            console.log(thruster_counter);
            thruster_counter += 1;
            //thruster_counter = 1; // uncomment this line to disable particle limits
            if (thruster_counter > 3) {
                console.log("resetting counter");
                thruster_counter = 0;
            } else if (thruster_counter == 1) {
                console.log("drawing particle");
                if (global.keys.up) {
                    // two backward thrusters
                    // this one is blue


@@ 612,6 614,8 @@ function module_type_to_tex_id(ty: ModuleType, is_on: boolean): string {
            return "cargo_off.png"
        } else if (ty == ModuleType.LandingThruster) {
            return "landingthruster_off.png"
        } else if (ty == ModuleType.LandingThrusterSuspension) {
            return "landingleg.png"
        } else if (ty == ModuleType.Hub) {
            return "hub_off.png"
        }


@@ 620,6 624,8 @@ function module_type_to_tex_id(ty: ModuleType, is_on: boolean): string {
            return "cargo_on.png"
        } else if (ty == ModuleType.LandingThruster) {
            return "landingthruster_on.png"
        } else if (ty == ModuleType.LandingThrusterSuspension) {
            return "landingleg.png"
        } else if (ty == ModuleType.Hub) {
            return "hub_on.png"
        }

M client/src/particle.ts => client/src/particle.ts +1 -3
@@ 69,8 69,6 @@ export function tickParticles(delta: number) {

        if (!(particles[i].timer > particles[i].lifetime)) {
            keptParticles.push(particles[i]);
        } else {
            console.log("dropping particle");
        }
    }
    particles = keptParticles;


@@ 79,4 77,4 @@ export function tickParticles(delta: number) {
export function tickAndDrawParticles(delta: number) {
    tickParticles(delta);
    drawParticles();
}
\ No newline at end of file
}

M client/src/protocol/module.ts => client/src/protocol/module.ts +26 -2
@@ 7,7 7,8 @@ export enum ModuleType {
  UNKNOWN = 0,
  Cargo = 1,
  LandingThruster = 2,
  Hub = 3,
  LandingThrusterSuspension = 3,
  Hub = 4,
  UNRECOGNIZED = -1,
}



@@ 23,6 24,9 @@ export function moduleTypeFromJSON(object: any): ModuleType {
    case "LandingThruster":
      return ModuleType.LandingThruster;
    case 3:
    case "LandingThrusterSuspension":
      return ModuleType.LandingThrusterSuspension;
    case 4:
    case "Hub":
      return ModuleType.Hub;
    case -1:


@@ 40,6 44,8 @@ export function moduleTypeToJSON(object: ModuleType): string {
      return "Cargo";
    case ModuleType.LandingThruster:
      return "LandingThruster";
    case ModuleType.LandingThrusterSuspension:
      return "LandingThrusterSuspension";
    case ModuleType.Hub:
      return "Hub";
    case ModuleType.UNRECOGNIZED:


@@ 55,6 61,7 @@ export interface Module {
  y: number;
  id: number;
  flags: number;
  children: Attachment[];
}

export interface AttachedModule {


@@ 72,7 79,7 @@ export interface Attachment {
}

function createBaseModule(): Module {
  return { moduleType: 0, rotation: 0, x: 0, y: 0, id: 0, flags: 0 };
  return { moduleType: 0, rotation: 0, x: 0, y: 0, id: 0, flags: 0, children: [] };
}

export const Module = {


@@ 95,6 102,9 @@ export const Module = {
    if (message.flags !== 0) {
      writer.uint32(48).uint32(message.flags);
    }
    for (const v of message.children) {
      Attachment.encode(v!, writer.uint32(58).fork()).ldelim();
    }
    return writer;
  },



@@ 147,6 157,13 @@ export const Module = {

          message.flags = reader.uint32();
          continue;
        case 7:
          if (tag != 58) {
            break;
          }

          message.children.push(Attachment.decode(reader, reader.uint32()));
          continue;
      }
      if ((tag & 7) == 4 || tag == 0) {
        break;


@@ 164,6 181,7 @@ export const Module = {
      y: isSet(object.y) ? Number(object.y) : 0,
      id: isSet(object.id) ? Number(object.id) : 0,
      flags: isSet(object.flags) ? Number(object.flags) : 0,
      children: Array.isArray(object?.children) ? object.children.map((e: any) => Attachment.fromJSON(e)) : [],
    };
  },



@@ 175,6 193,11 @@ export const Module = {
    message.y !== undefined && (obj.y = message.y);
    message.id !== undefined && (obj.id = Math.round(message.id));
    message.flags !== undefined && (obj.flags = Math.round(message.flags));
    if (message.children) {
      obj.children = message.children.map((e) => e ? Attachment.toJSON(e) : undefined);
    } else {
      obj.children = [];
    }
    return obj;
  },



@@ 190,6 213,7 @@ export const Module = {
    message.y = object.y ?? 0;
    message.id = object.id ?? 0;
    message.flags = object.flags ?? 0;
    message.children = object.children?.map((e) => Attachment.fromPartial(e)) || [];
    return message;
  },
};

M protocol/src/pbuf/module.proto => protocol/src/pbuf/module.proto +3 -1
@@ 8,6 8,7 @@ message Module {
    double y = 4;
    uint32 id = 5;
    uint32 flags = 6;
    repeated Attachment children = 7;
}
message AttachedModule {
    ModuleType module_type = 1;


@@ 26,5 27,6 @@ enum ModuleType {
    UNKNOWN = 0;
    Cargo = 1;
    LandingThruster = 2;
    Hub = 3;
    LandingThrusterSuspension = 3;
    Hub = 4;
}

M server/src/entity.rs => server/src/entity.rs +1 -1
@@ 17,7 17,7 @@ static mut ENTITY_ID_COUNT: AtomicU32 = AtomicU32::new(0);
pub fn get_entity_id() -> EntityId {
    let last_entity_id = unsafe { &ENTITY_ID_COUNT };
    let id = last_entity_id.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
    assert!(id <= 4_147_483_600, "No remaining entity ids");
    assert!(id <= 4_294_967_290, "No remaining entity ids");
    id
}


M server/src/handler.rs => server/src/handler.rs +67 -56
@@ 22,6 22,7 @@ use starkingdoms_protocol::message_s2c::{
};

use protobuf::SpecialFields;
use starkingdoms_protocol::module::ModuleType;
use starkingdoms_protocol::state::State;
use starkingdoms_protocol::{MessageC2S, MessageS2C, PROTOCOL_VERSION};
use std::error::Error;


@@ 457,6 458,14 @@ pub async fn handle_client(
                            .rigid_body_set
                            .get(module.handle)
                            .ok_or("module rigidbody does not exist")?;

                        let children = module.children.iter().map(|c| {
                            starkingdoms_protocol::module::Attachment {
                                id: c.child,
                                slot: 0,
                                special_fields: SpecialFields::default(),
                            }
                        }).collect();
                        let prot_module = starkingdoms_protocol::module::Module {
                            module_type: module.module_type.into(),
                            rotation: body.rotation().angle(),


@@ 464,6 473,7 @@ pub async fn handle_client(
                            y: body.translation().y * SCALE,
                            id: module_id,
                            flags: module.flags,
                            children,
                            special_fields: SpecialFields::default(),
                        };
                        let msg = MessageS2C::ModuleRemove(MessageS2CModuleRemove {


@@ 519,44 529,24 @@ pub async fn handle_client(
                            x.mul_add(angle.cos(), -y * angle.sin()),
                            x.mul_add(angle.sin(), y * angle.cos()),
                        );
                        let mut attachment_slot: Option<usize> = None;
                        if 1.5 < y && y < 3. && -2. < x && x < 2. {
                            attached_id = Some(AttachedModule::attach(
                                &mut data_handle,
                                &mut entities,
                                player_id,
                                player_id,
                                &module.clone().ok_or("module is None")?,
                                2,
                            ));
                            did_attach = true;
                            attachment_slot = Some(2);
                        } else if -3. < y && y < -1.5 && -2. < x && x < 2. {
                            attached_id = Some(AttachedModule::attach(
                                &mut data_handle,
                                &mut entities,
                                player_id,
                                player_id,
                                &module.clone().ok_or("module is None")?,
                                0,
                            ));
                            did_attach = true;
                            attachment_slot = Some(0);
                        } else if -3. < x && x < -1.5 && -2. < y && y < 2. {
                            attached_id = Some(AttachedModule::attach(
                                &mut data_handle,
                                &mut entities,
                                player_id,
                                player_id,
                                &module.clone().ok_or("module is None")?,
                                3,
                            ));
                            did_attach = true;
                            attachment_slot = Some(3);
                        } else if 1.5 < x && x < 3. && -2. < y && y < 2. {
                            attachment_slot = Some(1);
                        }
                        if let Some(slot) = attachment_slot {
                            attached_id = Some(AttachedModule::attach(
                                &mut data_handle,
                                &mut entities,
                                player_id,
                                player_id,
                                &module.clone().ok_or("module is None")?,
                                1,
                                slot,
                            ));
                            did_attach = true;
                        }


@@ 581,42 571,46 @@ pub async fn handle_client(

                            // ghostly: this is cursed as hell
                            // please find a better way in the future lmao
                            let mut attachment_slot: Option<usize> = None;
                            if 1.5 < y && y < 3. && -2. < x && x < 2. {
                                attached_id = Some(AttachedModule::attach(
                                    &mut data_handle,
                                    &mut entities,
                                    parent_id,
                                    player_id,
                                    &module.clone().ok_or("module does not exist")?,
                                    2,
                                ));
                                did_attach = true;
                                attachment_slot = Some(2);
                            } else if -3. < x && x < -1.5 && -2. < y && y < 2. {
                                attached_id = Some(AttachedModule::attach(
                                    &mut data_handle,
                                    &mut entities,
                                    parent_id,
                                    player_id,
                                    &module.clone().ok_or("module does not exist")?,
                                    3,
                                ));
                                did_attach = true;
                                attachment_slot = Some(3);
                            } else if 1.5 < x && x < 3. && -2. < y && y < 2. {
                                attached_id = Some(AttachedModule::attach(
                                    &mut data_handle,
                                    &mut entities,
                                    parent_id,
                                    player_id,
                                    &module.clone().ok_or("module does not exist")?,
                                    1,
                                ));
                                did_attach = true;
                                attachment_slot = Some(1);
                            }
                            if let Some(slot) = attachment_slot {
                                if let Some(module) = module.clone() {
                                    match attached.module_type {
                                        ModuleType::Cargo => {
                                            continue;
                                        }
                                        ModuleType::LandingThruster => {
                                            continue;
                                        }
                                        ModuleType::LandingThrusterSuspension => {
                                            continue;
                                        }
                                        _ => {}
                                    };
                                    attached_id = Some(AttachedModule::attach(
                                        &mut data_handle,
                                        &mut entities,
                                        parent_id,
                                        player_id,
                                        &module,
                                        slot,
                                    ));
                                    did_attach = true;
                                } else {
                                    return Err("module is None")?;
                                }
                            }
                        }
                        if !did_attach {
                            let body = data_handle
                                .rigid_body_set
                                .get_mut(module.ok_or("module does not exist")?.handle)
                                .get_mut(module.as_ref().ok_or("module does not exist")?.handle)
                                .ok_or("module rigidbody does not exist")?;
                            body.set_position(
                                Isometry::new(


@@ 625,6 619,23 @@ pub async fn handle_client(
                                ),
                                true,
                            );
                            if let Some(module) = module {
                                if module.module_type == ModuleType::LandingThruster {
                                    let suspension = entities.get_module_from_id(module.children.get(0).ok_or("suspension child not found")?.child)
                                        .ok_or("suspension not found")?;
                                    let body = data_handle
                                        .rigid_body_set
                                        .get_mut(suspension.handle)
                                        .ok_or("suspension rigidbody does not exist")?;
                                    body.set_position(
                                        Isometry::new(
                                            Vector2::new(p.worldpos_x / SCALE, p.worldpos_y / SCALE),
                                            body.rotation().angle(),
                                        ),
                                        true,
                                    );
                                }
                            }
                        } else if let Some(Ok(id)) = attached_id {
                            let prot_module = entities
                                .get_attached_from_id(id)

M server/src/module.rs => server/src/module.rs +238 -55
@@ 1,11 1,12 @@
use std::error::Error;
use std::f64::consts::PI;

use nalgebra::{point, vector, Isometry2, Unit, Vector2};
use log::debug;
use nalgebra::{point, vector, Isometry2, Unit, Vector2, Vector};
use protobuf::SpecialFields;
use rapier2d_f64::prelude::{
    ColliderBuilder, FixedJointBuilder, ImpulseJointHandle, MassProperties, Real, RigidBodyBuilder,
    RigidBodyHandle, RigidBodySet,
    RigidBodyHandle, RigidBodySet, SharedShape, PrismaticJointBuilder,
};
use starkingdoms_protocol::module::ModuleType;



@@ 21,11 22,24 @@ pub struct Module {
    pub module_type: ModuleType,
    pub lifetime: f64,
    pub flags: u32,
    pub children: Vec<Attachment>,
}
impl Module {
    pub fn search_modules(&self, entities: &EntityHandler) -> Vec<Self> {
        let mut modules = vec![self.clone()];
        for attachment in self.children.iter() {
            if let Some(Entity::Module(child_module)) =
                entities.entities.get(&attachment.child)
            {
                modules.append(&mut child_module.search_modules(entities));
            }
        }
        modules
    }
}

#[derive(Clone)]
pub struct ModuleTemplate {
    pub translation: Vector2<Real>,
    pub mass_properties: MassProperties,
    pub module_type: ModuleType,
}


@@ 115,30 129,76 @@ impl AttachedModule {
            let attach_joint_handle =
                data.impulse_joint_set
                    .insert(parent_handle, module.handle, attach_joint, true);
            let attached_module = Self {
                handle: module.handle,
                module_type: module.module_type,
                player_id,
                children: [None, None, None, None],
            };

            let attached_id = get_entity_id();
            match parent_entity {
                Entity::Player(ref mut player) => {
                    player.children[attachment_slot] = Some(Attachment {
                        child: attached_id,
                        connection: attach_joint_handle,
                    });
                    if module.module_type == ModuleType::LandingThrusterSuspension {
                        player.children[attachment_slot] = Some(Attachment {
                            child: attached_id,
                            connection: attach_joint_handle,
                            can_detach: true,
                        });
                    } else {
                        player.children[attachment_slot] = Some(Attachment {
                            child: attached_id,
                            connection: attach_joint_handle,
                            can_detach: false,
                        });
                    }
                }
                Entity::AttachedModule(ref mut module) => {
                    module.children[attachment_slot] = Some(Attachment {
                        child: attached_id,
                        connection: attach_joint_handle,
                    });
                    if module.module_type == ModuleType::LandingThrusterSuspension {
                        module.children[attachment_slot] = Some(Attachment {
                            child: attached_id,
                            connection: attach_joint_handle,
                            can_detach: true,
                        });
                    } else {
                        module.children[attachment_slot] = Some(Attachment {
                            child: attached_id,
                            connection: attach_joint_handle,
                            can_detach: false,
                        });
                    }
                }
                _ => {
                    panic!("unexpected parent");
                }
            };
            let mut children: [Option<Attachment>; 4] = [None, None, None, None];
            for child in module.children.clone() {
                let child_body = entities.get_module_from_id(child.child)
                    .ok_or("suspension doesn't exist")?;
                let child_body_body = data.rigid_body_set
                    .get_mut(child_body.handle)
                    .ok_or("module body does not exist")?;
                if child_body.module_type == ModuleType::LandingThrusterSuspension {
                    child_body_body.set_translation(module_pos, true);
                    child_body_body.set_rotation(Unit::from_angle(parent_angle + rotation), true);
                    let id = get_entity_id();
                    entity_map.remove(&child.child);
                    let child = Attachment {
                        child: id,
                        connection: child.connection,
                        can_detach: child.can_detach,
                    };
                    children[2] = Some(child.clone());
                    let attached_child = AttachedModule {
                        handle: child_body.handle,
                        module_type: child_body.module_type,
                        player_id,
                        children: [None, None, None, None],
                    };
                    entity_map.insert(id, Entity::AttachedModule(attached_child));
                }
            }
            let attached_module = Self {
                handle: module.handle,
                module_type: module.module_type,
                player_id,
                children,
            };
            entity_map.remove(&id);
            entity_map.insert(attached_id, Entity::AttachedModule(attached_module));
            entities.entities = entity_map;


@@ 154,10 214,18 @@ impl AttachedModule {
    ) -> Option<EntityId> {
        let mut entity_map = entities.entities.clone();

        let new_module;
        if module.module_type == ModuleType::LandingThrusterSuspension {
            new_module = entities.get_attached_from_id(module.find_parent(module, entities)?.0 as u32)?;
        } else {
            new_module = module.clone();
        }
        let module = new_module;

        // player not in parent search
        // also no parents included in parent search
        let player = entities.get_player_from_id(player_id)?;
        let (slot, parent_id) = player.find_parent(module, entities)?;
        let (slot, parent_id) = player.find_parent(&module, entities)?;
        let parent_entity = entity_map.get_mut(&parent_id)?;

        match parent_entity {


@@ 179,28 247,90 @@ impl AttachedModule {
        };
        // remove joint
        let tree = module.search_modules(entities);
        let new_module = Module {
            handle: module.handle,
            module_type: module.module_type,
            lifetime: 0.,
            flags: 0,
        };
        entity_map.remove(&entities.get_id_from_attached(module)?);
        let id = get_entity_id();
        entity_map.insert(id, Entity::Module(new_module));
        if module.module_type == ModuleType::LandingThruster {
            let suspension = (module.children.get(2)?.clone())?;
            let suspension_body = entities.get_attached_from_id(suspension.child)?;
            let suspension_module = Module {
                handle: suspension_body.handle,
                module_type: suspension_body.module_type,
                lifetime: 0.,
                flags: 0,
                children: Vec::new(),
            };
            let suspension_id = get_entity_id();
            let new_module = Module {
                handle: module.handle,
                module_type: module.module_type,
                lifetime: 0.,
                flags: 0,
                children: vec![Attachment {
                    child: suspension_id,
                    connection: suspension.connection,
                    can_detach: false,
                }],
            };
            entity_map.remove(&entities.get_id_from_attached(&module)?);
            entity_map.insert(id, Entity::Module(new_module));
            entity_map.remove(&suspension.child);
            entity_map.insert(suspension_id, Entity::Module(suspension_module));
        } else {
            let new_module = Module {
                handle: module.handle,
                module_type: module.module_type,
                lifetime: 0.,
                flags: 0,
                children: Vec::new(),
            };
            entity_map.remove(&entities.get_id_from_attached(&module)?);
            entity_map.insert(id, Entity::Module(new_module));
        }
        for element in tree {
            for child in element.clone().children.into_iter().flatten() {
                data.impulse_joint_set.remove(child.connection, true);
                let child_body = entities.get_attached_from_id(child.child)?;
                let new_module = Module {
                    handle: child_body.handle,
                    module_type: child_body.module_type,
                    lifetime: 0.,
                    flags: 0,
                };
                entity_map.remove(&entities.get_id_from_attached(&child_body)?);
                let attached_id = get_entity_id();
                entity_map.insert(attached_id, Entity::Module(new_module));
                if child_body.module_type == ModuleType::LandingThrusterSuspension {
                    continue;
                }
                data.impulse_joint_set.remove(child.connection, true);
                if child_body.module_type == ModuleType::LandingThruster {
                    let suspension = (child_body.children.get(2)?.clone())?;
                    let suspension_body = entities.get_attached_from_id(suspension.child)?;
                    let suspension_module = Module {
                        handle: suspension_body.handle,
                        module_type: suspension_body.module_type,
                        lifetime: 0.,
                        flags: 0,
                        children: Vec::new(),
                    };
                    let attached_id = get_entity_id();
                    let suspension_id = get_entity_id();
                    let new_module = Module {
                        handle: child_body.handle,
                        module_type: child_body.module_type,
                        lifetime: 0.,
                        flags: 0,
                        children: vec![Attachment {
                            child: suspension_id,
                            connection: suspension.connection,
                            can_detach: false,
                        }],
                    };
                    entity_map.remove(&entities.get_id_from_attached(&child_body)?);
                    entity_map.insert(attached_id, Entity::Module(new_module));
                    entity_map.remove(&suspension.child);
                    entity_map.insert(suspension_id, Entity::Module(suspension_module));
                } else {
                    let new_module = Module {
                        handle: child_body.handle,
                        module_type: child_body.module_type,
                        lifetime: 0.,
                        flags: 0,
                        children: Vec::new(),
                    };
                    entity_map.remove(&entities.get_id_from_attached(&child_body)?);
                    let attached_id = get_entity_id();
                    entity_map.insert(attached_id, Entity::Module(new_module));
                }
            }
        }
        entities.entities = entity_map;


@@ 228,13 358,20 @@ impl AttachedModule {
        let parent_body = data.rigid_body_set.get(parent_handle)?;
        let parent_pos = vector![parent_body.translation().x, parent_body.translation().y];

        let (anchor, rotation) = match attachment_slot {
        let (anchor, rotation) = if module.module_type == ModuleType::LandingThrusterSuspension { match attachment_slot {
            0 => (point![0. / SCALE, 0. / SCALE], PI),
            1 => (point![0. / SCALE, 0. / SCALE], -PI / 2.),
            2 => (point![0. / SCALE, 0. / SCALE], 0.),
            3 => (point![0. / SCALE, 0. / SCALE], PI / 2.),
            _ => (point![0. / SCALE, 0. / SCALE], 0.),
        } }
        else { match attachment_slot {
            0 => (point![0. / SCALE, 53. / SCALE], PI),
            1 => (point![-53. / SCALE, 0. / SCALE], -PI / 2.),
            2 => (point![0. / SCALE, -53. / SCALE], 0.),
            3 => (point![53. / SCALE, 0. / SCALE], PI / 2.),
            _ => (point![0. / SCALE, 53. / SCALE], 0.),
        };
        } };

        let relative_pos = vector![
            anchor.x.mul_add(


@@ 249,8 386,19 @@ impl AttachedModule {
        let module_pos = parent_pos + relative_pos;

        // create attachment module
        let module_collider = ColliderBuilder::cuboid(25.0 / SCALE, 25.0 / SCALE)
        let module_shape = if module.module_type == ModuleType::LandingThrusterSuspension {
            SharedShape::cuboid(25./SCALE, 1./SCALE)
        } else {
            SharedShape::cuboid(25./SCALE, 25./SCALE)
        };
        let relative_pos = if module.module_type == ModuleType::LandingThrusterSuspension {
            vector![0., -24./SCALE]
        } else {
            vector![0., 0.]
        };
        let module_collider = ColliderBuilder::new(module_shape)
            .mass_properties(module.mass_properties)
            .translation(relative_pos)
            .build();
        let module_body = RigidBodyBuilder::dynamic()
            .translation(module_pos)


@@ 263,14 411,28 @@ impl AttachedModule {
            &mut data.rigid_body_set,
        );

        let attach_joint = FixedJointBuilder::new()
            .local_anchor1(anchor)
            .local_anchor2(point![0.0, 0.0 / SCALE])
            .local_frame2(Isometry2::rotation(rotation))
            .build();
        let attach_joint_handle =
            data.impulse_joint_set
                .insert(parent_handle, attached_handle, attach_joint, true);
        let attach_joint_handle;
        if module.module_type == ModuleType::LandingThrusterSuspension {
            let y = Vector::y_axis();
            let attach_joint = PrismaticJointBuilder::new(y)
                        .local_anchor1(anchor)
                        .local_anchor2(point![0.0, 0.0 / SCALE])
                        .limits([0.0, 14.625/SCALE])
                        .motor_position(0., 32., 4.)
                        .build();
            attach_joint_handle =
                data.impulse_joint_set
                    .insert(parent_handle, attached_handle, attach_joint, true);
        } else {
            let attach_joint = FixedJointBuilder::new()
                        .local_anchor1(anchor)
                        .local_anchor2(point![0.0, 0.0 / SCALE])
                        .local_frame2(Isometry2::rotation(rotation))
                        .build();
            attach_joint_handle =
                data.impulse_joint_set
                    .insert(parent_handle, attached_handle, attach_joint, true);
        }
        let attached_module = Self {
            handle: attached_handle,
            module_type: module.module_type,


@@ 280,16 442,34 @@ impl AttachedModule {
        let attached_id = get_entity_id();
        match parent_entity {
            Entity::Player(ref mut player) => {
                player.children[attachment_slot] = Some(Attachment {
                    child: attached_id,
                    connection: attach_joint_handle,
                });
                if module.module_type == ModuleType::LandingThrusterSuspension {
                    player.children[attachment_slot] = Some(Attachment {
                        child: attached_id,
                        connection: attach_joint_handle,
                        can_detach: true,
                    });
                } else {
                    player.children[attachment_slot] = Some(Attachment {
                        child: attached_id,
                        connection: attach_joint_handle,
                        can_detach: false,
                    });
                }
            }
            Entity::AttachedModule(ref mut module) => {
                module.children[attachment_slot] = Some(Attachment {
                    child: attached_id,
                    connection: attach_joint_handle,
                });
                if module.module_type == ModuleType::LandingThrusterSuspension {
                    module.children[attachment_slot] = Some(Attachment {
                        child: attached_id,
                        connection: attach_joint_handle,
                        can_detach: true,
                    });
                } else {
                    module.children[attachment_slot] = Some(Attachment {
                        child: attached_id,
                        connection: attach_joint_handle,
                        can_detach: false,
                    });
                }
            }
            _ => {
                panic!("unexpected parent");


@@ 306,6 486,7 @@ impl AttachedModule {
            module_type: self.module_type,
            lifetime: 10.,
            flags: 0,
            children: Vec::new(),
        }
    }



@@ 318,6 499,7 @@ impl AttachedModule {
                module_type: self.module_type,
                lifetime: 10.,
                flags: 1,
                children: Vec::new(),
            },
        ))
    }


@@ 389,4 571,5 @@ impl AttachedModule {
pub struct Attachment {
    pub child: EntityId,
    pub connection: ImpulseJointHandle,
    pub can_detach: bool,
}

M server/src/timer.rs => server/src/timer.rs +181 -27
@@ 1,5 1,5 @@
use crate::entity::EntityHandler;
use crate::module::{Module, AttachedModule};
use crate::module::{Module, AttachedModule, ModuleTemplate};
use crate::orbit::constants::{GAME_ORBITS_ENABLED, MARS_APHELION, MARS_ORBIT_TIME, MARS_PERIHELION, MOON_APOAPSIS, MOON_ORBIT_TIME, MOON_PERIAPSIS};
use crate::orbit::orbit::{calculate_point_on_orbit, calculate_world_position_of_orbit};
use crate::{


@@ 160,6 160,7 @@ pub async fn timer_main(
                    module_type: ModuleType::Cargo,
                    lifetime: 0.0,
                    flags: 0,
                    children: Vec::new(),
                };
                entities
                    .write()


@@ 182,27 183,144 @@ pub async fn timer_main(
                );
                module_body.apply_impulse(vector![grav_force.0, grav_force.1], true);


                for child in module.children.clone() {
                    if let Some(child) = child {
                        let joint = physics_data.impulse_joint_set
                            .get(child.connection).ok_or("module joint does not exist")?;
                        if joint.impulses.magnitude() > 0.00012 {
                            let module: Option<AttachedModule>;
                            if let Some(Entity::AttachedModule(p_module)) =
                                entities.entities.get_mut(&child.child)
                            {
                                module = Some(p_module.clone());
                            } else {
                                warn!("attempted to detach nonexistent module");
                                continue;
                        if child.can_detach {
                            let joint = physics_data.impulse_joint_set
                                .get(child.connection).ok_or("module joint does not exist 190")?;
                            if joint.impulses.magnitude() > 0.00012 {
                                let module: Option<AttachedModule>;
                                if let Some(Entity::AttachedModule(p_module)) =
                                    entities.entities.get_mut(&child.child)
                                {
                                    module = Some(p_module.clone());
                                } else {
                                    warn!("attempted to detach nonexistent module");
                                    continue;
                                }
                                let player_id = module.as_ref().ok_or("cannot detach module that doesn't exist")?.player_id;
                                AttachedModule::detach(
                                    &mut physics_data,
                                    &mut entities,
                                    player_id,
                                    &module.ok_or("cannot detach module that doesn't exist")?,
                                );
                            }
                        }
                    }
                }
                if module.module_type == ModuleType::LandingThruster {
                    let player = entities.get_player_from_id(module.player_id).ok_or("attached module not affiliated with player")?;
                    let player_angle;
                    let player_translation;
                    {
                        let player_body = physics_data.rigid_body_set.get(player.handle).ok_or("player body does not exist")?;
                        player_angle = player_body.rotation().angle();
                        player_translation = *player_body.translation();
                    }
                    let module_body = physics_data.rigid_body_set.get_mut(module_handle).ok_or("module body does not exist")?;
                    let relative_angle = (module_body.rotation().angle() - player_angle).abs();
                    let relative_pos = module_body.translation() - player_translation;
                    let relative_pos = vector![
                        relative_pos.x.mul_add((-player_angle).cos(), -relative_pos.y * (-player_angle).sin()),
                        relative_pos.x.mul_add((-player_angle).sin(), relative_pos.y * (-player_angle).cos())
                    ];
                    let rotation = module_body.rotation().angle();
                    if player.input.up {
                        if 3.*PI/4. < relative_angle && relative_angle < 5.*PI/4. {
                            module_body.add_force_at_point(vector![
                                -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                 LATERAL_FORCE * 3. / SCALE * rotation.cos()
                            ], point![module_body.translation().x, module_body.translation().y], true);
                        }
                    }
                    if player.input.down {
                        if (0. < relative_angle && relative_angle < PI/4.)||(7.*PI/4. < relative_angle && relative_angle < 2.*PI) {
                            module_body.add_force_at_point(vector![
                                -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                 LATERAL_FORCE * 3. / SCALE * rotation.cos()
                            ], point![module_body.translation().x, module_body.translation().y], true);
                        }
                    }
                    if player.input.left {
                        if 3.*PI/4. < relative_angle && relative_angle < 5.*PI/4. {
                            if relative_pos.x > 2.4 {
                                module_body.add_force_at_point(vector![
                                    -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                     LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                        }
                        if (0. < relative_angle && relative_angle < PI/4.)||(7.*PI/4. < relative_angle && relative_angle < 2.*PI) {
                            if relative_pos.x < -2.4 {
                                module_body.add_force_at_point(vector![
                                    -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                     LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                        }
                        if PI/4. < relative_angle && relative_angle < 3.*PI/4. {
                            if relative_pos.y < -2.4 {
                                module_body.add_force_at_point(vector![
                                    -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                     LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                        }
                        if 5.*PI/4. < relative_angle && relative_angle < 7.*PI/4. {
                            if relative_pos.y > 2.4 {
                                module_body.add_force_at_point(vector![
                                    -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                     LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                            if -2.4 < relative_pos.y && relative_pos.y < 2.4 {
                                module_body.add_force_at_point(vector![
                                    -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                     LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                        }
                    }
                    if player.input.right {
                        if 3.*PI/4. < relative_angle && relative_angle < 5.*PI/4. {
                            if relative_pos.x < -2.4 {
                                module_body.add_force_at_point(vector![
                                    -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                     LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                        }
                        if (0. < relative_angle && relative_angle < PI/4.)||(7.*PI/4. < relative_angle && relative_angle < 2.*PI) {
                            if relative_pos.x > 2.4 {
                                module_body.add_force_at_point(vector![
                                    -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                     LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                        }
                        if PI/4. < relative_angle && relative_angle < 3.*PI/4. {
                            if relative_pos.y > 2.4 {
                                module_body.add_force_at_point(vector![
                                    -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                     LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                            if -2.4 < relative_pos.y && relative_pos.y < 2.4 {
                                module_body.add_force_at_point(vector![
                                     LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                    -LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                        }
                        if 5.*PI/4. < relative_angle && relative_angle < 7.*PI/4. {
                            if relative_pos.y < -2.4 {
                                module_body.add_force_at_point(vector![
                                    -LATERAL_FORCE * 3. / SCALE * rotation.sin(),
                                     LATERAL_FORCE * 3. / SCALE * rotation.cos()
                                ], point![module_body.translation().x, module_body.translation().y], true);
                            }
                            let player_id = module.as_ref().ok_or("cannot detach module that doesn't exist")?.player_id;
                            AttachedModule::detach(
                                &mut physics_data,
                                &mut entities,
                                player_id,
                                &module.ok_or("cannot detach module that doesn't exist")?,
                            );
                        }
                    }
                }


@@ 313,8 431,11 @@ pub async fn timer_main(

                for child in player.children.clone() {
                    if let Some(child) = child {
                        let joint = physics_data.impulse_joint_set
                            .get(child.connection).ok_or("module joint does not exist")?;
                        let joint = match physics_data.impulse_joint_set
                            .get(child.connection) {
                            Some(c) => c,
                            None => { warn!("module joint doesn't exist"); continue; }
                        };
                        // displays impulse on joint * 10000 so its visible, use to tune breaking
                        // force
                        //debug!("impulse: {}", joint.impulses.magnitude() * 10000.);


@@ 374,6 495,17 @@ pub async fn timer_main(
                                                        module_collider.set_shape(SharedShape::cuboid(25./SCALE, 25./SCALE));
                                                        module_collider.set_translation_wrt_parent(vector![0., 0.]);
                                                    }
                                                    PlanetType::Moon => {
                                                        attached.module_type = ModuleType::LandingThruster;
                                                        module_collider.set_shape(SharedShape::cuboid(25./SCALE, 18.75/SCALE));
                                                        module_collider.set_translation_wrt_parent(vector![0., 6.25/SCALE]);
                                                        AttachedModule::attach_new(&mut physics_data, &mut entities,
                                                                                   module_id, module.player_id,
                                                                                   &ModuleTemplate {
                                                                                       mass_properties: MassProperties::new(point![0.0, 0.0], 0.000075, 0.005),
                                                                                       module_type: ModuleType::LandingThrusterSuspension,
                                                                                   }, 2);
                                                    }
                                                    _ => {}
                                                };
                                            }


@@ 386,7 518,7 @@ pub async fn timer_main(
                                    for module in tree {
                                        if module.module_type == ModuleType::Cargo {
                                            let module_id = entities.get_id_from_attached(&module)
                                                .ok_or("module doesn't exist")?;
                                                .ok_or("attached module doesn't exist")?;
                                            let module_handle =physics_data.rigid_body_set.get(module.handle)
                                                .ok_or("attached module body does not exist")?.colliders()[0];
                                            let module_collider = physics_data.collider_set.get_mut(module_handle)


@@ 399,6 531,17 @@ pub async fn timer_main(
                                                        module_collider.set_shape(SharedShape::cuboid(25./SCALE, 25./SCALE));
                                                        module_collider.set_translation_wrt_parent(vector![0., 0.]);
                                                    }
                                                    PlanetType::Moon => {
                                                        attached.module_type = ModuleType::LandingThruster;
                                                        module_collider.set_shape(SharedShape::cuboid(25./SCALE, 18.75/SCALE));
                                                        module_collider.set_translation_wrt_parent(vector![0., 6.25/SCALE]);
                                                        AttachedModule::attach_new(&mut physics_data, &mut entities,
                                                                                   module_id, module.player_id,
                                                                                   &ModuleTemplate {
                                                                                       mass_properties: MassProperties::new(point![0.0, 0.0], 0.000075, 0.005),
                                                                                       module_type: ModuleType::LandingThrusterSuspension,
                                                                                   }, 2);
                                                    }
                                                    _ => {}
                                                };
                                            }


@@ 407,11 550,6 @@ pub async fn timer_main(
                                }
                                _ => {}
                            }
                        } else {
                            let player_handle = physics_data.collider_set.get(collider1).ok_or("player body doesn't exist")?;
                            let body_handle = player_handle.parent().ok_or("player collider not attached to body")?;
                            let player = entities.get_player_from_handle(body_handle).ok_or("player doesn't exist")?;
                            let tree = player.search_modules(&entities);
                        }
                    }
                }


@@ 529,6 667,13 @@ pub async fn timer_main(
                                        .get(module.handle)
                                        .expect("module body does not exist");
                                }
                                let children = module.children.iter().map(|c| {
                                    starkingdoms_protocol::module::Attachment {
                                        id: c.child,
                                        slot: 0,
                                        special_fields: SpecialFields::default(),
                                    }
                                }).collect();

                                starkingdoms_protocol::module::Module {
                                    module_type: module.module_type.into(),


@@ 537,6 682,7 @@ pub async fn timer_main(
                                    y: body.translation().y * SCALE,
                                    id,
                                    flags: module.flags,
                                    children,
                                    special_fields: SpecialFields::default(),
                                }
                            })


@@ 554,6 700,13 @@ pub async fn timer_main(
                                    .expect("module body does not exist");
                            }

                            let children = module.children.iter().map(|c| {
                                starkingdoms_protocol::module::Attachment {
                                    id: c.child,
                                    slot: 0,
                                    special_fields: SpecialFields::default(),
                                }
                            }).collect();
                            starkingdoms_protocol::module::Module {
                                module_type: module.module_type.into(),
                                rotation: body.rotation().angle(),


@@ 561,6 714,7 @@ pub async fn timer_main(
                                y: body.translation().y * SCALE,
                                id: *id,
                                flags: module.flags,
                                children,
                                special_fields: SpecialFields::default(),
                            }
                        })