-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVehicle.java
More file actions
344 lines (315 loc) · 9.69 KB
/
Copy pathVehicle.java
File metadata and controls
344 lines (315 loc) · 9.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
import greenfoot.*; // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
/**
* Vehicle inherits SuperSmoothMover, controls the motion algorithms and sound effects.
*
* Modified based on Mr.Cohen's codes. Most of them made by myself.
*
* @author Jordan Cohen, Yixin Cai
* @version 2022-10-24
*/
public abstract class Vehicle extends SuperSmoothMover implements iSoundMaker
{
protected double maxSpeed;
protected double speed;
protected int direction; // 1 = right, -1 = left
protected boolean moving;
protected boolean slowed;
protected boolean allowLaneChange;
protected int fromLane;
protected int toLane;
protected int delay;
protected int coolDown;
protected int maxCoolDown;
protected int yOffset;
protected VehicleSpawner origin;
protected GifImage[] gifImages;
protected GifImage gifImage;
protected GreenfootSound sound;
protected GreenfootSound brakeSound;
protected GreenfootSound honkSound;
// an abstract method that every subclasses have to implements.
protected abstract boolean checkHitPedestrian();
/**
* The constructor of Vehicle.
* @param origin A VehicleSpawner
*/
public Vehicle(VehicleSpawner origin) {
// set instance variables
this.origin = origin;
moving = true;
slowed = false;
allowLaneChange = false;
delay = 0;
coolDown = 0;
maxCoolDown = 60;
if (origin.facesRightward()){
direction = 1;
} else {
direction = -1;
getImage().mirrorHorizontally();
}
// pre-load the sound files
brakeSound = new GreenfootSound("brake.wav");
honkSound = new GreenfootSound("honk.wav");
}
/**
* This method is called by the Greenfoot system when this actor has been inserted into the world.
* @param w The World
*/
public void addedToWorld(World w) {
setLocation (origin.getX() - (direction * 100), origin.getY() - yOffset);
VehicleWorld vw = (VehicleWorld)w;
if (vw.isRaining()){
speed = maxSpeed / 2;
slowed = true;
}
if (sound != null) {
setVolume(vw.getVolume());
}
}
/**
* Act - do whatever the Vehicle wants to do. This method is called whenever
* the 'Act' or 'Run' button gets pressed in the environment.
*/
public void act() {
if (gifImage != null) {
setImage(gifImage.getCurrentImage());// update the gif images every act
}
drive();
checkHitPedestrian();
if (checkEdge()){
removeSelf();
}
}
/**
* A method used by all Vehicles to check if they are at the edge
* @return boolean True if vehicle is at the edge, False otherwise
*/
protected boolean checkEdge() {
if (direction == 1){
if (getX() > getWorld().getWidth() + 200){
return true;
}
} else {
if (getX() < -200){
return true;
}
}
return false;
}
/**
* Method that deals with movement. Speed can be set by individual subclasses in their constructors.
*/
public void drive()
{
// Ahead is a generic vehicle - we don't know what type BUT
// since every Vehicle "promises" to have a getSpeed() method,
// we can call that on any vehicle to find out it's speed
Vehicle ahead = (Vehicle) getOneObjectAtOffset(direction * (int)(speed + getImage().getWidth()/2 + 4), 0, Vehicle.class);
if (isChangingLane()) {
// check lane change completion
VehicleWorld vw = (VehicleWorld) getWorld();
int toLaneY = vw.getLaneY(toLane);
int y = getY();
int x = getX();
if (fromLane > toLane) {
if (y <= toLaneY) {
setLocation(x, toLaneY- yOffset);
turn(45 * direction);
fromLane = -1;
toLane = -1;
}
}
else if (fromLane < toLane) {
if (y >= toLaneY) {
setLocation(x, toLaneY - yOffset);
turn(45 * -direction);
fromLane = -1;
toLane = -1;
}
}
}
else if (delay > 0) {
// set speed to 0 while in delay
speed = 0;
}
else if (ahead == null) {
// resume speed if no vehicle ahead
if (slowed) {
speed = maxSpeed / 2.0;
}
else {
speed = maxSpeed;
}
}
else if (!ahead.isChangingLane()){
double oldSpeed = speed;
speed = ahead.getSpeed();
// check and attempt to change lane
if (shouldChangeLane()) {
changeLane();
}
// play brake sound if slow down
if (speed < oldSpeed) {
brakeSound.play();
}
}
move(speed * direction);
if (delay > 0) {
delay--;
}
if (coolDown > 0) {
coolDown--;
}
}
/**
* Check if the vehicle should perform lane change
* @return boolean True if vehicle should perform lane change, False otherwise
*/
private boolean shouldChangeLane() {
if (!allowLaneChange) {
return false;
}
if (coolDown > 0) {
return false;
}
return true;
}
/**
* Attempt to change lane. If a target lane is found, honk and turn toward the target land.
* Then, temporarily increase speed and set a cool down for next lane change.
*/
private void changeLane() {
VehicleWorld vw = (VehicleWorld) getWorld();
int lane = vw.getLane(getY() + yOffset);
int nextLane = findLaneToChange(lane);
if (lane == nextLane) {
return;
}
// honk before making lane change
honkSound.play();
// lane change
fromLane = lane;
toLane = nextLane;
turn(45 * (toLane - fromLane) * direction);
speed = Math.min(maxSpeed * 2, 15);
coolDown = maxCoolDown;
}
/**
* Give a lane, find the lane to be changed to.
* Check left side first, then right side.
* Return given lane if neither left lane and right lane is available.
*
* @param lane the lane number (zero-indexed)
* @return int the lane number (zero-indexed) that can be changed to
*/
private int findLaneToChange(int lane) {
VehicleWorld vw = (VehicleWorld) getWorld();
int leftLane = lane - direction;
int rightLane = lane + direction;
if (vw.isSameDirection(lane, leftLane)) {
if (checkLaneSpace(vw.getLaneY(leftLane))) {
return leftLane;
}
}
if (vw.isSameDirection(lane, rightLane)) {
if (checkLaneSpace(vw.getLaneY(rightLane))) {
return rightLane;
}
}
return lane;
}
/**
* Check if there is enough space for lane change at the given y position
*
* @param landY the y position of a lane's center
* @return boolean whether there is enough space
*/
private boolean checkLaneSpace(int laneY) {
int dy = laneY - getY();
int width = getImage().getWidth();
for (int dx = -width; dx < width; dx += 5) {
Vehicle vehicle = (Vehicle) getOneObjectAtOffset(dx, dy, Vehicle.class);
if (vehicle != null) {
return false;
}
}
return true;
}
/**
* Check if the vehicle is changing lane
* @return boolean True if is changing line, False otherwise
*/
public boolean isChangingLane() {
return fromLane != toLane;
}
/**
* Method to remove object and stop sound
*/
public void removeSelf() {
stopSound();
getWorld().removeObject(this);
}
/**
* Start playing sound if there is sound
*/
public void playSound() {
if (sound != null) {
sound.playLoop();
}
}
/**
* Stop playing sound if there is sound
*/
public void stopSound() {
if (sound != null) {
sound.stop();
}
}
/**
* Update volume of the sounds
* @param volume The current volume
*/
public void setVolume(int volume) {
if (sound != null) {
sound.setVolume(volume);
brakeSound.setVolume(volume);
honkSound.setVolume(volume);
}
}
/**
* An accessor that can be used to get this Vehicle's speed. Used, for example, when a vehicle wants to see
* if a faster vehicle is ahead in the lane.
* @return double The vehicle's speed
*/
public double getSpeed(){
return speed;
}
/**
* Slow down vehicle
*/
public void slowMeDown(){
this.speed = maxSpeed / 2.0;
slowed = true;
}
/**
* Called by Effects to resume normal speed
*/
public void resumeNormalSpeed(){
this.speed = maxSpeed;
slowed = false;
}
/**
* Change direction of the vehicle
*/
public void changeDirection() {
this.direction = -this.direction;
if (gifImage != null) {
gifImage = gifImages[direction == 1 ? 1 : 0];
}
// switch fromLane and toLane
int tempLane = fromLane;
fromLane = toLane;
toLane = tempLane;
}
}