Id: To index
Original: Legend
Status:
Mutant: Show

Testcases to display

Filter by kind

Filter by status

1:
#
include
 
"game.h"
2:
3:
#
include
 
"event.h"
4:
5:
#
include
 
<
iostream
>
6:
7:
Game
::
Game
(
Window
&
 
window
)
8:    
:
 
window
(
window
)
,
 
mobSystem_
(
*
this
)
,
 
physicsSystem_
(
*
this
)
,
 
renderSystem_
(
*
this
)
,
9:      
groundTiles_
(
worldBounds
.
width
,
 
worldBounds
.
height
,
 
'.'
)
 
{
}
10:
11:
void
 
Game
::
setup
(
)
 
{
12:    
const
 
auto
&
 
b
 
=
 
worldBounds
;
13:
14:    
// Create player
15:    
auto
&
 
playerMob
 
=
 
createMob
(
MobType
::
Player
,
 
{
0
,
 
0
}
)
;
16:    
player
 
=
 
playerMob
.
entity
;
17:    
cameraTarget
 
=
 
playerMob
.
position
;
18:    
cameraPosition
 
=
 
playerMob
.
position
;
19:
20:    
// Setup terrain
21:    
groundTiles_
.
fill
(
'.'
)
;
22:    
for
 
(
int
 
x
 
=
 
b
.
left
;
 
x
 
<
 
b
.
left
 
+
 
b
.
width
;
 
x
++
)
 
{
23:        
for
 
(
int
 
y
 
=
 
b
.
top
 
-
 
b
.
height
 
+
 
1
;
 
y
 
<=
 
b
.
top
;
 
y
++
)
 
{
24:            
if
 
(
randInt
(
0
,
 
6
)
 
==
 
0
)
 
{
25:                
groundTile
(
{
x
,
 
y
}
)
 
=
 
choose
(
{
','
,
 
'_'
,
 
' '
}
)
;
26:            
}
27:        
}
28:    
}
29:
30:    
// Populate world with mobs
31:    
int
 
numMobs
 
=
 
(
int
)
(
0.5
 
*
 
sqrt
(
b
.
width
 
*
 
b
.
height
)
)
;
32:    
for
 
(
int
 
i
 
=
 
0
;
 
i
 
<
 
numMobs
;
 
i
++
)
 
{
33:        
MobType
 
type
 
=
 
choose
(
{
MobType
::
Rabbit
,
 
MobType
::
OrcStrong
,
 
MobType
::
Snake
}
)
;
34:        
vec2i
 
pos
{
randInt
(
b
.
left
,
 
b
.
left
 
+
 
b
.
width
 
-
 
1
)
,
 
randInt
(
b
.
top
 
-
 
b
.
height
 
+
 
1
,
 
b
.
top
)
}
;
35:        
createMob
(
type
,
 
pos
)
;
36:
37:        
if
 
(
i
 
%
 
32
 
==
 
0
)
38:            
sync
(
)
;
39:    
}
40:
41:    
// Mob-less sprites
42:    
for
 
(
int
 
i
 
=
 
0
;
 
i
 
<
 
numMobs
 
/
 
2
;
 
i
++
)
 
{
43:        
vec2i
 
pos
{
randInt
(
b
.
left
,
 
b
.
left
 
+
 
b
.
width
 
-
 
1
)
,
 
randInt
(
b
.
top
 
-
 
b
.
height
 
+
 
1
,
 
b
.
top
)
}
;
44:        
if
 
(
randInt
(
0
,
 
2
)
 
!=
 
0
)
 
{
45:            
createSprite
(
"vV"
,
 
true
,
 
6
,
 
TB_MAGENTA
,
 
TB_BLACK
,
 
pos
,
 
RenderLayer
::
GroundCover
)
;
46:        
}
 
else
 
if
 
(
randInt
(
0
,
 
1
)
 
==
 
0
)
 
{
47:            
createSprite
(
"|/-\\"
,
 
true
,
 
2
,
 
TB_YELLOW
,
 
TB_BLACK
,
 
pos
,
 
RenderLayer
::
GroundCover
)
;
48:        
}
 
else
 
{
49:            
createSprite
(
"Xx"
,
 
true
,
 
1
,
 
TB_BLUE
,
 
TB_BLACK
,
 
pos
,
 
RenderLayer
::
GroundCover
)
;
50:        
}
51:
52:        
if
 
(
i
 
%
 
32
 
==
 
0
)
53:            
sync
(
)
;
54:    
}
55:
56:    
sync
(
)
;
57:
}
58:
59:
bool
 
