@@ -968,6 +968,12 @@ class Obstacle { |
| 968 | 968 | this.bobSpeed = random(0.02, 0.04) |
| 969 | 969 | this.bobAmount = 0 |
| 970 | 970 | |
| 971 | + // Wind effect properties |
| 972 | + this.windSway = 0 // Current sway amount |
| 973 | + this.windSwayTarget = 0 // Target sway for smooth animation |
| 974 | + this.windBob = 0 // Additional vertical movement from wind |
| 975 | + this.basketSwing = 0 // For balloon basket swinging |
| 976 | + |
| 971 | 977 | // Type-specific initialization |
| 972 | 978 | if (this.type === 'balloon') { |
| 973 | 979 | this.bobAmount = 8 // Balloons bob more |
@@ -1006,6 +1012,57 @@ class Obstacle { |
| 1006 | 1012 | let bob = sin(frameCount * this.bobSpeed + this.bobOffset) * this.bobAmount |
| 1007 | 1013 | this.y = this.originalY + bob |
| 1008 | 1014 | |
| 1015 | + // ENHANCED: Apply wind effects |
| 1016 | + if (windActive) { |
| 1017 | + // Different wind responses by type |
| 1018 | + if (this.type === 'balloon') { |
| 1019 | + // Balloons are highly affected by wind |
| 1020 | + this.windSwayTarget = cos(windDirection) * windStrength * 15 // Strong horizontal push |
| 1021 | + this.windBob = |
| 1022 | + sin(frameCount * 0.04 + this.bobOffset) * windStrength * 3 // Extra vertical movement |
| 1023 | + |
| 1024 | + // Basket swings opposite to balloon movement (pendulum effect) |
| 1025 | + this.basketSwing = |
| 1026 | + -this.windSway * 0.5 + sin(frameCount * 0.06) * windStrength * 0.3 |
| 1027 | + |
| 1028 | + // Actually move the balloon |
| 1029 | + this.originalX += cos(windDirection) * windStrength * 0.08 |
| 1030 | + |
| 1031 | + // Keep on screen with stronger resistance at edges |
| 1032 | + if (this.originalX < 50) { |
| 1033 | + this.originalX = 50 |
| 1034 | + this.windSwayTarget *= -0.5 // Bounce back effect |
| 1035 | + } |
| 1036 | + if (this.originalX > width - 50) { |
| 1037 | + this.originalX = width - 50 |
| 1038 | + this.windSwayTarget *= -0.5 |
| 1039 | + } |
| 1040 | + } else if (this.type === 'beetle') { |
| 1041 | + // Beetles resist but still affected |
| 1042 | + this.windSwayTarget = cos(windDirection) * windStrength * 3 |
| 1043 | + // Fight against wind |
| 1044 | + this.driftAngle -= cos(windDirection) * windStrength * 0.01 |
| 1045 | + } else if (this.type === 'leaf') { |
| 1046 | + // Leaves flutter in wind |
| 1047 | + this.windSwayTarget = cos(windDirection) * windStrength * 5 |
| 1048 | + this.rotation += windStrength * 0.02 // Spin faster |
| 1049 | + } |
| 1050 | + } else { |
| 1051 | + // No wind, return to normal |
| 1052 | + this.windSwayTarget = 0 |
| 1053 | + this.windBob = 0 |
| 1054 | + this.basketSwing = 0 |
| 1055 | + } |
| 1056 | + |
| 1057 | + // Smooth sway animation |
| 1058 | + this.windSway = lerp(this.windSway, this.windSwayTarget, 0.1) |
| 1059 | + |
| 1060 | + // Apply wind sway to position |
| 1061 | + this.x = this.originalX + this.windSway |
| 1062 | + |
| 1063 | + // Apply wind bob to vertical position |
| 1064 | + this.y = this.originalY + bob + this.windBob |
| 1065 | + |
| 1009 | 1066 | // Beetle-specific drift |
| 1010 | 1067 | if (this.type === 'beetle') { |
| 1011 | 1068 | // Store initial position if not set |
@@ -1201,272 +1258,293 @@ class Obstacle { |
| 1201 | 1258 | } |
| 1202 | 1259 | |
| 1203 | 1260 | display () { |
| 1261 | + push() |
| 1262 | + translate(this.x, this.y) |
| 1263 | + |
| 1264 | + if (this.type === 'balloon') { |
| 1265 | + // ============================================ |
| 1266 | + // HOT AIR BALLOON WITH CANVAS TEXTURE |
| 1267 | + // ============================================ |
| 1204 | 1268 | push() |
| 1205 | | - translate(this.x, this.y) |
| 1269 | + |
| 1270 | + // ENHANCED: Tilt balloon based on wind |
| 1271 | + if (windActive) { |
| 1272 | + rotate(this.windSway * 0.01) // Slight tilt in wind direction |
| 1273 | + } |
| 1206 | 1274 | |
| 1207 | | - if (this.type === 'balloon') { |
| 1208 | | - // ============================================ |
| 1209 | | - // HOT AIR BALLOON WITH CANVAS TEXTURE |
| 1210 | | - // ============================================ |
| 1275 | + // Balloon shadow |
| 1276 | + noStroke() |
| 1277 | + fill(0, 0, 0, 30) |
| 1278 | + ellipse(5, 5, this.radius * 2.1) |
| 1279 | + |
| 1280 | + // Main balloon with canvas panel texture |
| 1281 | + // Draw vertical panels like a real hot air balloon |
| 1282 | + let numPanels = 8 |
| 1283 | + for (let i = 0; i < numPanels; i++) { |
| 1211 | 1284 | push() |
| 1212 | 1285 | |
| 1213 | | - // Balloon shadow |
| 1214 | | - noStroke() |
| 1215 | | - fill(0, 0, 0, 30) |
| 1216 | | - ellipse(5, 5, this.radius * 2.1) |
| 1217 | | - |
| 1218 | | - // Main balloon with canvas panel texture |
| 1219 | | - // Draw vertical panels like a real hot air balloon |
| 1220 | | - let numPanels = 8 |
| 1221 | | - for (let i = 0; i < numPanels; i++) { |
| 1222 | | - push() |
| 1223 | | - |
| 1224 | | - // Rotate for each panel |
| 1225 | | - rotate((TWO_PI / numPanels) * i) |
| 1226 | | - |
| 1227 | | - // Alternate panel colors for classic hot air balloon look |
| 1228 | | - if (i % 2 === 0) { |
| 1229 | | - fill( |
| 1230 | | - red(this.balloonColor), |
| 1231 | | - green(this.balloonColor), |
| 1232 | | - blue(this.balloonColor) |
| 1233 | | - ) |
| 1234 | | - } else { |
| 1235 | | - // Slightly darker alternate panels |
| 1236 | | - fill( |
| 1237 | | - red(this.balloonColor) * 0.9, |
| 1238 | | - green(this.balloonColor) * 0.9, |
| 1239 | | - blue(this.balloonColor) * 0.9 |
| 1240 | | - ) |
| 1241 | | - } |
| 1286 | + // Rotate for each panel |
| 1287 | + rotate((TWO_PI / numPanels) * i) |
| 1242 | 1288 | |
| 1243 | | - // Draw panel as pie slice |
| 1244 | | - noStroke() |
| 1245 | | - arc( |
| 1246 | | - 0, |
| 1247 | | - 0, |
| 1248 | | - this.radius * 2, |
| 1249 | | - this.radius * 2, |
| 1250 | | - -PI / numPanels, |
| 1251 | | - PI / numPanels, |
| 1252 | | - PIE |
| 1289 | + // Alternate panel colors for classic hot air balloon look |
| 1290 | + if (i % 2 === 0) { |
| 1291 | + fill( |
| 1292 | + red(this.balloonColor), |
| 1293 | + green(this.balloonColor), |
| 1294 | + blue(this.balloonColor) |
| 1295 | + ) |
| 1296 | + } else { |
| 1297 | + // Slightly darker alternate panels |
| 1298 | + fill( |
| 1299 | + red(this.balloonColor) * 0.9, |
| 1300 | + green(this.balloonColor) * 0.9, |
| 1301 | + blue(this.balloonColor) * 0.9 |
| 1253 | 1302 | ) |
| 1254 | | - |
| 1255 | | - pop() |
| 1256 | | - } |
| 1257 | | - |
| 1258 | | - // Add panel seams (the ropes/stitching between panels) |
| 1259 | | - stroke(60, 40, 20, 110) |
| 1260 | | - strokeWeight(1) |
| 1261 | | - for (let i = 0; i < numPanels; i++) { |
| 1262 | | - let angle = (TWO_PI / numPanels) * i |
| 1263 | | - let x1 = cos(angle) * this.radius * 0.2 |
| 1264 | | - let y1 = sin(angle) * this.radius * 0.2 |
| 1265 | | - let x2 = cos(angle) * this.radius * 0.95 |
| 1266 | | - let y2 = sin(angle) * this.radius * 0.95 |
| 1267 | | - line(x1, y1, x2, y2) |
| 1268 | 1303 | } |
| 1269 | 1304 | |
| 1270 | | - // Add circular reinforcement bands |
| 1271 | | - noFill() |
| 1272 | | - stroke(80, 50, 30, 80) |
| 1273 | | - strokeWeight(1.5) |
| 1274 | | - ellipse(0, 0, this.radius * 1.4) |
| 1275 | | - ellipse(0, 0, this.radius * 0.8) |
| 1276 | | - |
| 1277 | | - // Matte fabric shading (subtle, non-glossy) |
| 1278 | | - noStroke() |
| 1279 | | - // Soft radial shading toward top-left to imply ambient light without specular shine |
| 1280 | | - for ( |
| 1281 | | - let r = this.radius * 1.2; |
| 1282 | | - r > this.radius * 0.2; |
| 1283 | | - r -= this.radius * 0.15 |
| 1284 | | - ) { |
| 1285 | | - fill(255, 255, 255, 8) // very low alpha |
| 1286 | | - ellipse(-this.radius * 0.25, -this.radius * 0.35, r * 0.25, r * 0.18) |
| 1287 | | - } |
| 1288 | | - // Global matte overlay to reduce plastic look |
| 1305 | + // Draw panel as pie slice |
| 1289 | 1306 | noStroke() |
| 1290 | | - fill(230, 210, 190, 18) |
| 1291 | | - ellipse(0, 0, this.radius * 2, this.radius * 2) |
| 1292 | | - |
| 1293 | | - // Bottom opening of balloon (where flame goes) |
| 1294 | | - fill(40, 20, 10) |
| 1295 | | - ellipse(0, this.radius * 0.9, this.radius * 0.4, this.radius * 0.15) |
| 1296 | | - |
| 1297 | | - // Support ropes from balloon to basket |
| 1298 | | - stroke(80, 60, 40) |
| 1299 | | - strokeWeight(2) |
| 1300 | | - // Four support ropes |
| 1301 | | - line(-this.radius * 0.3, this.radius * 0.85, -8, this.radius + 20) |
| 1302 | | - line(this.radius * 0.3, this.radius * 0.85, 8, this.radius + 20) |
| 1303 | | - line(-this.radius * 0.15, this.radius * 0.9, -4, this.radius + 20) |
| 1304 | | - line(this.radius * 0.15, this.radius * 0.9, 4, this.radius + 20) |
| 1307 | + arc( |
| 1308 | + 0, |
| 1309 | + 0, |
| 1310 | + this.radius * 2, |
| 1311 | + this.radius * 2, |
| 1312 | + -PI / numPanels, |
| 1313 | + PI / numPanels, |
| 1314 | + PIE |
| 1315 | + ) |
| 1305 | 1316 | |
| 1306 | | - // FLAME EFFECT (between balloon and basket) |
| 1307 | | - push() |
| 1308 | | - translate(0, this.radius + 10) |
| 1317 | + pop() |
| 1318 | + } |
| 1309 | 1319 | |
| 1310 | | - // Flame glow |
| 1311 | | - noStroke() |
| 1312 | | - fill(255, 200, 0, 30 + sin(frameCount * 0.2) * 20) |
| 1313 | | - ellipse(0, 0, 30, 30) |
| 1314 | | - fill(255, 150, 0, 50 + sin(frameCount * 0.3) * 30) |
| 1315 | | - ellipse(0, 0, 20, 25) |
| 1320 | + // Add panel seams (the ropes/stitching between panels) |
| 1321 | + stroke(60, 40, 20, 110) |
| 1322 | + strokeWeight(1) |
| 1323 | + for (let i = 0; i < numPanels; i++) { |
| 1324 | + let angle = (TWO_PI / numPanels) * i |
| 1325 | + let x1 = cos(angle) * this.radius * 0.2 |
| 1326 | + let y1 = sin(angle) * this.radius * 0.2 |
| 1327 | + let x2 = cos(angle) * this.radius * 0.95 |
| 1328 | + let y2 = sin(angle) * this.radius * 0.95 |
| 1329 | + line(x1, y1, x2, y2) |
| 1330 | + } |
| 1331 | + |
| 1332 | + // Add circular reinforcement bands |
| 1333 | + noFill() |
| 1334 | + stroke(80, 50, 30, 80) |
| 1335 | + strokeWeight(1.5) |
| 1336 | + ellipse(0, 0, this.radius * 1.4) |
| 1337 | + ellipse(0, 0, this.radius * 0.8) |
| 1316 | 1338 | |
| 1317 | | - // Animated flame |
| 1318 | | - push() |
| 1319 | | - let flameHeight = 12 + sin(frameCount * 0.4) * 4 |
| 1320 | | - let flameWave = sin(frameCount * 0.3) * 2 |
| 1339 | + // Matte fabric shading (subtle, non-glossy) |
| 1340 | + noStroke() |
| 1341 | + // Soft radial shading toward top-left to imply ambient light without specular shine |
| 1342 | + for ( |
| 1343 | + let r = this.radius * 1.2; |
| 1344 | + r > this.radius * 0.2; |
| 1345 | + r -= this.radius * 0.15 |
| 1346 | + ) { |
| 1347 | + fill(255, 255, 255, 8) // very low alpha |
| 1348 | + ellipse(-this.radius * 0.25, -this.radius * 0.35, r * 0.25, r * 0.18) |
| 1349 | + } |
| 1350 | + // Global matte overlay to reduce plastic look |
| 1351 | + noStroke() |
| 1352 | + fill(230, 210, 190, 18) |
| 1353 | + ellipse(0, 0, this.radius * 2, this.radius * 2) |
| 1354 | + |
| 1355 | + // Bottom opening of balloon (where flame goes) |
| 1356 | + fill(40, 20, 10) |
| 1357 | + ellipse(0, this.radius * 0.9, this.radius * 0.4, this.radius * 0.15) |
| 1358 | + |
| 1359 | + // Support ropes from balloon to basket |
| 1360 | + stroke(80, 60, 40) |
| 1361 | + strokeWeight(2) |
| 1362 | + // Four support ropes |
| 1363 | + line(-this.radius * 0.3, this.radius * 0.85, -8, this.radius + 20) |
| 1364 | + line(this.radius * 0.3, this.radius * 0.85, 8, this.radius + 20) |
| 1365 | + line(-this.radius * 0.15, this.radius * 0.9, -4, this.radius + 20) |
| 1366 | + line(this.radius * 0.15, this.radius * 0.9, 4, this.radius + 20) |
| 1367 | + |
| 1368 | + // FLAME EFFECT (between balloon and basket) |
| 1369 | + push() |
| 1370 | + translate(0, this.radius + 10) |
| 1321 | 1371 | |
| 1322 | | - // Outer flame (orange) |
| 1323 | | - fill(255, 150, 0) |
| 1324 | | - beginShape() |
| 1325 | | - vertex(-5, 5) |
| 1326 | | - bezierVertex( |
| 1327 | | - -5 + flameWave, |
| 1328 | | - -flameHeight * 0.5, |
| 1329 | | - -2 + flameWave, |
| 1330 | | - -flameHeight * 0.8, |
| 1331 | | - flameWave, |
| 1332 | | - -flameHeight |
| 1333 | | - ) |
| 1334 | | - bezierVertex( |
| 1335 | | - 2 + flameWave, |
| 1336 | | - -flameHeight * 0.8, |
| 1337 | | - 5 + flameWave, |
| 1338 | | - -flameHeight * 0.5, |
| 1339 | | - 5, |
| 1340 | | - 5 |
| 1341 | | - ) |
| 1342 | | - endShape(CLOSE) |
| 1372 | + // Flame glow |
| 1373 | + noStroke() |
| 1374 | + fill(255, 200, 0, 30 + sin(frameCount * 0.2) * 20) |
| 1375 | + ellipse(0, 0, 30, 30) |
| 1376 | + fill(255, 150, 0, 50 + sin(frameCount * 0.3) * 30) |
| 1377 | + ellipse(0, 0, 20, 25) |
| 1343 | 1378 | |
| 1344 | | - // Inner flame (yellow/white) |
| 1345 | | - fill(255, 255, 150) |
| 1346 | | - beginShape() |
| 1347 | | - vertex(-2, 5) |
| 1348 | | - bezierVertex( |
| 1349 | | - -2 + flameWave * 0.5, |
| 1350 | | - -flameHeight * 0.3, |
| 1351 | | - -1 + flameWave * 0.5, |
| 1352 | | - -flameHeight * 0.5, |
| 1353 | | - flameWave * 0.5, |
| 1354 | | - -flameHeight * 0.7 |
| 1355 | | - ) |
| 1356 | | - bezierVertex( |
| 1357 | | - 1 + flameWave * 0.5, |
| 1358 | | - -flameHeight * 0.5, |
| 1359 | | - 2 + flameWave * 0.5, |
| 1360 | | - -flameHeight * 0.3, |
| 1361 | | - 2, |
| 1362 | | - 5 |
| 1363 | | - ) |
| 1364 | | - endShape(CLOSE) |
| 1379 | + // Animated flame |
| 1380 | + push() |
| 1381 | + let flameHeight = 12 + sin(frameCount * 0.4) * 4 |
| 1382 | + let flameWave = sin(frameCount * 0.3) * 2 |
| 1383 | + |
| 1384 | + // Outer flame (orange) |
| 1385 | + fill(255, 150, 0) |
| 1386 | + beginShape() |
| 1387 | + vertex(-5, 5) |
| 1388 | + bezierVertex( |
| 1389 | + -5 + flameWave, |
| 1390 | + -flameHeight * 0.5, |
| 1391 | + -2 + flameWave, |
| 1392 | + -flameHeight * 0.8, |
| 1393 | + flameWave, |
| 1394 | + -flameHeight |
| 1395 | + ) |
| 1396 | + bezierVertex( |
| 1397 | + 2 + flameWave, |
| 1398 | + -flameHeight * 0.8, |
| 1399 | + 5 + flameWave, |
| 1400 | + -flameHeight * 0.5, |
| 1401 | + 5, |
| 1402 | + 5 |
| 1403 | + ) |
| 1404 | + endShape(CLOSE) |
| 1405 | + |
| 1406 | + // Inner flame (yellow/white) |
| 1407 | + fill(255, 255, 150) |
| 1408 | + beginShape() |
| 1409 | + vertex(-2, 5) |
| 1410 | + bezierVertex( |
| 1411 | + -2 + flameWave * 0.5, |
| 1412 | + -flameHeight * 0.3, |
| 1413 | + -1 + flameWave * 0.5, |
| 1414 | + -flameHeight * 0.5, |
| 1415 | + flameWave * 0.5, |
| 1416 | + -flameHeight * 0.7 |
| 1417 | + ) |
| 1418 | + bezierVertex( |
| 1419 | + 1 + flameWave * 0.5, |
| 1420 | + -flameHeight * 0.5, |
| 1421 | + 2 + flameWave * 0.5, |
| 1422 | + -flameHeight * 0.3, |
| 1423 | + 2, |
| 1424 | + 5 |
| 1425 | + ) |
| 1426 | + endShape(CLOSE) |
| 1365 | 1427 | |
| 1366 | | - // Flame tip (bright white) |
| 1367 | | - fill(255, 255, 255) |
| 1368 | | - ellipse(flameWave * 0.5, -flameHeight * 0.5, 3, 5) |
| 1369 | | - pop() |
| 1428 | + // Flame tip (bright white) |
| 1429 | + fill(255, 255, 255) |
| 1430 | + ellipse(flameWave * 0.5, -flameHeight * 0.5, 3, 5) |
| 1431 | + pop() |
| 1370 | 1432 | |
| 1371 | | - pop() |
| 1433 | + pop() |
| 1372 | 1434 | |
| 1373 | | - // BIGGER, MORE DETAILED BASKET |
| 1374 | | - push() |
| 1375 | | - translate(0, this.radius + 25) |
| 1435 | + // BIGGER, MORE DETAILED BASKET WITH SWING |
| 1436 | + push() |
| 1437 | + translate(0, this.radius + 25) |
| 1438 | + |
| 1439 | + // ENHANCED: Apply basket swing |
| 1440 | + if (windActive) { |
| 1441 | + rotate(this.basketSwing * 0.02) |
| 1442 | + } |
| 1376 | 1443 | |
| 1377 | | - // Basket shadow |
| 1378 | | - noStroke() |
| 1379 | | - fill(0, 0, 0, 20) |
| 1380 | | - rect(-11, 2, 22, 15, 2) |
| 1444 | + // Basket shadow |
| 1445 | + noStroke() |
| 1446 | + fill(0, 0, 0, 20) |
| 1447 | + rect(-11, 2, 22, 15, 2) |
| 1381 | 1448 | |
| 1382 | | - // Main basket - bigger to see ant better |
| 1383 | | - fill(101, 67, 33) |
| 1384 | | - stroke(80, 50, 20) |
| 1385 | | - strokeWeight(1.5) |
| 1386 | | - rect(-10, 0, 20, 14, 2) // Bigger basket |
| 1449 | + // Main basket - bigger to see ant better |
| 1450 | + fill(101, 67, 33) |
| 1451 | + stroke(80, 50, 20) |
| 1452 | + strokeWeight(1.5) |
| 1453 | + rect(-10, 0, 20, 14, 2) // Bigger basket |
| 1387 | 1454 | |
| 1388 | | - // Woven basket texture |
| 1389 | | - stroke(80, 50, 20, 150) |
| 1390 | | - strokeWeight(1) |
| 1391 | | - // Vertical weaves |
| 1392 | | - for (let i = -8; i < 8; i += 3) { |
| 1393 | | - line(i, 1, i, 13) |
| 1394 | | - } |
| 1395 | | - // Horizontal weaves |
| 1396 | | - for (let i = 3; i < 12; i += 3) { |
| 1397 | | - line(-9, i, 9, i) |
| 1398 | | - } |
| 1455 | + // Woven basket texture |
| 1456 | + stroke(80, 50, 20, 150) |
| 1457 | + strokeWeight(1) |
| 1458 | + // Vertical weaves |
| 1459 | + for (let i = -8; i < 8; i += 3) { |
| 1460 | + line(i, 1, i, 13) |
| 1461 | + } |
| 1462 | + // Horizontal weaves |
| 1463 | + for (let i = 3; i < 12; i += 3) { |
| 1464 | + line(-9, i, 9, i) |
| 1465 | + } |
| 1399 | 1466 | |
| 1400 | | - // Basket rim (thicker, more pronounced) |
| 1401 | | - stroke(60, 40, 20) |
| 1402 | | - strokeWeight(2) |
| 1403 | | - line(-10, 0, 10, 0) |
| 1467 | + // Basket rim (thicker, more pronounced) |
| 1468 | + stroke(60, 40, 20) |
| 1469 | + strokeWeight(2) |
| 1470 | + line(-10, 0, 10, 0) |
| 1404 | 1471 | |
| 1405 | | - // Corner reinforcements |
| 1406 | | - fill(80, 50, 20) |
| 1407 | | - noStroke() |
| 1408 | | - ellipse(-9, 0, 3) |
| 1409 | | - ellipse(9, 0, 3) |
| 1472 | + // Corner reinforcements |
| 1473 | + fill(80, 50, 20) |
| 1474 | + noStroke() |
| 1475 | + ellipse(-9, 0, 3) |
| 1476 | + ellipse(9, 0, 3) |
| 1410 | 1477 | |
| 1411 | | - pop() |
| 1478 | + pop() |
| 1412 | 1479 | |
| 1413 | | - // DETAILED ANT PILOT (bigger, more visible) |
| 1414 | | - push() |
| 1415 | | - translate(0, this.radius + 28) |
| 1480 | + // DETAILED ANT PILOT (bigger, more visible) |
| 1481 | + push() |
| 1482 | + translate(0, this.radius + 28) |
| 1483 | + |
| 1484 | + // ENHANCED: Ant holds on tighter in wind |
| 1485 | + if (windActive) { |
| 1486 | + rotate(-this.basketSwing * 0.01) // Ant leans opposite to basket |
| 1487 | + } |
| 1416 | 1488 | |
| 1417 | | - // Ant body |
| 1418 | | - fill(20) |
| 1419 | | - noStroke() |
| 1420 | | - ellipse(0, 0, 8, 5) // Thorax |
| 1421 | | - ellipse(0, -3, 6, 5) // Head |
| 1422 | | - ellipse(0, 3, 7, 6) // Abdomen |
| 1489 | + // Ant body |
| 1490 | + fill(20) |
| 1491 | + noStroke() |
| 1492 | + ellipse(0, 0, 8, 5) // Thorax |
| 1493 | + ellipse(0, -3, 6, 5) // Head |
| 1494 | + ellipse(0, 3, 7, 6) // Abdomen |
| 1423 | 1495 | |
| 1424 | | - // Ant eyes |
| 1425 | | - fill(255, 100, 100) |
| 1426 | | - ellipse(-2, -3, 2) |
| 1427 | | - ellipse(2, -3, 2) |
| 1496 | + // Ant eyes |
| 1497 | + fill(255, 100, 100) |
| 1498 | + ellipse(-2, -3, 2) |
| 1499 | + ellipse(2, -3, 2) |
| 1428 | 1500 | |
| 1429 | | - // Antennae |
| 1430 | | - stroke(20) |
| 1431 | | - strokeWeight(1) |
| 1432 | | - line(-1, -5, -3, -8) |
| 1433 | | - line(1, -5, 3, -8) |
| 1501 | + // Antennae |
| 1502 | + stroke(20) |
| 1503 | + strokeWeight(1) |
| 1504 | + line(-1, -5, -3, -8) |
| 1505 | + line(1, -5, 3, -8) |
| 1434 | 1506 | |
| 1435 | | - // Little ant arms holding basket edge |
| 1436 | | - strokeWeight(1.5) |
| 1437 | | - line(-3, 0, -6, -3) |
| 1438 | | - line(3, 0, 6, -3) |
| 1507 | + // Little ant arms holding basket edge |
| 1508 | + strokeWeight(1.5) |
| 1509 | + line(-3, 0, -6, -3) |
| 1510 | + line(3, 0, 6, -3) |
| 1439 | 1511 | |
| 1440 | | - // Ant legs visible over basket edge |
| 1441 | | - line(-2, 2, -4, 5) |
| 1442 | | - line(2, 2, 4, 5) |
| 1512 | + // Ant legs visible over basket edge |
| 1513 | + line(-2, 2, -4, 5) |
| 1514 | + line(2, 2, 4, 5) |
| 1443 | 1515 | |
| 1444 | | - // Optional: Tiny pilot goggles |
| 1445 | | - stroke(100, 50, 0) |
| 1446 | | - strokeWeight(1) |
| 1447 | | - noFill() |
| 1448 | | - ellipse(-2, -3, 3) |
| 1449 | | - ellipse(2, -3, 3) |
| 1450 | | - line(-0.5, -3, 0.5, -3) |
| 1516 | + // Optional: Tiny pilot goggles |
| 1517 | + stroke(100, 50, 0) |
| 1518 | + strokeWeight(1) |
| 1519 | + noFill() |
| 1520 | + ellipse(-2, -3, 3) |
| 1521 | + ellipse(2, -3, 3) |
| 1522 | + line(-0.5, -3, 0.5, -3) |
| 1451 | 1523 | |
| 1452 | | - pop() |
| 1524 | + pop() |
| 1453 | 1525 | |
| 1454 | | - // Sandbags hanging from basket (optional detail) |
| 1455 | | - push() |
| 1456 | | - translate(0, this.radius + 25) |
| 1457 | | - fill(80, 60, 40) |
| 1458 | | - noStroke() |
| 1459 | | - ellipse(-12, 10, 4, 5) |
| 1460 | | - ellipse(12, 10, 4, 5) |
| 1461 | | - // Sandbag ropes |
| 1462 | | - stroke(60, 40, 20) |
| 1463 | | - strokeWeight(0.5) |
| 1464 | | - line(-10, 7, -12, 10) |
| 1465 | | - line(10, 7, 12, 10) |
| 1466 | | - pop() |
| 1526 | + // Sandbags hanging from basket (optional detail) |
| 1527 | + push() |
| 1528 | + translate(0, this.radius + 25) |
| 1529 | + |
| 1530 | + // ENHANCED: Sandbags swing in wind |
| 1531 | + if (windActive) { |
| 1532 | + rotate(this.basketSwing * 0.03) |
| 1533 | + } |
| 1534 | + |
| 1535 | + fill(80, 60, 40) |
| 1536 | + noStroke() |
| 1537 | + ellipse(-12, 10, 4, 5) |
| 1538 | + ellipse(12, 10, 4, 5) |
| 1539 | + // Sandbag ropes |
| 1540 | + stroke(60, 40, 20) |
| 1541 | + strokeWeight(0.5) |
| 1542 | + line(-10, 7, -12, 10) |
| 1543 | + line(10, 7, 12, 10) |
| 1544 | + pop() |
| 1467 | 1545 | |
| 1468 | | - pop() |
| 1469 | | - } else if (this.type === 'beetle') { |
| 1546 | + pop() |
| 1547 | + } else if (this.type === 'beetle') { |
| 1470 | 1548 | push() |
| 1471 | 1549 | rotate(this.rotation) |
| 1472 | 1550 | |