diff --git a/js/elements/cpg-village.js b/js/elements/cpg-village.js index d61d801..f99855b 100755 --- a/js/elements/cpg-village.js +++ b/js/elements/cpg-village.js @@ -2,7 +2,7 @@ import { VILLAGER } from '/js/objects/villager.js'; import Bandit from '/js/objects/bandit.js'; import Apple, { APPLE } from '/js/objects/apple.js'; import Home, { HOME } from '/js/objects/home.js'; -import { randomNumber, randomX } from '../utility.js'; +import { randomNumber } from '../utility.js'; import { ROTTEN_APPLE } from '../objects/apple.js'; import { BANDIT } from '../objects/bandit.js'; @@ -55,7 +55,6 @@ export default class CpgVillage extends HTMLElement { this.updateDelay = parseFloat(this.getAttribute('update-delay')) || 1; this.appleAgeRate = parseFloat(this.getAttribute('apple-age-rate')) || 1; this.banditDensity = parseFloat(this.getAttribute('bandit-density')) || 1; - this.midGameStart = parseFloat(this.getAttribute('bandit-density')) || 20000; this.villages = parseFloat(this.getAttribute('villages')) || 3; /* --------------------- */ @@ -93,10 +92,10 @@ export default class CpgVillage extends HTMLElement { } adjustMaxApples() { - if (this.villagers.length > this.maxVillagers && this.appleDensity < this.startAppleDensity * 2) { - this.appleDensity = this.appleDensity * 0.995; - } else if (this.villagers.length < this.maxVillagers && this.appleDensity > this.startAppleDensity * 0.5) { - this.appleDensity = this.appleDensity * 1.005; + if (this.villagers.length < this.maxVillagers && this.appleDensity < this.startAppleDensity * 2) { + this.appleDensity = this.appleDensity * 1.01; + } else if (this.villagers.length > this.maxVillagers && this.appleDensity > this.startAppleDensity * 0.5) { + this.appleDensity = this.appleDensity * 0.99; } } @@ -151,9 +150,6 @@ export default class CpgVillage extends HTMLElement { for (let i = 0; i < this.villages; i++) { const home = this.addHome(); home.addVillager(); - home.addVillager(); - home.addVillager(); - home.addVillager(); } } diff --git a/js/objects/bandit.js b/js/objects/bandit.js index 6ce481b..bccb614 100755 --- a/js/objects/bandit.js +++ b/js/objects/bandit.js @@ -1,11 +1,12 @@ -import { randomX, randomY, drawRect, shuffle, writeText, percentAdjust } from '../utility.js'; +import { randomX, randomY, drawRect, shuffle, writeText, percentAdjust, drawArc, drawLine } from '../utility.js'; import { movePoint2, getDistancePts } from '../utility.js'; -import { VILLAGER, WARRIOR } from './villager.js'; +import { HERO, VILLAGER, WARRIOR } from './villager.js'; export const BANDIT = 'bandit'; -const BANDIT_MAX_SPEED = 0.4; -const BANDIT_AGILITY = 0.0075; -const BANDIT_BASE_GIVE_UP_DISTANCE = 200; +const BANDIT_MAX_SPEED = 0.5; +const BANDIT_AGILITY = 0.03; +const BANDIT_BASE_GIVE_UP_DISTANCE = 1; +const EXPLORE = 'EXPLORE'; export default class Bandit { constructor({ @@ -19,7 +20,7 @@ export default class Bandit { size = 20, maxSpeed = BANDIT_MAX_SPEED * percentAdjust(0.3), agility = BANDIT_AGILITY * percentAdjust(0.3), - giveUpDistance = BANDIT_BASE_GIVE_UP_DISTANCE * percentAdjust(0.3) + giveUp = BANDIT_BASE_GIVE_UP_DISTANCE * percentAdjust(0.3) } = {}) { this.context = context; this.environment = environment; @@ -35,7 +36,7 @@ export default class Bandit { this.v = { x: 0, y: 0 }; this.maxSpeed = maxSpeed; this.agility = agility; - this.giveUpDistance = giveUpDistance; + this.giveUp = giveUp; this.impacting = false; this.type = BANDIT; this.maxAge = 10000 + (Math.random() * 25000); @@ -63,6 +64,24 @@ export default class Bandit { green: 255, blue: 255 }); + // TODO: Uncomment this to see the destination drawn on the screen + // if (this.destination) { + // drawArc({ + // context: this.context, + // x: this.destination.x, + // y: this.destination.y, + // radius: 5, + // lineWidth: 0.5, + // lineStyle: `rgb(0,0,0)` + // }); + // drawLine( + // this.context, + // this, + // this.destination, + // 2, + // 'rgb(0,0,0)' + // ) + // } } } @@ -74,44 +93,104 @@ export default class Bandit { return this.timeRemaining > 0; } + get giveUpDistance() { + return (this.environment.canvas.width * this.environment.canvas.height * this.giveUp) / (75 * 75); + } + + get radius() { + return this.size; + } + move() { - if (this.destination && this.destination.destroy) { - this.findTarget(); - } else if (this.destination && getDistancePts(this, this.destination) > this.giveUpDistance) { - this.findTarget(); - } else if (this.destination && getDistancePts(this, this.destination) > ((this.size / 2) + this.destination.radius)) { - this.moveToDestination(); - } else if (this.destination && this.destination.type === VILLAGER) { + if (this.reachedVillager()) { this.killVillager(); + } else if (this.reachedExploreDestination()) { + this.lookForNearbyVillagerOrExplore(); + } else if (this.hasNearbyVillager()) { + this.moveToDestination(); + } else if (this.isExploring()) { + this.lookForNearbyVillager(); + this.moveToDestination(); } else { - this.findTarget(); + this.lookForNearbyVillagerOrExplore(); } } - killVillager() { - this.death += (this.destination.timeRemaining * 0.40); - this.destination.home.heroTargets.push(this); - this.destination.destroy = true; - this.destination = undefined; + lookForNearbyVillagerOrExplore() { + const foundVillager = this.getNearbyVillager(); + if (foundVillager) { + this.destination = foundVillager; + } else { + this.destination = { + x: randomX(), + y: randomY(), + type: EXPLORE, + radius: 1 + }; + } } - findTarget() { - this.destination = undefined; + getNearbyVillager() { + let foundVillager = undefined; if (this.grids && Math.random()) { this.grids.forEach((grid, index) => { - if (!this.destination) { + if (!foundVillager) { const objs = this.environment.grids[index].rows[grid.row][grid.col] || []; - this.destination = this.findTargetFromList(objs); + foundVillager = this.findTargetFromList(objs); } }); } - if (!this.destination) { - this.destination = this.findTargetFromList(this.environment.objects); + if (!foundVillager) { + foundVillager = this.findTargetFromList(this.environment.objects); + } + return foundVillager; + } + + lookForNearbyVillager() { + const foundVillager = this.getNearbyVillager(); + if (foundVillager) { + this.destination = foundVillager; } } + isExploring() { + return this.destination && this.destination.type === EXPLORE; + } + + targetIsClose() { + return getDistancePts(this, this.destination) < this.giveUpDistance; + } + + hasNearbyVillager() { + return this.destination && this.destination.type === VILLAGER && this.targetIsClose(); + } + + reachedExploreDestination() { + return this.reachedTarget() && this.destination.type === EXPLORE; + } + + reachedVillager() { + return this.reachedTarget() && this.destination.type === VILLAGER; + } + + reachedTarget() { + return this.destination && getDistancePts(this, this.destination) < 1 + this.destination.radius; + } + + killVillager() { + this.death += (this.destination.timeRemaining * 0.80); + this.destination.home.heroTargets.push(this); + this.destination.destroy = true; + this.destination = undefined; + } + findTargetFromList(objects) { - return shuffle(objects).find(obj => obj.type === VILLAGER && obj.subType === WARRIOR && !obj.destroy); + return shuffle(objects).find(obj => + obj.type === VILLAGER && + obj.subType !== HERO && + !obj.destroy && + getDistancePts(this, obj) <= this.giveUpDistance + ); } moveToDestination() { diff --git a/js/objects/home.js b/js/objects/home.js index f269598..8239ea7 100755 --- a/js/objects/home.js +++ b/js/objects/home.js @@ -2,17 +2,19 @@ import { randomX, randomY, drawCircle, writeText, getDistancePts, movePoint, per import Villager, { HERO } from './villager.js'; export const HOME = 'home'; -export const BASE_MIN_AGE = 12000; -export const BASE_MAX_AGE = 24000; +export const BASE_MIN_AGE = 24000; +export const BASE_MAX_AGE = 48000; export const BASE_MIN_POP = 5; export const BASE_MAX_POP = 10; export const BASE_HOME_SPEED = 100; -export const VILLAGER_BASE_ADVENTUROUSNESS = 0.25; -export const BASE_VILLAGER_MAX_SPEED = 1; -export const BASE_VILLAGER_AGILITY = 0.04; +export const VILLAGER_BASE_ADVENTUROUSNESS = 0.2; +export const BASE_VILLAGER_MAX_SPEED = 0.65; +export const BASE_VILLAGER_AGILITY = 0.0275; export const BASE_VILLAGER_AGGRESIVENESS = 0.1; -export const BASE_VILLAGER_HEROISM = 0.025; -export const BASE_VILLAGER_STRENGTH = 6; +export const BASE_VILLAGER_HEROISM = 0.01; +export const BASE_VILLAGER_STRENGTH = 7; +export const MUTATION_RATE = 0.2; +export const STARTING_VARIABILITY = 0.5; export default class Home { constructor({ @@ -25,16 +27,19 @@ export default class Home { blue = 50, radius = 15, food = 0, - adventurousness = VILLAGER_BASE_ADVENTUROUSNESS * percentAdjust(0.3), - maxPopulation = randomNumber({ min: BASE_MIN_POP, max: BASE_MAX_POP }) * percentAdjust(0.3), - maxAge = randomNumber({ min: BASE_MIN_AGE, max: BASE_MAX_AGE }) * percentAdjust(0.3), - homeSpeed = BASE_HOME_SPEED * percentAdjust(0.3), - villagerAgility = BASE_VILLAGER_AGILITY * percentAdjust(0.3), - villagerMaxSpeed = BASE_VILLAGER_MAX_SPEED * percentAdjust(0.3), - villagerAgressiveness = BASE_VILLAGER_AGGRESIVENESS * percentAdjust(0.3), - villagerStrength = BASE_VILLAGER_STRENGTH * percentAdjust(0.3), - villagerHeroism = BASE_VILLAGER_HEROISM * percentAdjust(0.3), + adventurousness = VILLAGER_BASE_ADVENTUROUSNESS * percentAdjust(STARTING_VARIABILITY), + maxPopulation = randomNumber({ min: BASE_MIN_POP, max: BASE_MAX_POP }) * percentAdjust(STARTING_VARIABILITY), + maxAge = randomNumber({ min: BASE_MIN_AGE, max: BASE_MAX_AGE }) * percentAdjust(STARTING_VARIABILITY), + homeSpeed = BASE_HOME_SPEED * percentAdjust(STARTING_VARIABILITY), + villagerAgility = BASE_VILLAGER_AGILITY * percentAdjust(STARTING_VARIABILITY), + villagerMaxSpeed = BASE_VILLAGER_MAX_SPEED * percentAdjust(STARTING_VARIABILITY), + villagerAgressiveness = BASE_VILLAGER_AGGRESIVENESS * percentAdjust(STARTING_VARIABILITY), + villagerStrength = BASE_VILLAGER_STRENGTH * percentAdjust(STARTING_VARIABILITY), + villagerHeroism = BASE_VILLAGER_HEROISM * percentAdjust(STARTING_VARIABILITY), } = {}) { + this.villagers = []; + this.heroTargets = []; + this.heroes = []; this.context = context; this.environment = environment; this.id = Math.random(); @@ -55,9 +60,6 @@ export default class Home { this.villagerAgressiveness = villagerAgressiveness; this.villagerStrength = villagerStrength; this.villagerHeroism = villagerHeroism; - this.villagers = []; - this.heroTargets = []; - this.heroes = []; this.environment.history.push(this.characteristics); window.localStorage.setItem('VILLAGE_HISTORY', JSON.stringify(this.environment.history)); } @@ -76,36 +78,19 @@ export default class Home { }; } - right() { - return this.x + this.radius; - } - - left() { - return this.x - this.radius; - } - - bottom() { - return this.y + this.radius; - } - - top() { - return this.y - this.radius; - } - get fullRadius() { return this.radius + this.villagers.length * 1.5; } get villagerCost() { return ( - (this.homeSpeed / BASE_HOME_SPEED) + - (this.villagerAgility / BASE_VILLAGER_AGILITY) + - (this.villagerMaxSpeed / BASE_VILLAGER_MAX_SPEED) + - (this.maxAge / BASE_MAX_AGE) + - (this.maxPopulation / BASE_MAX_POP) + - (this.villagerStrength / BASE_VILLAGER_STRENGTH) - ) - * 3.7; + ((1 * this.homeSpeed) / BASE_HOME_SPEED) + + ((2 * this.villagerAgility) / BASE_VILLAGER_AGILITY) + + ((9 * this.villagerMaxSpeed) / BASE_VILLAGER_MAX_SPEED) + + ((4 * this.maxAge) / BASE_MAX_AGE) + + ((2 * this.maxPopulation) / BASE_MAX_POP) + + ((3 * this.villagerStrength) / BASE_VILLAGER_STRENGTH) + ); } draw() { @@ -173,15 +158,15 @@ export default class Home { const newHome = this.environment.addHome(); newHome.x = this.x; newHome.y = this.y; - newHome.adventurousness = this.adventurousness * percentAdjust(0.1); - newHome.maxPopulation = this.maxPopulation * percentAdjust(0.1); - newHome.maxAge = this.maxAge * percentAdjust(0.1); - newHome.homeSpeed = this.homeSpeed * percentAdjust(0.1); - newHome.villagerAgility = this.villagerAgility * percentAdjust(0.1); - newHome.villagerMaxSpeed = this.villagerMaxSpeed * percentAdjust(0.1); - newHome.villagerStrength = this.villagerStrength * percentAdjust(0.1); - newHome.villagerAgressiveness = this.villagerAgressiveness * percentAdjust(0.1); - newHome.villagerHeroism = this.villagerHeroism * percentAdjust(0.1); + newHome.adventurousness = this.adventurousness * percentAdjust(MUTATION_RATE) + newHome.maxPopulation = this.maxPopulation * percentAdjust(MUTATION_RATE) + newHome.maxAge = this.maxAge * percentAdjust(MUTATION_RATE) + newHome.homeSpeed = this.homeSpeed * percentAdjust(MUTATION_RATE) + newHome.villagerAgility = this.villagerAgility * percentAdjust(MUTATION_RATE) + newHome.villagerMaxSpeed = this.villagerMaxSpeed * percentAdjust(MUTATION_RATE) + newHome.villagerStrength = this.villagerStrength * percentAdjust(MUTATION_RATE) + newHome.villagerAgressiveness = this.villagerAgressiveness * percentAdjust(MUTATION_RATE) + newHome.villagerHeroism = this.villagerHeroism * percentAdjust(MUTATION_RATE) newHome.parent = this; newHome.destination = { x: randomX(), diff --git a/js/objects/villager.js b/js/objects/villager.js index 98775df..a81d300 100755 --- a/js/objects/villager.js +++ b/js/objects/villager.js @@ -36,7 +36,7 @@ export default class Villager { this.death = Date.now() + this.home.maxAge; if (Math.random() < this.home.villagerAgressiveness) { this.subType = WARRIOR; - } else if (this.home.heroes.length === 0 && Math.random() < this.home.villagerHeroism) { + } else if (this.home.heroes.length === 0 && Math.random() < this.home.villagerHeroism && this.home.villagers.length > 0) { this.subType = HERO; this.death = Date.now() + (this.home.maxAge * 4); this.home.heroes.push(this); @@ -45,22 +45,6 @@ export default class Villager { } } - right() { - return this.x + this.radius; - } - - left() { - return this.x - this.radius; - } - - bottom() { - return this.y + this.radius; - } - - top() { - return this.y - this.radius; - } - draw() { if (this.alive) { drawCircle({ @@ -158,6 +142,24 @@ export default class Villager { fillGreen: 0, fillBlue: 0 }) + // TODO: Uncomment this to see the destination drawn on the screen + // if (this.destination) { + // drawArc({ + // context: this.context, + // x: this.destination.x, + // y: this.destination.y, + // radius: 5, + // lineWidth: 0.5, + // lineStyle: `rgb(0,0,0)` + // }); + // drawLine( + // this.context, + // this, + // this.destination, + // 2, + // 'rgb(0,0,0)' + // ) + // } } } } @@ -171,7 +173,7 @@ export default class Villager { } move() { - if (this.destination && getDistancePts(this, this.destination) > (this.radius + this.destination.radius)) { + if (this.destination && !this.reachedDestination() && !this.destination.destroy) { this.moveToDestination(); } else if (this.hunting && this.destination.id === this.hunting.id) { this.attackTarget(); @@ -187,6 +189,14 @@ export default class Villager { } } + reachedDestination() { + if (this.subType === HERO && this.destination.type === HOME) { + return getDistancePts(this, this.destination) < 2; + } else { + return getDistancePts(this, this.destination) < (this.radius + this.destination.radius); + } + } + attackTarget() { this.hunting.destroy = true; this.hunting = undefined; @@ -220,10 +230,17 @@ export default class Villager { findBadGuy() { this.home.heroTargets = this.home.heroTargets.filter(obj => !obj.destroy); if (this.home.heroTargets.length > 0) { - setTimeout(() => { - this.destination = shuffle(this.home.heroTargets)[0]; + const destination = shuffle(this.home.heroTargets)[0]; + const wait = 3000 * (1 - this.home.villagerAgressiveness); + if (destination.type === VILLAGER) { + setTimeout(() => { + this.destination = destination; + this.hunting = this.destination; + }, wait); + } else { + this.destination = destination; this.hunting = this.destination; - }, this.agility * 10000); + } } else { this.destination = this.home; } @@ -248,15 +265,15 @@ export default class Villager { } get agility() { - return this.home.villagerAgility * (this.subType === HERO ? 1.2 : 1); + return this.home.villagerAgility * (this.subType === HERO ? 1.5 : 1); } get maxSpeed() { - return this.home.villagerMaxSpeed * (this.subType === HERO ? 1.2 : 1); + return this.home.villagerMaxSpeed * (this.subType === HERO ? 1.5 : 1); } get strength() { - return this.home.villagerStrength * (this.subType === HERO ? 1.2 : 1); + return this.home.villagerStrength * (this.subType === HERO ? 1.5 : 1); } moveToDestination() {