Game
::
update
(
)
 
{
60:    
handleInput
(
)
;
61:    
updateCamera
(
)
;
 
// NB: Outside of world update
62:
63:    
// Use this to slow down the world update
64:    
const
 
int
 
subTicksPerTick
 
=
 
2
;
65:    
if
 
(
--
subTick_
 
<=
 
0
)
 
{
66:        
subTick_
 
=
 
subTicksPerTick
;
67:
68:        
if
 
(
freezeTimer
 
>
 
0
)
69:            
freezeTimer
--
;
70:        
bool
 
updateWorld
 
=
 
(
freezeTimer
 
==
 
0
)
;
71:
72:        
if
 
(
updateWorld
)
 
{
73:            
updatePlayer
(
)
;
74:
75:            
for
 
(
auto
*
 
sys
 
:
 
systems_
)
 
{
76:                
sys
->
update
(
)
;
77:            
}
78:
79:            
for
 
(
auto
&
 
e
 
:
 
entities
.
values
(
)
)
 
{
80:                
e
.
age
++
;
81:                
if
 
(/* e.life > 0 && */truefalse
e
.
life
 
>
 
0
 
&&||/* && e.age >= e.life */
 
e
.
age
 
>=
 
e
.
life
)
 
{
82:                    
queueEvent
(
EvRemove
{
e
.
id
}
)
;
83:                
}
84:            
}
85:
86:            
// Dirt system
87:            
for
 
(
auto
&
 
c
 
:
 
groundTiles_
.
data
(
)
)
 
{
88:                
// Roughen flat ground
89:                
if
 
(/* c == '_' && */truefalse
c
 
==
 
'_'
 
&&||/* && randInt(0, 60) == 0 */
 
randInt
(
0
,
 
60
)
 
==
 
0
)
90:                    
c
 
=
 
'.'
;
91:            
}
92:        
}
93:
94:        
// Events
95:        
auto
&
 
events
 
=
 
events_
[
eventsIndex_
]
;
96:        
eventsIndex_
 
=
 
1
 
-
 
eventsIndex_
;
 
// toggle buffer
97:        
std
::
vector
<
ident
>
 
remove
;
98:
99:        
for
 
(
const
 
EvAny
&
 
any
 
:
 
events
)
 
{
100:            
const
 
bool
 
logEvents
 
=
 
false
;
101:            
if
 
(
logEvents
)
 
{
102:                
log
(
to_string
(
any
)
)
;
103:            
}
104:
105:            
if
 
(
any
.
is
<
EvRemove
>
(
)
)
 
{
106:                
const
 
auto
&
 
ev
 
=
 
any
.
get
<
EvRemove
>
(
)
;
107:                
remove
.
push_back
(
{
ev
.
entity
}
)
;
108:            
}
 
else
 
if
 
(
any
.
is
<
EvKillMob
>
(
)
)
 
{
109:                
const
 
auto
&
 
ev
 
=
 
any
.
get
<
EvKillMob
>
(
)
;
110:                
Mob
&
 
mob
 
=
 
mobs
[
ev
.
who
]
;
111:                
auto
&
 
e
 
=
 
entities
[
mob
.
entity
]
;
112:                
auto
&
 
sprite
 
=
 
sprites
[
e
.
sprite
]
;
113:                
queueEvent
(
EvRemove
{
mob
.
entity
}
)
;
114:
115:                
if
 
(
onScreen
(
mob
.
position
)
)
 
{
116:                    
cameraShake
 
=
 
true
;
117:                    
cameraShakeTimer
 
=
 
0
;
118:                    
cameraShakeStrength
 
=
 
2
;
119:                    
freezeTimer
 
=
 
1
;
120:                
}
121:
122:                
createBloodSplatter
(
mob
.
position
)
;
123:                
createBones
(
sprite
.
frames
[
sprite
.
frame
]
,
 
mob
.
position
)
;
124:            
}
 
else
 
if
 
(
any
.
is
<
EvSpawnMob
>
(
)
)
 
{
125:                
const
 
auto
&
 
ev
 
=
 
any
.
get
<
EvSpawnMob
>
(
)
;
126:                
createMob
(
ev
.
type
,
 
ev
.
position
)
;
127:            
}
 
else
 
if
 
(
any
.
is
<
EvTryWalk
>
(
)
)
 
{
128:                
// const auto& ev = any.get<EvTryWalk>();
129:                
// ...
130:            
}
 
else
 
if
 
(
any
.
is
<
EvWalked
>
(
)
)
 
{
131:                
const
 
auto
&
 
ev
 
=
 
any
.
get
<
EvWalked
>
(
)
;
132:                
if
 
(
ev
.
mob
 
==
 
entities
[
player
]
.
mob
)
 
{
133:                    
// Camera tracks player
134:                    
const
 
vec2i
 
margin
{
8
,
 
4
}
;
135:                    
vec2i
 
newScreenPos
 
=
 
screenCoord
(
ev
.
to
)
;
136:                    
if
 
(
(
window
.
width
(
)
 
-
 
newScreenPos
.
x
)
 
<
 
margin
.
x
)
 
{
137:                        
cameraTarget
.
x
 
+=
 
margin
.
x
;
138:                    
}
 
else
 
if
 
(
newScreenPos
.
x
 
<
 
margin
.
x
)
 
{
139:                        
cameraTarget
.
x
 
-=
 
margin
.
x
;
140:                    
}
 
else
 
if
 
(
(
window
.
height
(
)
 
-
 
newScreenPos
.
y
)
 
<
 
margin
.
y
)
 
{
141:                        
cameraTarget
.
y
 
-=
 
margin
.
y
;
142:                    
}
 
else
 
if
 
(
newScreenPos
.
y
 
<
 
margin
.
y
)
 
{
143:                        
cameraTarget
.
y
 
+=
 
margin
.
y
;
144:                    
}
145:                
}
146:            
}
 
else
 
if
 
(
any
.
is
<
EvAttack
>
(
)
)
 
{
147:                
const
 
auto
&
 
ev
 
=
 
any
.
get
<
EvAttack
>
(
)
;
148:                
if
 
(
onScreen
(
mobs
[
ev
.
target
]
.
position
)
)
 
{
149:                    
cameraShake
 
=
 
true
;
150:                    
cameraShakeTimer
 
=
 
0
;
151:                    
cameraShakeStrength
 
=
 
1
;
152:                
}
153:            
}
154:
155:            
for
 
(
auto
*
 
sys
 
:
 
systems_
)
 
{
156:                
sys
->
handleEvent
(
any
)
;
157:            
}
158:        
}
159:        
events
.
clear
(
)
;
160:
161:        
auto
 
it
 
=
 
remove
.
begin
(
)
;
162:        
while
 
(
it
 
!=
 
remove
.
end
(
)
)
 
{
163:            
const
 
ident
&
 
id
 
=
 
*
it
;
164:            
Entity
&
 
e
 
=
 
entities
[
id
]
;
165:            
if
 
(
!
e
)
 
{
166:                
++
it
;
167:                
continue
;
 
// Already removed
168:            
}
169:
170:            
using
 
cid
 
=
 
std
::
pair
<
ComponentType
,
 
ident
>
;
171:            
auto
 
comps
 
=
 
{
cid
{
ComponentType
::
Mob
,
 
e
.
mob
}
,
 
cid
{
ComponentType
::
Sprite
,
 
e
.
sprite
}
,
172:                          
cid
{
ComponentType
::
Physics
,
 
e
.
physics
}
}
;
173:
174:            
for
 
(
auto
 
pair
 
:
 
comps
)
 
{
175:                
ident
 
component
 
=
 
pair
.
second
;
176:                
if
 
(
component
)
 
{
177:                    
ComponentType
 
type
 
=
 
pair
.
first
;
178:
179:                    
switch
 
(
type
)
 
{
180:                    
default
:
181:                        
break
;
182:                    
case
 
ComponentType
::
Mob
:
 
{
183:                        
mobs
.
remove
(
component
)
;
184:                        
break
;
185:                    
}
186:                    
case
 
ComponentType
::
Sprite
:
 
{
187:                        
sprites
.
remove
(
component
)
;
188:                        
break
;
189:                    
}
190:                    
case
 
ComponentType
::
Physics
:
 
{
191:                        
physics
.
remove
(
component
)
;
192:                        
break
;
193:                    
}
194:                    
}
195:                
}
196:            
}
197:
198:            
for
 
(
const
 
auto
&
 
ch
 
:
 
e
.
children
)
 
{
199:                
queueEvent
(
EvRemove
{
ch
}
)
;
200:            
}
201:            
e
.
children
.
clear
(
)
;
202:
203:            
entities
.
remove
(
id
)
;
204:            
it
++
;
205:        
}
206:
207:        
sync
(
)
;
208:        
tick_
++
;
209:
210:        
while
 
(
!
log_
.
empty
(
)
)
 
{
211:            
const
 
auto
&
 
pair
 
=
 
log_
.
front
(
)
;
212:            
if
 
(
tick_
 
>
 
pair
.
second
 
+
 
20
)
 
{
213:                
log_
.
pop_front
(
)
;
214:            
}
 
else
215:                
break
;
216:        
}
217:    
}
218:
219:    
return
 
true
;
220:
}
221:
222:
void
 
