Skip to content

Commit 2d9876b

Browse files
committed
common: replace properties with getters (part 1)
1 parent bb27e1d commit 2d9876b

File tree

14 files changed

+170
-142
lines changed

14 files changed

+170
-142
lines changed

examples/nade-trajectories/nade_trajectories.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func drawInfernos(gc *draw2dimg.GraphicContext, infernos []*common.Inferno) {
138138
// Calculate hulls
139139
hulls := make([][]r2.Point, len(infernos))
140140
for i := range infernos {
141-
hulls[i] = infernos[i].ConvexHull2D()
141+
hulls[i] = infernos[i].Fires().ConvexHull2D()
142142
}
143143

144144
for _, hull := range hulls {

pkg/demoinfocs/common/common.go

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"time"
77

88
"github.com/golang/geo/r3"
9+
10+
st "github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/sendtables"
911
)
1012

1113
// Team is the type for the various TeamXYZ constants.
@@ -57,41 +59,24 @@ func (h DemoHeader) FrameTime() time.Duration {
5759
return time.Duration(h.PlaybackTime.Nanoseconds() / int64(h.PlaybackFrames))
5860
}
5961

60-
// TickRate returns the tick-rate the server ran on during the game.
61-
// Deprecated: this function might return 0 in some cases (corrupt demo headers), use Parser.TickRate() instead.
62-
// VolvoPlx128TixKTnxBye
63-
func (h DemoHeader) TickRate() float64 {
64-
if h.PlaybackTime == 0 {
65-
return 0
66-
}
67-
68-
return float64(h.PlaybackTicks) / h.PlaybackTime.Seconds()
69-
}
70-
71-
// TickTime returns the time a single tick takes in seconds.
72-
// Deprecated: this function might return 0 in some cases (corrupt demo headers), use Parser.TickTime() instead.
73-
func (h DemoHeader) TickTime() time.Duration {
74-
if h.PlaybackTicks == 0 {
75-
return 0
76-
}
77-
78-
return time.Duration(h.PlaybackTime.Nanoseconds() / int64(h.PlaybackTicks))
79-
}
80-
8162
// GrenadeProjectile is a grenade thrown intentionally by a player. It is used to track grenade projectile
8263
// positions between the time at which they are thrown and until they detonate.
8364
type GrenadeProjectile struct {
84-
EntityID int
65+
Entity st.IEntity
8566
WeaponInstance *Equipment
86-
Thrower *Player // Always seems to be the same as Owner, even if the grenade was picked up
87-
Owner *Player // Always seems to be the same as Thrower, even if the grenade was picked up
88-
Position r3.Vector
67+
Thrower *Player // Always seems to be the same as Owner, even if the grenade was picked up
68+
Owner *Player // Always seems to be the same as Thrower, even if the grenade was picked up
8969
Trajectory []r3.Vector // List of all known locations of the grenade up to the current point
9070

9171
// uniqueID is used to distinguish different grenades (which potentially have the same, reused entityID) from each other.
9272
uniqueID int64
9373
}
9474

75+
// Position returns the current position of the grenade projectile in world coordinates.
76+
func (g GrenadeProjectile) Position() r3.Vector {
77+
return g.Entity.Position()
78+
}
79+
9580
// UniqueID returns the unique id of the grenade.
9681
// The unique id is a random int generated internally by this library and can be used to differentiate
9782
// grenades from each other. This is needed because demo-files reuse entity ids.

pkg/demoinfocs/common/common_test.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ func TestDemoHeader(t *testing.T) {
3737

3838
assert.Equal(t, float64(64), header.FrameRate(), "FrameRate should be 64")
3939
assert.Equal(t, time.Second/64, header.FrameTime(), "FrameTime should be 1/64 sec")
40-
41-
assert.Equal(t, float64(128.0), header.TickRate(), "TickRate should be 128")
42-
assert.Equal(t, time.Second/128, header.TickTime(), "TickTime should be 1/128")
4340
}
4441

4542
func TestDemoHeader_FrameRate_PlaybackTime_Zero(t *testing.T) {
@@ -50,14 +47,6 @@ func TestDemoHeader_FrameTime_PlaybackFrames_Zero(t *testing.T) {
5047
assert.Zero(t, DemoHeader{}.FrameTime())
5148
}
5249

53-
func TestDemoHeader_TickRate_PlaybackTicks_Zero(t *testing.T) {
54-
assert.Zero(t, DemoHeader{}.TickTime())
55-
}
56-
57-
func TestDemoHeader_TickTime_PlaybackTime_Zero(t *testing.T) {
58-
assert.Zero(t, DemoHeader{}.TickRate())
59-
}
60-
6150
func TestTeamState_Team(t *testing.T) {
6251
assert.Equal(t, TeamTerrorists, NewTeamState(TeamTerrorists, nil).Team())
6352
assert.Equal(t, TeamCounterTerrorists, NewTeamState(TeamCounterTerrorists, nil).Team())
@@ -144,6 +133,8 @@ func entityWithProperty(propName string, value st.PropertyValue) st.IEntity {
144133
prop.On("Value").Return(value)
145134

146135
entity.On("FindProperty", propName).Return(prop)
136+
entity.On("PropertyValue", propName).Return(prop, true)
137+
entity.On("PropertyValueMust", propName).Return(value)
147138

148139
return entity
149140
}

pkg/demoinfocs/common/equipment.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -285,13 +285,10 @@ const (
285285
// Equipment is a weapon / piece of equipment belonging to a player.
286286
// This also includes the skin and some additional data.
287287
type Equipment struct {
288-
EntityID int // ID of the game entity
289-
Entity st.IEntity // The entity instance
290288
Type EquipmentType // The type of weapon which the equipment instantiates.
289+
Entity st.IEntity // The game entity instance
291290
Owner *Player // The player carrying the equipment, not necessarily the buyer.
292-
AmmoType int // TODO: Remove this? doesn't seem applicable to CS:GO
293291
OriginalString string // E.g. 'models/weapons/w_rif_m4a1_s.mdl'. Used internally to differentiate alternative weapons (M4A4 / M4A1-S etc.).
294-
ZoomLevel ZoomLevel // How far the player has zoomed in on the weapon.
295292

296293
uniqueID int64
297294
}
@@ -330,14 +327,26 @@ func (e Equipment) AmmoInMagazine() int {
330327
return val.IntVal
331328
}
332329

330+
// AmmoType returns the weapon's ammo type, mostly (only?) relevant for grenades.
331+
func (e Equipment) AmmoType() int {
332+
return e.Entity.PropertyValueMust("LocalWeaponData.m_iPrimaryAmmoType").IntVal
333+
}
334+
335+
// ZoomLevel returns how far the player has zoomed in on the weapon.
336+
// Only weapons with scopes have a valid zoom level.
337+
func (e Equipment) ZoomLevel() ZoomLevel {
338+
val, _ := e.Entity.PropertyValue("LocalWeaponData.m_iPrimaryAmmoType")
339+
return ZoomLevel(val.IntVal)
340+
}
341+
333342
// AmmoReserve returns the ammo left available for reloading.
334343
// Returns CWeaponCSBase.m_iPrimaryReserveAmmoCount for most weapons and 'Owner.AmmoLeft[AmmoType] - 1' for grenades.
335-
// Use AmmoInMagazine2() + AmmoReserve2() to quickly get the amount of grenades a player owns.
344+
// Use AmmoInMagazine() + AmmoReserve() to quickly get the amount of grenades a player owns.
336345
func (e Equipment) AmmoReserve() int {
337346
if e.Class() == EqClassGrenade {
338347
if e.Owner != nil {
339348
// minus one for 'InMagazine'
340-
return e.Owner.AmmoLeft[e.AmmoType] - 1
349+
return e.Owner.AmmoLeft[e.AmmoType()] - 1
341350
}
342351

343352
return 0

pkg/demoinfocs/common/equipment_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"testing"
55

66
"github.com/stretchr/testify/assert"
7+
8+
st "github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/sendtables"
79
)
810

911
func TestEquipmentElement_Class(t *testing.T) {
@@ -47,10 +49,12 @@ func TestEquipment_AmmoInMagazine_Grenade(t *testing.T) {
4749
func TestEquipment_AmmoReserve_Grenade(t *testing.T) {
4850
owner := new(Player)
4951
owner.AmmoLeft[1] = 2
52+
53+
entity := entityWithProperty("LocalWeaponData.m_iPrimaryAmmoType", st.PropertyValue{IntVal: 1})
5054
wep := &Equipment{
51-
Type: EqFlash,
52-
Owner: owner,
53-
AmmoType: 1,
55+
Type: EqFlash,
56+
Owner: owner,
57+
Entity: entity,
5458
}
5559

5660
assert.Equal(t, 1, wep.AmmoReserve())

pkg/demoinfocs/common/inferno.go

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package common
22

33
import (
4+
"fmt"
45
"math/rand"
56
"sort"
67

@@ -16,9 +17,7 @@ import (
1617
//
1718
// See also: Inferno.Active() and Fire.IsBurning
1819
type Inferno struct {
19-
Entity st.IEntity
20-
EntityID int // Same as Entity.ID(), use Entity.ID() instead
21-
Fires []*Fire
20+
Entity st.IEntity
2221

2322
// uniqueID is used to distinguish different infernos (which potentially have the same, reused entityID) from each other.
2423
uniqueID int64
@@ -32,36 +31,65 @@ type Fire struct {
3231
IsBurning bool
3332
}
3433

34+
// Fires is a collection of fires that provides utility functions for things like calculation of 2D & 3D convex hulls.
35+
type Fires struct {
36+
s []Fire
37+
}
38+
3539
// UniqueID returns the unique id of the inferno.
3640
// The unique id is a random int generated internally by this library and can be used to differentiate
3741
// infernos from each other. This is needed because demo-files reuse entity ids.
3842
func (inf Inferno) UniqueID() int64 {
3943
return inf.uniqueID
4044
}
4145

42-
// Active returns an Inferno containing only the active fires of the original.
43-
// The returned Inferno will have the same Unique-ID as the original.
44-
func (inf Inferno) Active() Inferno {
45-
res := Inferno{
46-
uniqueID: inf.uniqueID,
46+
// Fires returns all fires (past + present).
47+
// Some are currently active and some have extinguished (see Fire.IsBurning).
48+
func (inf Inferno) Fires() Fires {
49+
entity := inf.Entity
50+
origin := entity.Position()
51+
52+
var fires []Fire
53+
nFires := entity.PropertyValueMust("m_fireCount").IntVal
54+
55+
for i := 0; i < nFires; i++ {
56+
iStr := fmt.Sprintf("%03d", i)
57+
offset := r3.Vector{
58+
X: float64(entity.PropertyValueMust("m_fireXDelta." + iStr).IntVal),
59+
Y: float64(entity.PropertyValueMust("m_fireYDelta." + iStr).IntVal),
60+
Z: float64(entity.PropertyValueMust("m_fireZDelta." + iStr).IntVal),
61+
}
62+
63+
fire := Fire{
64+
Vector: origin.Add(offset),
65+
IsBurning: entity.PropertyValueMust("m_bFireIsBurning."+iStr).IntVal == 1,
66+
}
67+
68+
fires = append(fires, fire)
4769
}
4870

49-
res.Fires = make([]*Fire, 0, len(inf.Fires))
71+
return Fires{s: fires}
72+
}
73+
74+
// Active returns all currently active fires (only Fire.IsBurning == true).
75+
func (f Fires) Active() Fires {
76+
allFires := f.s
77+
active := make([]Fire, 0, len(allFires))
5078

51-
for _, f := range inf.Fires {
79+
for _, f := range allFires {
5280
if f.IsBurning {
53-
res.Fires = append(res.Fires, f)
81+
active = append(active, f)
5482
}
5583
}
5684

57-
return res
85+
return Fires{s: active}
5886
}
5987

6088
// ConvexHull2D returns clockwise sorted corner points making up the 2D convex hull of all the fires in the inferno.
6189
// Useful for drawing on 2D maps.
62-
func (inf Inferno) ConvexHull2D() []r2.Point {
63-
pointCloud := make([]r3.Vector, len(inf.Fires))
64-
for i, f := range inf.Fires {
90+
func (f Fires) ConvexHull2D() []r2.Point {
91+
pointCloud := make([]r3.Vector, len(f.s))
92+
for i, f := range f.s {
6593
pointCloud[i] = f.Vector
6694
pointCloud[i].Z = 0
6795
}
@@ -135,25 +163,16 @@ func sortPointsClockwise(points []r2.Point) {
135163
}
136164

137165
// ConvexHull3D returns the 3D convex hull of all the fires in the inferno.
138-
func (inf Inferno) ConvexHull3D() quickhull.ConvexHull {
139-
pointCloud := make([]r3.Vector, len(inf.Fires))
166+
func (f Fires) ConvexHull3D() quickhull.ConvexHull {
167+
pointCloud := make([]r3.Vector, len(f.s))
140168

141-
for i, f := range inf.Fires {
169+
for i, f := range f.s {
142170
pointCloud[i] = f.Vector
143171
}
144172

145173
return convexHull(pointCloud)
146174
}
147175

148-
// Owner returns the player who threw the fire grenade.
149-
// Could be nil if the player disconnected after throwing it.
150-
//
151-
// Deprecated: Owner() exists for historical compatibility
152-
// and should not be used. Use Thrower() instead.
153-
func (inf Inferno) Owner() *Player {
154-
return inf.Thrower()
155-
}
156-
157176
// Thrower returns the player who threw the fire grenade.
158177
// Could be nil if the player disconnected after throwing it.
159178
func (inf Inferno) Thrower() *Player {
@@ -170,7 +189,6 @@ func convexHull(pointCloud []r3.Vector) quickhull.ConvexHull {
170189
func NewInferno(demoInfoProvider demoInfoProvider, entity st.IEntity) *Inferno {
171190
return &Inferno{
172191
Entity: entity,
173-
EntityID: entity.ID(),
174192
uniqueID: rand.Int63(),
175193
demoInfoProvider: demoInfoProvider,
176194
}

pkg/demoinfocs/common/inferno_test.go

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,21 @@ func TestInferno_UniqueID(t *testing.T) {
1515
assert.NotEqual(t, NewInferno(nil, entity).UniqueID(), NewInferno(nil, entity).UniqueID(), "UniqueIDs of different infernos should be different")
1616
}
1717

18+
// FIXME: Inferno.Fires test
19+
1820
func TestInferno_Active(t *testing.T) {
19-
inf := Inferno{
20-
Fires: []*Fire{
21+
inf := Fires{
22+
s: []Fire{
2123
{
2224
IsBurning: false,
2325
Vector: r3.Vector{X: 1, Y: 2, Z: 3},
2426
},
2527
},
2628
}
2729

28-
assert.Empty(t, inf.Active().Fires, "Inferno should have no active fires")
30+
assert.Empty(t, inf.Active().s, "Inferno should have no active fires")
2931

30-
activeFires := []*Fire{
32+
activeFires := []Fire{
3133
{
3234
IsBurning: true,
3335
Vector: r3.Vector{X: 4, Y: 5, Z: 6},
@@ -37,9 +39,9 @@ func TestInferno_Active(t *testing.T) {
3739
Vector: r3.Vector{X: 7, Y: 8, Z: 9},
3840
},
3941
}
40-
inf.Fires = append(inf.Fires, activeFires...)
42+
inf.s = append(inf.s, activeFires...)
4143

42-
assert.Equal(t, activeFires, inf.Active().Fires, "Active inferno should contain active fires")
44+
assert.Equal(t, activeFires, inf.Active().s, "Active inferno should contain active fires")
4345
}
4446

4547
func TestInferno_ConvexHull2D(t *testing.T) {
@@ -52,8 +54,8 @@ func TestInferno_ConvexHull2D(t *testing.T) {
5254
// / \
5355
// A - - - - - - - B
5456
//
55-
inf := Inferno{
56-
Fires: []*Fire{
57+
inf := Fires{
58+
s: []Fire{
5759
{
5860
Vector: r3.Vector{X: 1, Y: 2, Z: 3},
5961
},
@@ -83,8 +85,8 @@ func TestInferno_ConvexHull2D(t *testing.T) {
8385

8486
// Just check that all fires are passed to quickhull.ConvexHull()
8587
func TestInferno_ConvexHull3D(t *testing.T) {
86-
inf := Inferno{
87-
Fires: []*Fire{
88+
inf := Fires{
89+
s: []Fire{
8890
{
8991
Vector: r3.Vector{X: 1, Y: 2, Z: 3},
9092
},
@@ -110,17 +112,6 @@ func TestInferno_ConvexHull3D(t *testing.T) {
110112
assert.ElementsMatch(t, expectedHull, inf.ConvexHull3D().Vertices, "ConvexHull3D should contain all fire locations")
111113
}
112114

113-
func TestInferno_Owner(t *testing.T) {
114-
entity := entityWithProperty("m_hOwnerEntity", st.PropertyValue{IntVal: 1})
115-
116-
player := new(Player)
117-
provider := demoInfoProviderMock{
118-
playersByHandle: map[int]*Player{1: player},
119-
}
120-
121-
assert.Equal(t, player, NewInferno(provider, entity).Owner())
122-
}
123-
124115
func TestInferno_Thrower(t *testing.T) {
125116
entity := entityWithProperty("m_hOwnerEntity", st.PropertyValue{IntVal: 1})
126117

0 commit comments

Comments
 (0)