I’d do it like multitile doors: by handling the multi-tileness where required.
For example, when calculating total gas storage, you don’t need to do anything, but for draining 5 gas you’d have to make sure all the neighbors have similar amount of gas after that.
Remember that finding neighbors is moderately expensive - it requires you to go through all parts of the vehicle every single time you do it. So it is pretty important that you don’t use the “part_at” type functions to find each of 4 possible neighbors, but rather build a map of parts that could be neighbors once per turn/calculation and then operate on that. At least if you want to access the multi-tile types often (for example, want multi-tile gas tanks).
Remembering the list of neighbors could work, but forget about the idea of updating it from the current state - you’ll have to recalculate it from scratch every time you install/wreck a part. This won’t be very expensive, as it parts getting installed/wrecked doesn’t happen a lot.
If you want to keep all the multi-tile components rather than just neighbors, calculate the list at once and then copy it to all components. ie. go through the entire vehicle, build all lists of parts that are components of multi-tile “megaparts”, then copy those lists to components.
To sum up:
[ul][li]Keep a list of neighbors. List has to be of part ids (“part number 30”) rather than part positions[/li]
[li]Make sure you recalculate the list when ids change (part is added/removed)[/li]
[li]Avoid part_at functions for finding neighbors, they are slow. Instead build a map of same-type parts. Map can be point->id[/li][/ul]