Game
::
render
(
)
 
{
223:    
window
.
clear
(
)
;
224:    
renderSystem_
.
render
(
)
;
225:
226:    
const
 
bool
 
showLog
 
=
 
true
;
227:    
if
 
(
showLog
)
 
{
228:        
const
 
int
 
maxMessages
 
=
 
10
;
229:        
int
 
y
 
=
 
0
;
230:        
for
 
(
const
 
auto
&
 
message
 
:
 
log_
)
 
{
231:            
auto
 
tick
 
=
 
std
::
to_string
(
message
.
second
)
;
232:            
for
 
(
int
 
x
 
=
 
0
;
 
x
 
<
 
(
int
)
tick
.
size
(
)
;
 
x
++
)
 
{
233:                
window
.
set
(
x
,
 
y
,
 
tick
[
x
]
,
 
TB_WHITE
,
 
TB_BLUE
)
;
234:            
}
235:            
for
 
(
int
 
x
 
=
 
(
int
)
tick
.
size
(
)
;
 
x
 
<
 
6
;
 
x
++
)
 
{
236:                
window
.
set
(
x
,
 
y
,
 
' '
,
 
TB_WHITE
,
 
TB_BLUE
)
;
237:            
}
238:            
for
 
(
int
 
x
 
=
 
0
;
 
x
 
<
 
(
int
)
message
.
first
.
size
(
)
;
 
x
++
)
 
{
239:                
window
.
set
(
6
 
+
 
x
,
 
y
,
 
message
.
first
[
x
]
,
 
TB_WHITE
,
 
TB_BLUE
)
;
240:            
}
241:            
y
++
;
242:            
if
 
(
y
 
>
 
maxMessages
)
243:                
break
;
244:        
}
245:    
}
246:
247:    
std
::
string
 
header
 
=
 
"Some Roguelike Thing"
;
248:    
for
 
(
int
 
x
 
=
 
0
;
 
x
 
<
 
(
int
)
header
.
size
(
)
;
 
x
++
)
 
{
249:        
window
.
set
(
x
,
 
0
,
 
header
[
x
]
,
 
TB_WHITE
,
 
TB_BLUE
)
;
250:    
}
251:    
for
 
(
int
 
x
 
=
 
(
int
)
header
.
size
(
)
;
 
x
 
<
 
window
.
width
(
)
;
 
x
++
)
 
{
252:        
window
.
set
(
x
,
 
0
,
 
' '
,
 
TB_WHITE
,
 
TB_BLUE
)
;
253:    
}
254:
255:
#
ifdef
 
__EMSCRIPTEN__
256:    
std
::
string
 
footer
 
=
 
"Arrows: Move. Code: https://github.com/eigenbom/game-example."
;
257:
#
else
258:    
std
::
string
 
footer
 
=
 
"ESC: Exit. Arrows: Move."
;
259:
#
endif
260:
261:    
for
 
(
int
 
x
 
=
 
0
;
 
x
 
<
 
(
int
)
footer
.
size
(
)
;
 
x
++
)
 
{
262:        
window
.
set
(
x
,
 
window
.
height
(
)
 
-
 
1
,
 
footer
[
x
]
,
 
TB_WHITE
,
 
TB_BLUE
)
;
263:    
}
264:    
for
 
(
int
 
x
 
=
 
(
int
)
footer
.
size
(
)
;
 
x
 
<
 
window
.
width
(
)
;
 
x
++
)
 
{
265:        
window
.
set
(
x
,
 
0
,
 
' '
,
 
TB_WHITE
,
 
TB_BLUE
)
;
266:    
}
267:
}
268:
269:
vec2i
 
