diff --git a/assets/worlds/mainworld.json b/assets/worlds/mainworld.json index ca552aa..40d085e 100644 --- a/assets/worlds/mainworld.json +++ b/assets/worlds/mainworld.json @@ -8,13 +8,6 @@ "y": 50.0 }, "fish": [ - { - "x": 500.0, - "y": 300.0 - }, - { - "x": 800.0, - "y": 200.0 - } + {"x":49,"y":801},{"x":570,"y":594},{"x":761,"y":186},{"x":760,"y":940},{"x":241,"y":32},{"x":501,"y":18},{"x":487,"y":37},{"x":802,"y":849},{"x":864,"y":43},{"x":544,"y":886},{"x":987,"y":710},{"x":949,"y":404},{"x":694,"y":32},{"x":364,"y":899},{"x":26,"y":849},{"x":253,"y":627},{"x":39,"y":547},{"x":307,"y":730},{"x":133,"y":967},{"x":861,"y":76},{"x":199,"y":229},{"x":617,"y":532},{"x":391,"y":388},{"x":491,"y":816},{"x":539,"y":243},{"x":222,"y":288},{"x":81,"y":784},{"x":432,"y":830},{"x":741,"y":737},{"x":426,"y":480},{"x":591,"y":437},{"x":903,"y":380},{"x":653,"y":349},{"x":684,"y":235},{"x":797,"y":438},{"x":546,"y":615},{"x":497,"y":523},{"x":406,"y":468},{"x":173,"y":183},{"x":641,"y":187},{"x":517,"y":294},{"x":527,"y":650},{"x":962,"y":237},{"x":25,"y":868},{"x":16,"y":369},{"x":434,"y":712},{"x":632,"y":315},{"x":172,"y":421},{"x":450,"y":53},{"x":731,"y":220},{"x":532,"y":467},{"x":816,"y":497},{"x":948,"y":539},{"x":467,"y":829},{"x":533,"y":809},{"x":146,"y":989},{"x":850,"y":245},{"x":989,"y":214},{"x":203,"y":354},{"x":466,"y":611},{"x":382,"y":376},{"x":111,"y":148},{"x":411,"y":77},{"x":124,"y":418},{"x":154,"y":611},{"x":56,"y":732},{"x":800,"y":488},{"x":851,"y":668},{"x":240,"y":220},{"x":1000,"y":62},{"x":95,"y":784},{"x":700,"y":428},{"x":735,"y":517},{"x":259,"y":843},{"x":647,"y":268},{"x":668,"y":823},{"x":198,"y":241},{"x":243,"y":422},{"x":838,"y":433},{"x":642,"y":97},{"x":563,"y":974},{"x":386,"y":548},{"x":646,"y":482},{"x":691,"y":794},{"x":167,"y":485},{"x":978,"y":622},{"x":845,"y":431},{"x":529,"y":719},{"x":963,"y":145},{"x":6,"y":412},{"x":381,"y":830},{"x":918,"y":118},{"x":915,"y":27},{"x":618,"y":262},{"x":250,"y":635},{"x":100,"y":500},{"x":442,"y":321},{"x":769,"y":767},{"x":714,"y":204},{"x":506,"y":872},{"x":575,"y":178},{"x":256,"y":411},{"x":921,"y":617},{"x":971,"y":214},{"x":726,"y":702},{"x":103,"y":450},{"x":501,"y":134},{"x":265,"y":993},{"x":31,"y":63},{"x":502,"y":448},{"x":46,"y":457},{"x":809,"y":184},{"x":763,"y":962},{"x":632,"y":873},{"x":916,"y":761},{"x":710,"y":720},{"x":873,"y":222},{"x":256,"y":861},{"x":246,"y":482},{"x":390,"y":812},{"x":28,"y":247},{"x":516,"y":523},{"x":869,"y":43},{"x":680,"y":740},{"x":406,"y":65},{"x":657,"y":196},{"x":692,"y":635},{"x":97,"y":993},{"x":616,"y":490},{"x":515,"y":955},{"x":412,"y":502},{"x":743,"y":565},{"x":16,"y":499},{"x":324,"y":582},{"x":871,"y":62},{"x":128,"y":476},{"x":716,"y":525},{"x":627,"y":2},{"x":730,"y":913},{"x":704,"y":522},{"x":242,"y":934},{"x":172,"y":277},{"x":651,"y":948},{"x":349,"y":263},{"x":731,"y":967},{"x":382,"y":762},{"x":217,"y":15},{"x":49,"y":25},{"x":583,"y":110},{"x":700,"y":620},{"x":230,"y":537},{"x":285,"y":978},{"x":4,"y":791},{"x":939,"y":866},{"x":371,"y":342},{"x":759,"y":870},{"x":892,"y":103},{"x":57,"y":129},{"x":233,"y":383},{"x":171,"y":472},{"x":173,"y":842},{"x":516,"y":464},{"x":407,"y":458},{"x":963,"y":231},{"x":526,"y":253},{"x":815,"y":857},{"x":175,"y":909},{"x":993,"y":255},{"x":129,"y":390},{"x":76,"y":997},{"x":833,"y":174},{"x":501,"y":396},{"x":897,"y":218},{"x":876,"y":601},{"x":41,"y":165},{"x":993,"y":473},{"x":606,"y":308},{"x":831,"y":382},{"x":517,"y":828},{"x":984,"y":26},{"x":286,"y":712},{"x":422,"y":311},{"x":448,"y":103},{"x":260,"y":229},{"x":5,"y":738},{"x":283,"y":346},{"x":744,"y":463},{"x":634,"y":719},{"x":446,"y":977},{"x":220,"y":89},{"x":745,"y":866},{"x":851,"y":860},{"x":369,"y":940},{"x":828,"y":577},{"x":350,"y":337},{"x":334,"y":378},{"x":203,"y":248},{"x":665,"y":788},{"x":334,"y":927},{"x":307,"y":764},{"x":500,"y":763},{"x":613,"y":843},{"x":384,"y":253},{"x":956,"y":569},{"x":846,"y":137},{"x":105,"y":728},{"x":686,"y":226},{"x":657,"y":52},{"x":592,"y":433},{"x":997,"y":820},{"x":746,"y":389},{"x":405,"y":448},{"x":973,"y":19},{"x":538,"y":518},{"x":790,"y":275},{"x":633,"y":738},{"x":128,"y":484},{"x":603,"y":371},{"x":932,"y":21},{"x":582,"y":445},{"x":438,"y":793},{"x":963,"y":69},{"x":158,"y":263},{"x":988,"y":297},{"x":249,"y":227},{"x":245,"y":466},{"x":131,"y":495},{"x":620,"y":266},{"x":505,"y":384},{"x":813,"y":647},{"x":113,"y":66},{"x":757,"y":10},{"x":2,"y":707},{"x":540,"y":140},{"x":562,"y":691},{"x":484,"y":433},{"x":859,"y":455},{"x":248,"y":117},{"x":36,"y":432},{"x":798,"y":754},{"x":611,"y":291},{"x":664,"y":770},{"x":299,"y":788},{"x":433,"y":920},{"x":540,"y":739},{"x":201,"y":829},{"x":972,"y":362},{"x":811,"y":120},{"x":941,"y":670},{"x":186,"y":448},{"x":549,"y":611},{"x":206,"y":387},{"x":973,"y":437},{"x":700,"y":709},{"x":472,"y":243},{"x":971,"y":518},{"x":184,"y":540},{"x":271,"y":257},{"x":290,"y":895},{"x":546,"y":7},{"x":256,"y":542},{"x":418,"y":553},{"x":816,"y":875},{"x":908,"y":547},{"x":315,"y":354},{"x":266,"y":471},{"x":242,"y":88},{"x":785,"y":52},{"x":497,"y":47},{"x":466,"y":279},{"x":750,"y":690},{"x":329,"y":296},{"x":545,"y":715},{"x":508,"y":562},{"x":993,"y":467},{"x":703,"y":733},{"x":824,"y":11},{"x":419,"y":337},{"x":393,"y":229},{"x":898,"y":261},{"x":264,"y":708},{"x":711,"y":768},{"x":568,"y":409},{"x":473,"y":342},{"x":329,"y":53},{"x":95,"y":815},{"x":783,"y":977},{"x":48,"y":551},{"x":635,"y":931},{"x":653,"y":86},{"x":9,"y":153},{"x":955,"y":660},{"x":480,"y":716},{"x":936,"y":622},{"x":607,"y":221},{"x":423,"y":545},{"x":507,"y":668},{"x":676,"y":957},{"x":253,"y":515},{"x":327,"y":495},{"x":965,"y":808},{"x":2,"y":807},{"x":276,"y":199},{"x":584,"y":75},{"x":770,"y":51},{"x":667,"y":717},{"x":944,"y":913},{"x":982,"y":977},{"x":618,"y":482},{"x":372,"y":545},{"x":507,"y":518},{"x":604,"y":492},{"x":772,"y":730},{"x":350,"y":141},{"x":783,"y":437},{"x":282,"y":714},{"x":269,"y":691},{"x":991,"y":386},{"x":234,"y":196},{"x":908,"y":635},{"x":785,"y":340},{"x":125,"y":712},{"x":466,"y":210},{"x":280,"y":185},{"x":995,"y":466},{"x":589,"y":258},{"x":700,"y":120},{"x":855,"y":323},{"x":690,"y":355},{"x":755,"y":353},{"x":378,"y":970},{"x":865,"y":270},{"x":220,"y":62},{"x":685,"y":848},{"x":670,"y":907},{"x":710,"y":671},{"x":209,"y":68},{"x":642,"y":470},{"x":104,"y":642},{"x":631,"y":328},{"x":898,"y":424},{"x":909,"y":427},{"x":189,"y":141},{"x":259,"y":993},{"x":332,"y":791},{"x":842,"y":778},{"x":63,"y":390},{"x":146,"y":895},{"x":230,"y":274},{"x":316,"y":447},{"x":603,"y":59},{"x":377,"y":841},{"x":602,"y":119},{"x":728,"y":557},{"x":395,"y":514},{"x":379,"y":754},{"x":822,"y":840},{"x":860,"y":478},{"x":695,"y":360},{"x":156,"y":784},{"x":241,"y":353},{"x":195,"y":199},{"x":284,"y":110},{"x":484,"y":966},{"x":889,"y":370},{"x":246,"y":684},{"x":710,"y":345},{"x":382,"y":635},{"x":447,"y":948},{"x":741,"y":274},{"x":224,"y":883},{"x":99,"y":37},{"x":472,"y":803},{"x":141,"y":397},{"x":371,"y":602},{"x":7,"y":482},{"x":184,"y":990},{"x":555,"y":313},{"x":573,"y":886},{"x":167,"y":365},{"x":810,"y":721},{"x":958,"y":767},{"x":891,"y":561},{"x":314,"y":987},{"x":156,"y":95},{"x":349,"y":542},{"x":775,"y":35},{"x":121,"y":655},{"x":311,"y":242},{"x":534,"y":135},{"x":71,"y":134},{"x":367,"y":896},{"x":447,"y":524},{"x":120,"y":421},{"x":878,"y":398},{"x":469,"y":822},{"x":483,"y":966},{"x":240,"y":880},{"x":759,"y":980},{"x":531,"y":759},{"x":395,"y":118},{"x":354,"y":360},{"x":173,"y":924},{"x":550,"y":958},{"x":888,"y":379},{"x":244,"y":448},{"x":999,"y":554},{"x":941,"y":455},{"x":798,"y":916},{"x":134,"y":123},{"x":90,"y":440},{"x":923,"y":263},{"x":405,"y":595},{"x":194,"y":387},{"x":370,"y":697},{"x":943,"y":888},{"x":607,"y":336},{"x":168,"y":105},{"x":874,"y":66},{"x":675,"y":50},{"x":601,"y":242},{"x":925,"y":728},{"x":643,"y":609},{"x":769,"y":713},{"x":410,"y":913},{"x":153,"y":776},{"x":775,"y":949},{"x":184,"y":93},{"x":624,"y":632},{"x":899,"y":804},{"x":909,"y":327},{"x":371,"y":510},{"x":663,"y":415},{"x":337,"y":542},{"x":248,"y":104},{"x":925,"y":450},{"x":310,"y":925},{"x":4,"y":550},{"x":559,"y":652},{"x":671,"y":296},{"x":414,"y":60},{"x":972,"y":505},{"x":221,"y":147},{"x":318,"y":592},{"x":861,"y":656},{"x":258,"y":675},{"x":565,"y":390},{"x":703,"y":236},{"x":227,"y":76},{"x":989,"y":252},{"x":924,"y":419},{"x":983,"y":971},{"x":795,"y":244},{"x":256,"y":498},{"x":517,"y":674},{"x":89,"y":197},{"x":366,"y":234},{"x":41,"y":952},{"x":487,"y":981},{"x":939,"y":922},{"x":384,"y":315},{"x":958,"y":57},{"x":499,"y":152},{"x":716,"y":167},{"x":167,"y":301},{"x":781,"y":964},{"x":101,"y":215},{"x":605,"y":396},{"x":31,"y":973},{"x":128,"y":831},{"x":685,"y":701},{"x":150,"y":507},{"x":663,"y":77},{"x":792,"y":561},{"x":398,"y":281},{"x":168,"y":936},{"x":8,"y":266},{"x":19,"y":723},{"x":377,"y":975},{"x":68,"y":114},{"x":191,"y":784},{"x":94,"y":222},{"x":986,"y":578},{"x":474,"y":160},{"x":936,"y":945},{"x":603,"y":778},{"x":105,"y":845},{"x":955,"y":583},{"x":832,"y":905},{"x":264,"y":132},{"x":219,"y":747},{"x":515,"y":562},{"x":178,"y":198},{"x":999,"y":1},{"x":470,"y":345},{"x":450,"y":490},{"x":967,"y":306},{"x":257,"y":360},{"x":632,"y":26},{"x":916,"y":382},{"x":631,"y":194},{"x":492,"y":235},{"x":479,"y":373},{"x":887,"y":154},{"x":65,"y":181},{"x":956,"y":879},{"x":567,"y":578},{"x":718,"y":617},{"x":464,"y":243},{"x":545,"y":410},{"x":923,"y":340},{"x":978,"y":716},{"x":277,"y":261},{"x":462,"y":600},{"x":687,"y":507} ] } \ No newline at end of file diff --git a/src/entities/fish.rs b/src/entities/fish.rs index 452d9ed..8982d0b 100644 --- a/src/entities/fish.rs +++ b/src/entities/fish.rs @@ -1,30 +1,51 @@ -use rand::{Rng, prelude::ThreadRng}; +use rand::{prelude::ThreadRng, Rng}; use raylib::prelude::*; -use crate::{gamecore::GameCore, lib::utils::triangles::rotate_vector, player::Player, world::World}; +use crate::{ + gamecore::GameCore, lib::utils::triangles::rotate_vector, player::Player, world::World, +}; const FISH_FOLLOW_PLAYER_DISTANCE: f32 = 30.0; const FISH_FOLLOW_PLAYER_SPEED: f32 = 1.8; const FISH_FOLLOW_PLAYER_SPEED_FAST: f32 = FISH_FOLLOW_PLAYER_SPEED * 3.0; const FISH_ATTACH_RADIUS: f32 = 20.0; +const FISH_VISION: f32 = 25.0; +const FISH_MAX_SPEED: f32 = 2.0; +const FISH_MAX_FORCE: f32 = 0.05; +const FISH_FACTOR_ATTRACTION: f32 = 1.0; +const FISH_FACTOR_PLAYER: f32 = 0.1; +const FISH_FACTOR_COHESION: f32 = 0.1; +const FISH_SEPARATION_DISTANCE: f32 = 15.0; +const FISH_FACTOR_SEPARATION: f32 = 1.5; + #[derive(Debug, Clone)] pub struct FishEntity { position: Vector2, direction: Vector2, + velocity: Vector2, pub following_player: bool, size: Vector2, - rng: ThreadRng + rng: ThreadRng, + color: Color, } impl FishEntity { pub fn new(position: Vector2) -> Self { + let mut rng = rand::thread_rng(); Self { position: position, direction: Vector2::zero(), + velocity: Vector2::zero(), following_player: false, size: Vector2 { x: 5.0, y: 8.0 }, - rng: rand::thread_rng() + color: Color { + r: rng.gen_range(128..225), + g: rng.gen_range(128..225), + b: rng.gen_range(128..225), + a: 140, + }, + rng, } } @@ -37,33 +58,80 @@ impl FishEntity { } pub fn handle_follow_player(&mut self, player: &Player, dt: f64, other_fish: &Vec) { - // Distance and direction to player - let dist_to_player = player.position - self.position; - let dist_to_player_lin = self.position.distance_to(player.position); - let mut direction_to_player = dist_to_player; - direction_to_player.normalize(); + let mut acceleration: Vector2 = Vector2::zero(); - // Fish movement - let movement; - - // Random variance - let variance = self.rng.gen_range(500.0..1000.0) / 1000.0; - - // If the fish is double its follow distance from the player - if dist_to_player_lin.abs() > (FISH_FOLLOW_PLAYER_DISTANCE * 2.0) { - movement = direction_to_player * FISH_FOLLOW_PLAYER_SPEED_FAST * variance; - } else { - // Move slowly in the direction of the player unless too close - if dist_to_player_lin.abs() > FISH_FOLLOW_PLAYER_DISTANCE { - movement = direction_to_player * FISH_FOLLOW_PLAYER_SPEED * variance; - } else { - movement = Vector2::zero(); + let mut steer: Vector2 = Vector2::zero(); + let mut count1: u16 = 0; + let mut sum1: Vector2 = Vector2::zero(); + let mut count2: u16 = 0; + let mut sum2: Vector2 = Vector2::zero(); + let mut count3: u16 = 0; + // separation + for i in other_fish { + let dist = (self.position - i.position).length(); + if dist < FISH_SEPARATION_DISTANCE && dist > 0.0 { + let mut diff: Vector2 = self.position - i.position; + diff.normalize(); + diff /= dist; + steer += diff; + count1 += 1; + } + if dist < FISH_VISION && dist > 0.0 { + sum1 += i.direction; + count2 += 1; + sum2 += i.position; + count3 += 1; } } + if count1 > 0 { + steer /= count1 as f32; + } + if steer.x != 0.0 || steer.y != 0.0 { + steer.normalize(); + steer *= FISH_MAX_SPEED; + steer -= self.velocity; + steer.x = f32::min(f32::max(steer.x, -FISH_MAX_FORCE), FISH_MAX_FORCE); + steer.y = f32::min(f32::max(steer.y, -FISH_MAX_FORCE), FISH_MAX_FORCE); + acceleration += steer * FISH_FACTOR_SEPARATION; + } + + // attraction + if count2 > 0 { + sum1 /= count2 as f32; + sum1.normalize(); + sum1 *= FISH_MAX_SPEED; + sum1 -= self.velocity; + sum1.x = f32::min(f32::max(sum1.x, -FISH_MAX_FORCE), FISH_MAX_FORCE); + sum1.y = f32::min(f32::max(sum1.y, -FISH_MAX_FORCE), FISH_MAX_FORCE); + acceleration += sum1 * FISH_FACTOR_ATTRACTION; + } + + // cohesion + if count3 > 0 { + sum2 /= count3 as f32; + let mut desired: Vector2 = sum2 - self.position; + + desired.normalize(); + desired *= FISH_MAX_SPEED; + + desired.x = f32::min(f32::max(desired.x, -FISH_MAX_FORCE), FISH_MAX_FORCE); + desired.y = f32::min(f32::max(desired.y, -FISH_MAX_FORCE), FISH_MAX_FORCE); + + acceleration += desired * FISH_FACTOR_COHESION; + } + + // turn to player + let mut player_factor: Vector2 = player.position - self.position; + player_factor.normalize(); + acceleration += player_factor * FISH_FACTOR_PLAYER; // Move the fish - self.direction = direction_to_player; - self.position += movement; + self.direction = self.velocity.normalized(); + self.velocity += acceleration; + + self.velocity.x = f32::min(f32::max(self.velocity.x, -FISH_MAX_SPEED), FISH_MAX_SPEED); + self.velocity.y = f32::min(f32::max(self.velocity.y, -FISH_MAX_SPEED), FISH_MAX_SPEED); + self.position += self.velocity; } pub fn handle_free_movement(&mut self, player: &mut Player, dt: f64) { @@ -76,6 +144,7 @@ impl FishEntity { // Handle player picking up fish if player.position.distance_to(self.position).abs() <= player.size.y * 2.2 { self.following_player = true; + self.velocity = self.direction.normalized(); // Add currency to the player player.coins += 1; @@ -127,7 +196,7 @@ impl FishEntity { self.position + fish_front, self.position + fish_bl, self.position + fish_br, - Color::BLACK, + self.color, ); } }