1
+ /*
2
+ * A set of functions that makes creeps tell other creeps to get out of the way using creep memory
3
+ *
4
+ * call so creep reacts to being nudged
5
+ * Creep.giveWay() - swaps places with creep that nudged it
6
+ * Creep.giveWay(true) - moves into random available spot
7
+ * Creep.giveWay({pos: controller.pos, range: 3 }) - moves into random available spot in range of target, if none are avaiable fallbacks to random spot
8
+ */
9
+
10
+ /*
11
+ * if alwaysNudge false you have to call Creep.move with additional argument -
12
+ * creep.move(direction, true); - for creep to nudge other creeps,
13
+ * so it's not compatible with creep.moveTo
14
+ *
15
+ * if alwaysNudge is true then creeps... always nudge creeps in front of them
16
+ */
17
+ const alwaysNudge = true ;
18
+
19
+ /*
20
+ * some utils that I'm using
21
+ */
22
+ const offsetX = [ 0 , 0 , 1 , 1 , 1 , 0 , - 1 , - 1 , - 1 ] ;
23
+ const offsetY = [ 0 , - 1 , - 1 , 0 , 1 , 1 , 1 , 0 , - 1 ] ;
24
+ function getRandomDir ( ) {
25
+ return ( Math . floor ( Math . random ( ) * 8 ) + 1 ) ;
26
+ }
27
+ function getOppositeDir ( dir ) {
28
+ return ( ( dir + 3 ) % 8 + 1 ) ;
29
+ }
30
+
31
+ /**
32
+ * returns a weighted random direction from given position
33
+ * prefers empty tiles over ones with creeps
34
+ * never picks a direction that would result in hitting a wall or an obstacle structure
35
+ *
36
+ * @param {RoomPosition } pos
37
+ */
38
+ function getNudgeDirection_Random ( pos ) {
39
+ const room = Game . rooms [ pos . roomName ] ;
40
+ const terrain = Game . map . getRoomTerrain ( pos . roomName ) ;
41
+ let totalWeight = 0 ;
42
+ let dirCandidates = new Uint8Array ( 9 ) ;
43
+ for ( let dir = TOP ; dir <= TOP_LEFT ; ++ dir ) {
44
+ let posX = pos . x + offsetX [ dir ] ;
45
+ let posY = pos . y + offsetY [ dir ] ;
46
+ if ( posX < 1 || posX > 48 || posY < 1 || posY > 48 )
47
+ continue ;
48
+ if ( ( terrain . get ( posX , posY ) & TERRAIN_MASK_WALL ) > 0 )
49
+ continue ;
50
+ if ( room . lookForAt ( LOOK_STRUCTURES , posX , posY ) . find ( s => OBSTACLE_OBJECT_TYPES . includes ( s . structureType ) ) )
51
+ continue ;
52
+
53
+ const hasCreeps = room . lookForAt ( LOOK_CREEPS , posX , posY ) . length > 0 ;
54
+ const addWeight = hasCreeps ? 1 : 2 ;
55
+ dirCandidates [ dir ] += addWeight ;
56
+ totalWeight += dirCandidates [ dir ] ;
57
+ }
58
+
59
+ let sum = 0 ;
60
+ let rnd = _ . random ( 1 , totalWeight , false ) ;
61
+ for ( let dir = TOP ; dir <= TOP_LEFT ; ++ dir ) {
62
+ if ( dirCandidates [ dir ] > 0 ) {
63
+ sum += dirCandidates [ dir ] ;
64
+ if ( rnd <= sum ) {
65
+ return dir ;
66
+ }
67
+ }
68
+ }
69
+
70
+ // this should never happen, unless creep is spawned into a corner
71
+ // or structure is built next to it and seals the only path out
72
+ return getRandomDir ( ) ;
73
+ }
74
+
75
+ /**
76
+ * returns a weighted random direction from given position
77
+ * tries to stay in targets range, if it's impossible then fallbacks to random direction
78
+ * prefers empty tiles over ones with creeps
79
+ * never picks a direction that would result in hitting a wall or an obstacle structure
80
+ *
81
+ * @param {RoomPosition } pos
82
+ * @param {Object } target
83
+ * @param {RoomPosition } target.pos
84
+ * @param {number } target.range
85
+ */
86
+ function getNudgeDirection_KeepRange ( pos , target ) {
87
+ const room = Game . rooms [ pos . roomName ] ;
88
+ const terrain = Game . map . getRoomTerrain ( pos . roomName ) ;
89
+ let keepRangeTotalWeight = 0 ;
90
+ let keepRangeDirCandidates = new Uint8Array ( 9 ) ;
91
+ let randomTotalWeight = 0 ;
92
+ let randomDirCandidates = new Uint8Array ( 9 ) ;
93
+ for ( let dir = TOP ; dir <= TOP_LEFT ; ++ dir ) {
94
+ let posX = pos . x + offsetX [ dir ] ;
95
+ let posY = pos . y + offsetY [ dir ] ;
96
+ if ( posX < 1 || posX > 48 || posY < 1 || posY > 48 )
97
+ continue ;
98
+ if ( ( terrain . get ( posX , posY ) & TERRAIN_MASK_WALL ) > 0 )
99
+ continue ;
100
+ if ( room . lookForAt ( LOOK_STRUCTURES , posX , posY ) . find ( s => OBSTACLE_OBJECT_TYPES . includes ( s . structureType ) ) )
101
+ continue ;
102
+
103
+ const hasCreeps = room . lookForAt ( LOOK_CREEPS , posX , posY ) . length > 0 ;
104
+ const addWeight = hasCreeps ? 1 : 2 ;
105
+ randomDirCandidates [ dir ] += addWeight ;
106
+ if ( target . pos . inRangeTo ( posX , posY , target . range ) )
107
+ keepRangeDirCandidates [ dir ] += addWeight ;
108
+ keepRangeTotalWeight += keepRangeDirCandidates [ dir ] ;
109
+ randomTotalWeight += randomDirCandidates [ dir ] ;
110
+ }
111
+
112
+ const dirCandidates = keepRangeTotalWeight > 0 ? keepRangeDirCandidates : randomDirCandidates ;
113
+ const totalWeight = keepRangeTotalWeight > 0 ? keepRangeTotalWeight : randomTotalWeight ;
114
+ let sum = 0 ;
115
+ if ( totalWeight > 0 ) {
116
+ let rnd = _ . random ( 1 , totalWeight , false ) ;
117
+ for ( let dir = TOP ; dir <= TOP_LEFT ; ++ dir ) {
118
+ if ( dirCandidates [ dir ] > 0 ) {
119
+ sum += dirCandidates [ dir ] ;
120
+ if ( rnd <= sum ) {
121
+ return dir ;
122
+ }
123
+ }
124
+ }
125
+ }
126
+
127
+ // this should never happen, unless creep is spawned into a corner
128
+ // or structure is built next to it and seals the only path out
129
+ return getRandomDir ( ) ;
130
+ }
131
+
132
+ /**
133
+ * a nudge
134
+ *
135
+ * @param {RoomPosition } pos - a nudge origin point
136
+ * @param {DirectionConstant } direction
137
+ */
138
+ function excuseMe ( pos , direction ) {
139
+ const nextX = pos . x + offsetX [ direction ] ;
140
+ const nextY = pos . y + offsetY [ direction ] ;
141
+ if ( nextX > 49 || nextX < 0 || nextY > 49 || nextY < 0 )
142
+ return ;
143
+
144
+ const room = Game . rooms [ pos . roomName ] ;
145
+ const creeps = room . lookForAt ( LOOK_CREEPS , nextX , nextY ) ;
146
+ if ( creeps . length > 0 && creeps [ 0 ] . my )
147
+ creeps [ 0 ] . memory . excuseMe = getOppositeDir ( direction ) ;
148
+ const powerCreeps = room . lookForAt ( LOOK_POWER_CREEPS , nextX , nextY ) ;
149
+ if ( powerCreeps . length > 0 && powerCreeps [ 0 ] . my )
150
+ powerCreeps [ 0 ] . memory . excuseMe = getOppositeDir ( direction ) ;
151
+ }
152
+
153
+ /*
154
+ *
155
+ */
156
+ let creepsThatTriedToMove = { } ;
157
+ const move = Creep . prototype . move ;
158
+ Creep . prototype . move = function ( direction , nudge ) {
159
+ if ( ( alwaysNudge || nudge ) && _ . isNumber ( direction ) )
160
+ excuseMe ( this . pos , direction ) ;
161
+ creepsThatTriedToMove [ this . name ] = this . pos ;
162
+ return move . call ( this , direction ) ;
163
+ } ;
164
+
165
+ /*
166
+ * call this on creeps that should react to being nudged
167
+ */
168
+ function giveWay ( creep , arg ) {
169
+ if ( creep . memory . excuseMe ) {
170
+ if ( ! arg )
171
+ creep . move ( creep . memory . excuseMe , true ) ;
172
+ else if ( typeof arg === 'object' )
173
+ creep . move ( getNudgeDirection_KeepRange ( creep . pos , arg ) , true ) ;
174
+ else
175
+ creep . move ( getNudgeDirection_Random ( creep . pos ) , true ) ;
176
+ }
177
+ }
178
+ Creep . prototype . giveWay = function ( arg ) {
179
+ giveWay ( this , arg ) ;
180
+ } ;
181
+ PowerCreep . prototype . giveWay = function ( arg ) {
182
+ giveWay ( this , arg ) ;
183
+ } ;
184
+
185
+ /*
186
+ * clears nudges from memory of creeps that moved
187
+ * call on tick start
188
+ */
189
+ function clearNudges ( ) {
190
+ for ( let creepName in creepsThatTriedToMove ) {
191
+ const creep = Game . creeps [ creepName ] ;
192
+ const powerCreep = Game . powerCreeps [ creepName ] ;
193
+ const prevPos = creepsThatTriedToMove [ creepName ] ;
194
+ if ( ( ! creep || ! creep . pos . isEqualTo ( prevPos ) ) && ( ! powerCreep || ! powerCreep . pos . isEqualTo ( prevPos ) ) ) {
195
+ const creepMemory = Memory . creeps [ creepName ] ;
196
+ if ( creepMemory )
197
+ creepMemory . excuseMe = undefined ;
198
+ const powerCreepMemory = Memory . powerCreeps [ creepName ] ;
199
+ if ( powerCreepMemory )
200
+ powerCreepMemory . excuseMe = undefined ;
201
+ delete creepsThatTriedToMove [ creepName ] ;
202
+ }
203
+ }
204
+ }
205
+
206
+ module . exports = {
207
+ clearNudges : clearNudges
208
+ } ;
0 commit comments