Game
::
worldCoord
(
vec2i
 
screenCoord
)
 
const
 
{
270:    
const
 
vec2i
 
ws
{
window
.
width
(
)
,
 
window
.
height
(
)
}
;
271:    
vec2i
 
q
 
=
 
screenCoord
 
-
 
ws
 
/
 
2
;
272:    
vec2i
 
camFinal
 
=
 
cameraPosition
 
+
 
(
cameraShake
 
?
 
cameraShakeOffset
 
:
 
vec2i
{
0
,
 
0
}
)
;
273:    
return
 
{
q
.
x
 
+
 
camFinal
.
x
,
 
-
(
q
.
y
 
-
 
camFinal
.
y
)
}
;
274:
}
275:
276:
vec2i
 
Game
::
screenCoord
(
vec2i
 
worldCoord
)
 
const
 
{
277:    
const
 
vec2i
 
ws
{
window
.
width
(
)
,
 
window
.
height
(
)
}
;
278:    
vec2i
 
wc
 
=
 
worldCoord
;
279:    
vec2i
 
camFinal
 
=
 
cameraPosition
 
+
 
(
cameraShake
 
?
 
cameraShakeOffset
 
:
 
vec2i
{
0
,
 
0
}
)
;
280:    
return
 
vec2i
{
wc
.
x
 
-
 
camFinal
.
x
,
 
camFinal
.
y
 
-
 
wc
.
y
}
 
+
 
ws
 
/
 
2
;
281:
}
282:
283:
bool
 
