@@ -81,6 +81,12 @@ func (e *Entity) ApplyUpdate(reader *bit.BitReader) {
8181
8282 for _ , idx := range * updatedPropIndices {
8383 propDecoder .decodeProp (& e .props [idx ], reader )
84+ }
85+
86+ // Fire update only after all properties have been updated
87+ // That way data that is made up of multiple properties won't be wrong
88+ // For instance the entity's position
89+ for _ , idx := range * updatedPropIndices {
8490 e .props [idx ].firePropertyUpdate ()
8591 }
8692
@@ -147,15 +153,22 @@ func (e *Entity) applyBaseline(baseline map[int]PropertyValue) {
147153 }
148154}
149155
150- const maxCoordInt = 16384
156+ const (
157+ maxCoordInt = 16384
158+ propCellBits = "m_cellbits"
159+ propCellX = "m_cellX"
160+ propCellY = "m_cellY"
161+ propCellZ = "m_cellZ"
162+ propVecOrigin = "m_vecOrigin"
163+ )
151164
152165// Position returns the entity's position in world coordinates.
153166func (e * Entity ) Position () r3.Vector {
154- cellWidth := 1 << uint (e .FindProperty ("m_cellbits" ).value .IntVal )
155- cellX := e .FindProperty ("m_cellX" ).value .IntVal
156- cellY := e .FindProperty ("m_cellY" ).value .IntVal
157- cellZ := e .FindProperty ("m_cellZ" ).value .IntVal
158- offset := e .FindProperty ("m_vecOrigin" ).value .VectorVal
167+ cellWidth := 1 << uint (e .FindProperty (propCellBits ).value .IntVal )
168+ cellX := e .FindProperty (propCellX ).value .IntVal
169+ cellY := e .FindProperty (propCellY ).value .IntVal
170+ cellZ := e .FindProperty (propCellZ ).value .IntVal
171+ offset := e .FindProperty (propVecOrigin ).value .VectorVal
159172
160173 return r3.Vector {
161174 X : coordFromCell (cellX , cellWidth , offset .X ),
@@ -164,6 +177,38 @@ func (e *Entity) Position() r3.Vector {
164177 }
165178}
166179
180+ // OnPositionUpdate registers a handler for the entity's position update.
181+ // The handler is called with the new position every time a position-relevant property is updated.
182+ // This does NOT work for players as their position is calculated differently.
183+ //
184+ // See also Position()
185+ func (e * Entity ) OnPositionUpdate (h func (pos r3.Vector )) {
186+ pos := new (r3.Vector )
187+ firePosUpdate := func (PropertyValue ) {
188+ newPos := e .Position ()
189+ if * pos != newPos {
190+ h (newPos )
191+ * pos = newPos
192+ }
193+ }
194+
195+ e .FindProperty (propCellX ).OnUpdate (firePosUpdate )
196+ e .FindProperty (propCellY ).OnUpdate (firePosUpdate )
197+ e .FindProperty (propCellZ ).OnUpdate (firePosUpdate )
198+ e .FindProperty (propVecOrigin ).OnUpdate (firePosUpdate )
199+ }
200+
201+ // BindPosition binds the entity's position to a pointer variable.
202+ // The pointer is updated every time a position-relevant property is updated.
203+ // This does NOT work for players as their position is calculated differently.
204+ //
205+ // See also OnPositionUpdate()
206+ func (e * Entity ) BindPosition (pos * r3.Vector ) {
207+ e .OnPositionUpdate (func (newPos r3.Vector ) {
208+ * pos = newPos
209+ })
210+ }
211+
167212// Returns a coordinate from a cell + offset
168213func coordFromCell (cell , cellWidth int , offset float64 ) float64 {
169214 return float64 (cell * cellWidth - maxCoordInt ) + offset
0 commit comments