Game
::
onScreen
(
vec2i
 
worldCoord
)
 
const
 
{
284:    
vec2i
 
sc
 
=
 
screenCoord
(
worldCoord
)
;
285:    
recti
 
windowBounds
{
0
,
 
window
.
height
(
)
 
-
 
1
,
 
window
.
width
(
)
,
 
window
.
height
(
)
}
;
286:    
return
 
windowBounds
.
contains
(
sc
)
;
287:
}
288:
289:
void
 
Game
::
queueEvent
(
const
 
EvAny
&
 
ev
)
 
{
 
events_
[
eventsIndex_
]
.
push_back
(
ev
)
;
 
}
290:
291:
void
 
Game
::
sync
(
)
 
{
292:    
entities
.
sync
(
)
;
293:    
mobs
.
sync
(
)
;
294:    
sprites
.
sync
(
)
;
295:    
physics
.
sync
(
)
;
296:
}
297:
298:
void
 
Game
::
log
(
const
 
std
::
string
 
message
)
 
{
 
log_
.
push_back
(
{
message
,
 
tick_
}
)
;
 
}
299:
300:
Sprite
&
 
Game
::
createSprite
(
std
::
string
 
frames
,
 
bool
 
animated
,
 
int
 
frameRate
,
 
uint16_t
 
fg
,
301:                           
uint16_t
 
bg
,
 
vec2i
 
position
,
 
RenderLayer
 
renderLayer
)
 
{
302:    
auto
&
 
e
 
=
 
entities
.
add
(
)
;
303:
304:    
auto
&
 
spr
 
=
 
sprites
.
add
(
Sprite
{
frames
,
 
animated
,
 
frameRate
,
 
fg
,
 
bg
,
 
position
,
 
renderLayer
}
)
;
305:    
spr
.
entity
 
=
 
e
.
id
;
306:    
e
.
sprite
 
=
 
spr
.
id
;
307:    
return
 
spr
;
308:
}
309:
310:
Mob
&
 
Game
::
createMob
(
MobType
 
type
,
 
vec2i
 
position
)
 
{
311:    
auto
&
 
e
 
=
 
entities
.
add
(
)
;
312:
313:    
auto
&
 
info
 
=
 
MobDatabase
.
at
(
type
)
;
314:    
Mob
&
 
mob
 
=
 
mobs
.
add
(
Mob
{
&
info
}
)
;
315:    
e
.
mob
 
=
 
mob
.
id
;
316:    
mob
.
entity
 
=
 
e
.
id
;
317:
318:    
mob
.
health
 
=
 
info
.
health
;
319:    
mob
.
position
 
=
 
position
;
320:
321:    
const
 
char
*
 
frames
 
=
 
"?!"
;
322:    
int
 
frameRate
 
=
 
1
;
323:    
auto
 
fg
 
=
 
TB_WHITE
;
324:    
auto
 
bg
 
=
 
TB_BLACK
;
325:    
switch
 
(
mob
.
info
->
category
)
 
{
326:    
case
 
MobCategory
::
Rabbit
:
327:        
frames
 
=
 
"r"
;
328:        
frameRate
 
=
 
1
;
329:        
fg
 
=
 
TB_YELLOW
;
330:        
break
;
331:    
case
 
MobCategory
::
Snake
:
332:        
frames
 
=
 
"i!~~"
;
333:        
frameRate
 
=
 
0
;
334:        
fg
 
=
 
TB_GREEN
;
335:        
break
;
336:    
case
 
MobCategory
::
Orc
:
337:        
frames
 
=
 
"oO"
;
338:        
frameRate
 
=
 
3
;
339:        
fg
 
=
 
TB_GREEN
;
340:        
bg
 
=
 
TB_BLACK
;
341:        
break
;
342:    
case
 
MobCategory
::
Player
:
343:        
frames
 
=
 
"@"
;
344:        
break
;
345:    
default
:
346:        
frames
 
=
 
"?!"
;
347:        
break
;
348:    
}
349:    
auto
&
 
spr
 
=
350:        
sprites
.
add
(
Sprite
(
frames
,
 
frameRate
 
>
 
0
,
 
frameRate
,
 
fg
,
 
bg
,
 
position
,
 
RenderLayer
::
Mob
)
)
;
351:    
e
.
sprite
 
=
 
spr
.
id
;
352:    
spr
.
entity
 
=
 
e
.
id
;
353:
354:    
switch
 
(
mob
.
info
->
category
)
 
{
355:    
case
 
MobCategory
::
Snake
:
 
{
356:        
auto
 
spr
 
=
 
createSprite
(
"oo"
,
 
false
,
 
0
,
 
TB_GREEN
,
 
TB_BLACK
,
 
mob
.
position
 
+
 
mob
.
dir
,
357:                                
RenderLayer
::
Mob
)
;
358:        
auto
&
 
child
 
=
 
entities
[
spr
.
entity
]
;
359:        
e
.
addChild
(
child
)
;
360:
361:        
mob
.
extraSprite
 
=
 
spr
.
id
;
362:        
break
;
363:    
}
364:    
case
 
MobCategory
::
Orc
:
 
{
365:        
auto
&
 
spr1
 
=
 
createSprite
(
"\\|"
,
 
true
,
 
6
,
 
TB_GREEN
,
 
TB_BLACK
,
 
mob
.
position
 
+
 
vec2i
{
-
1
,
 
1
}
,
366:                                  
RenderLayer
::
MobBelow
)
;
367:        
auto
&
 
child1
 
=
 
entities
[
spr1
.
entity
]
;
368:        
e
.
addChild
(
child1
)
;
369:        
mob
.
extraSprite
 
=
 
spr1
.
id
;
370:
371:        
auto
&
 
spr2
 
=
 
createSprite
(
"/|"
,
 
true
,
 
6
,
 
TB_GREEN
,
 
TB_BLACK
,
 
mob
.
position
 
+
 
vec2i
{
1
,
 
1
}
,
372:                                  
RenderLayer
::
MobBelow
)
;
373:        
auto
&
 
child2
 
=
 
entities
[
spr2
.
entity
]
;
374:        
e
.
addChild
(
child2
)
;
375:        
mob
.
extraSprite2
 
=
 
spr2
.
id
;
376:        
break
;
377:    
}
378:    
default
:
379:        
break
;
380:    
}
381:
382:    
return
 
mob
;
383:
}
384:
385:
void
 
Game
::
createBloodSplatter
(
vec2i
 
position
)
 
{
386:    
if
 
(
sprites
.
size
(
)
 
>=
 
sprites
.
max_size
(
)
 
/
 
2
)
387:        
return
;
388:
389:    
const
 
int
 
radius
 
=
 
3
;
390:    
const
 
int
 
sqradius
 
=
 
radius
 
*
 
radius
;
391:    
for
 
(
int
 
dx
 
=
 
-
radius
;
 
dx
 
<=
 
radius
;
 
dx
++
)
 
{
392:        
for
 
(
int
 
dy
 
=
 
-
radius
;
 
dy
 
<=
 
radius
;
 
dy
++
)
 
{
393:            
if
 
(
(
dx
 
*
 
dx
 
+
 
dy
 
*
 
dy
)
 
<=
 
sqradius
)
 
{
394:                
if
 
(
randInt
(
0
,
 
4
)
 
!=
 
0
)
 
{
395:                    
auto
&
 
spr
 
=
 
createSprite
(
"."
,
 
false
,
 
0
,
 
TB_RED
,
 
TB_BLACK
,
396:                                             
position
 
+
 
vec2i
{
dx
,
 
dy
}
,
 
RenderLayer
::
Ground
)
;
397:                    
auto
&
 
e
 
=
 
entities
[
spr
.
entity
]
;
398:                    
e
.
life
 
=
 
randInt
(
200
,
 
300
)
;
399:                
}
400:            
}
401:        
}
402:    
}
403:
404:    
int
 
numBloodParticles
 
=
 
randInt
(
10
,
 
40
)
;
405:    
for
 
(
int
 
i
 
=
 
0
;
 
i
 
<
 
numBloodParticles
;
 
i
++
)
 
{
406:        
auto
&
 
spr
 
=
 
createSprite
(
"o"
,
 
false
,
 
0
,
 
TB_RED
,
 
TB_BLACK
,
 
position
,
 
RenderLayer
::
Particles
)
;
407:        
auto
&
 
e
 
=
 
entities
[
spr
.
entity
]
;
408:        
e
.
life
 
=
 
randInt
(
6
,
 
12
)
;
409:
410:        
double
 
vel
 
=
 
random
(
0.4
,
 
0.6
)
;
411:
412:        
Physics
&
 
ph
 
=
 
physics
.
add
(
)
;
413:        
ph
.
type
 
=
 
PhysicsType
::
Projectile
;
414:        
ph
.
position
 
=
 
(
vec2d
)
position
;
415:        
double
 
th
 
=
 
random
(
-
M_PI
,
 
M_PI
)
;
416:        
ph
.
velocity
.
x
 
=
 
vel
 
*
 
cos
(
th
)
;
417:        
ph
.
velocity
.
y
 
=
 
vel
 
*
 
sin
(
th
)
;
418:
419:        
e
.
physics
 
=
 
ph
.
id
;
420:        
ph
.
entity
 
=
 
e
.
id
;
421:    
}
422:
}
423:
424:
void
 
Game
::
createBones
(
char
 
c
,
 
vec2i
 
position
)
 
{
425:    
auto
&
 
spr
 
=
426:        
createSprite
(
std
::
string
(
1
,
 
c
)
,
 
false
,
 
0
,
 
TB_RED
,
 
TB_BLACK
,
 
position
,
 
RenderLayer
::
Ground
)
;
427:    
auto
&
 
e
 
=
 
entities
[
spr
.
entity
]
;
428:    
e
.
life
 
=
 
randInt
(
100
,
 
110
)
;
429:
}
430:
431:
void
 
Game
::
handleInput
(
)
 
{
432:    
for
 
(
auto
 
ev
 
:
 
window
.
events
(
)
)
 
{
433:        
bool
 
isPlayerMove
 
=
 
[
ev
]
(
)
 
{
434:            
switch
 
(
ev
)
 
{
435:            
case
 
WindowEvent
::
ArrowUp
:
436:            
case
 
WindowEvent
::
ArrowDown
:
437:            
case
 
WindowEvent
::
ArrowLeft
:
438:            
case
 
WindowEvent
::
ArrowRight
:
439:                
return
 
true
;
440:            
default
:
441:                
return
 
false
;
442:            
}
443:        
}
(
)
;
444:
445:        
if
 
(/* !isPlayerMove || */truefalse
!
isPlayerMove
 
||&&/* || windowEvents_.size() < 1 */
 
windowEvents_
.
size
(
)
 
<
 
1
)
 
{
446:            
// log(to_string(ev));
447:            
windowEvents_
.
push_back
(
ev
)
;
448:        
}
449:    
}
450:
}
451:
452:
void
 
Game
::
updatePlayer
(
)
 
{
453:    
auto
&
 
entity
 
=
 
entities
[
player
]
;
454:    
auto
&
 
mob
 
=
 
mobs
[
entity
.
mob
]
;
455:
456:    
mob
.
tick
 
+=
 
mob
.
info
->
speed
;
457:    
mob
.
tick
 
=
 
std
::
min
(
mob
.
tick
,
 
2
 
*
 
Mob
::
TicksPerAction
 
-
 
1
)
;
458:
459:    
// Map input to player commands
460:    
vec2i
 
movePlayer
{
0
,
 
0
}
;
461:
462:    
while
 
(
!
windowEvents_
.
empty
(
)
)
 
{
463:        
const
 
auto
&
 
ev
 
=
 
windowEvents_
.
front
(
)
;
464:        
switch
 
(
ev
)
 
{
465:        
default
:
466:            
break
;
467:        
case
 
WindowEvent
::
ArrowUp
:
468:            
movePlayer
 
=
 
vec2i
{
0
,
 
1
}
;
469:            
break
;
470:        
case
 
WindowEvent
::
ArrowDown
:
471:            
movePlayer
 
=
 
vec2i
{
0
,
 
-
1
}
;
472:            
break
;
473:        
case
 
WindowEvent
::
ArrowLeft
:
474:            
movePlayer
 
=
 
vec2i
{
-
1
,
 
0
}
;
475:            
break
;
476:        
case
 
WindowEvent
::
ArrowRight
:
477:            
movePlayer
 
=
 
vec2i
{
1
,
 
0
}
;
478:            
break
;
479:        
}
480:
481:        
// A move requires a full action
482:        
if
 
(
movePlayer
 
!=
 
vec2i
{
0
,
 
0
}
)
 
{
483:            
if
 
(
mob
.
tick
 
>=
 
Mob
::
TicksPerAction
)
 
{
484:                
windowEvents_
.
pop_front
(
)
;
485:                
mob
.
tick
 
-=
 
Mob
::
TicksPerAction
;
486:                
break
;
487:            
}
 
else
 
{
488:                
// Can't move yet
489:                
return
;
490:            
}
491:        
}
492:    
}
493:
494:    
if
 
(
movePlayer
 
!=
 
vec2i
{
0
,
 
0
}
)
 
{
495:        
vec2i
 
oldPos
 
=
 
mob
.
position
;
496:        
vec2i
 
newPos
 
=
 
oldPos
 
+
 
movePlayer
;
497:
498:        
ident
 
target
 
=
 
invalid_id
;
499:        
for
 
(
auto
&
 
other
 
:
 
mobs
.
values
(
)
)
 
{
500:            
if
 
(/* other.id != mob.id && */truefalse
other
.
id
 
!=
 
mob
.
id
 
&&||/* && other.position == newPos */
 
other
.
position
 
==
 
newPos
)
 
{
501:                
target
 
=
 
other
.
id
;
502:                
break
;
503:            
}
504:        
}
505:
506:        
if
 
(
target
)
 
{
507:            
queueEvent
(
EvAttack
{
mob
.
id
,
 
target
}
)
;
508:        
}
 
else
 
{
509:            
queueEvent
(
EvTryWalk
{
mob
.
id
,
 
oldPos
,
 
newPos
}
)
;
510:        
}
511:    
}
512:
}
513:
514:
void
 
Game
::
updateCamera
(
)
 
{
515:    
if
 
(
cameraShake
)
 
{
516:        
cameraShakeTimer
++
;
517:        
if
 
(
cameraShakeStrength
 
==
 
1
)
518:            
cameraShakeTimer
++
;
519:
520:        
if
 
(
cameraShakeTimer
 
>
 
7
)
 
{
521:            
cameraShake
 
=
 
false
;
522:            
cameraShakeOffset
 
=
 
vec2i
{
0
,
 
0
}
;
523:            
cameraShakeTimer
 
=
 
0
;
524:        
}
 
else
 
if
 
(
cameraShakeTimer
 
%
 
2
 
==
 
0
)
 
{
525:            
if
 
(
cameraShakeStrength
 
==
 
1
)
 
{
526:                
if
 
(
randInt
(
0
,
 
1
)
 
==
 
0
)
 
{
527:                    
cameraShakeOffset
 
=
 
vec2i
{
randInt
(
-
1
,
 
1
)
,
 
0
}
;
528:                
}
 
else
 
{
529:                    
cameraShakeOffset
 
=
 
vec2i
{
0
,
 
randInt
(
-
1
,
 
1
)
}
;
530:                
}
531:            
}
 
else
 
{
532:                
cameraShakeOffset
 
=
 
vec2i
{
randInt
(
-
1
,
 
1
)
,
 
randInt
(
-
1
,
 
1
)
}
;
533:            
}
534:        
}
535:    
}
536:
537:    
if
 
(
cameraPosition
 
!=
 
cameraTarget
)
 
{
538:        
static
 
int
 
cameraTick_
 
=
 
0
;
539:        
if
 
(
cameraTick_
++
 
>=
 
1
)
 
{
540:            
cameraTick_
 
=
 
0
;
541:            
vec2i
 
dc
 
=
 
cameraTarget
 
-
 
cameraPosition
;
542:            
int
 
dx
 
=
 
sign
(
dc
.
x
)
;
543:            
int
 
dy
 
=
 
sign
(
dc
.
y
)
;
544:            
cameraPosition
 
+=
 
vec2i
{
dx
,
 
dy
}
;
545:        
}
546:    
}
547:
}