Skip to content

Commit

Permalink
Merge pull request #98 from bananu7/limit-harvesting-to-one
Browse files Browse the repository at this point in the history
Limit harvesting to one
  • Loading branch information
bananu7 authored Jun 25, 2024
2 parents e65b451 + 401741e commit 2c6e4f6
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 22 deletions.
80 changes: 60 additions & 20 deletions packages/server/src/game/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import {

import { isBuildPlacementOk } from '../shared.js'

import { getHpComponent, getMoveComponent, getAttackerComponent, getHarvesterComponent, getProducerComponent, getBuilderComponent, getVisionComponent, getBuildingComponent } from './components.js'
import { getHpComponent, getMoveComponent, getAttackerComponent, getHarvesterComponent, getProducerComponent, getBuilderComponent, getVisionComponent, getBuildingComponent, getResourceComponent } from './components.js'
import * as V from '../vector.js'

import { moveTowardsUnit, moveToPointOrCancelCommand, moveTowardsUnitById, moveTowardsMapPosition } from './unit/movement.js'
import { clearCurrentCommand, stopMoving, becomeIdleAtCurrentPosition } from './unit/clear.js'
import { detectNearbyEnemy, findClosestUnitBy, cancelProduction, aggro } from './unit/unit.js'
import { HARVESTING_DISTANCE, HARVESTING_RESOURCE_COUNT, MAX_PLAYER_UNITS, UNIT_FOLLOW_DISTANCE } from './constants.js'
import { createUnit, UnitData, getUnitDataByName } from './units.js'
import { findClosestEmptyTile } from './util.js'
import { findClosestEmptyTile, unitInteractionDistance } from './util.js'
import { findPositionForProducedUnit } from './produce.js'
import { buildPresenceAndBuildingMaps } from './presence.js'

Expand Down Expand Up @@ -113,33 +113,60 @@ export const harvestCommand = (ctx: CommandContext, cmd: CommandHarvest) => {
throw new ComponentMissingError("Harvester");
}

const target = g.units.find(u => u.id === cmd.target);
if (!target) {
// TODO find other nearby resource
clearCurrentCommand(unit);
return;
}

// If the unit doesn't have resources, we go into "obtain resources"
// branch, otherwise it's dropoff. We only check the target if we need to.
if (!hc.resourcesCarried) {
// TODO - should resources use perimeter?
switch(moveTowardsUnit(unit, target, HARVESTING_DISTANCE, ctx.gm, dt)) {
case 'Unreachable':
const target = g.units.find(u => u.id === cmd.target);
if (!target) {
// TODO find other nearby resource
clearCurrentCommand(unit);
break;
case 'ReachedTarget':
return;
}

const resource = getResourceComponent(target);
if (!resource) {
console.warn("Unit received a harvest command to something that's not a resource.");
clearCurrentCommand(unit);
return;
}

if (unitInteractionDistance(unit, target) > HARVESTING_DISTANCE) {
// in case of displacement mid-harvesting, reset and retry
if (unit.state.action === 'Harvesting') {
hc.harvestingProgress = 0;
unit.state.action = 'Idle';
return;
} else {
switch(moveTowardsUnit(unit, target, HARVESTING_DISTANCE, ctx.gm, dt)) {
case 'Unreachable':
clearCurrentCommand(unit);
break;
case 'ReachedTarget':
unit.state.action = 'Idle';
break;
}
}
} else {
if (!resourceAvailableFor(ctx.unit.id, target.id, ctx)) {
// just wait
unit.state.action = 'Idle';
return;
}

unit.state.action = 'Harvesting'
hc.harvestingProgress += dt;

if (hc.harvestingProgress >= hc.harvestingTime) {
hc.resourcesCarried = HARVESTING_RESOURCE_COUNT;
hc.resourcesCarried = hc.harvestingValue;
resource.value -= hc.resourcesCarried;

// TODO - reset harvesting at any other action
// maybe i could use some "exit state function"?
hc.harvestingProgress = 0;
} else {
unit.state.action = 'Harvesting';
hc.harvestingProgress += dt;
unit.state.action = 'Idle';
}
break;
}
} else {
// TODO include building&unit size in this distance
const DROPOFF_DISTANCE = 1;
// TODO cache the dropoff base
// TODO - resource dropoff component
Expand All @@ -166,6 +193,19 @@ export const harvestCommand = (ctx: CommandContext, cmd: CommandHarvest) => {
}
}

function resourceAvailableFor(unitId: UnitId, targetId: UnitId, ctx: CommandContext) {
// Check if any other harvester is competing for the same resource
const competingHarvesters = ctx.gm.game.units.filter(u =>
u.id !== unitId
&& u.state.state === "active"
&& u.state.action === "Harvesting"
&& u.state.current.typ === "Harvest"
&& u.state.current.target === targetId
);

return competingHarvesters.length === 0;
}

export const produceCommand = (ctx: CommandContext, cmd: CommandProduce) => {
const unit = ctx.unit;
const owner = ctx.owner;
Expand Down
6 changes: 5 additions & 1 deletion packages/server/src/game/components.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
Unit,
Hp, Mover, Attacker, Harvester, ProductionFacility, Builder, Vision, Building, Component
Hp, Mover, Attacker, Harvester, ProductionFacility, Builder, Vision, Building, Resource, Component
} from '../types'

export const getHpComponent = (unit: Unit) => {
Expand Down Expand Up @@ -34,3 +34,7 @@ export const getVisionComponent = (unit: Unit) => {
export const getBuildingComponent = (unit: Unit) => {
return unit.components.find(c => c.type === 'Building') as Building;
};

export const getResourceComponent = (unit: Unit) => {
return unit.components.find(c => c.type === 'Resource') as Resource;
}
2 changes: 1 addition & 1 deletion packages/server/src/game/units.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const UNIT_CATALOG : Catalog = {
{ type: 'Hp', maxHp: 50, hp: 50 },
{ type: 'Mover', speed: 10 },
{ type: 'Attacker', damage: 5, attackRate: 1000, range: 2, cooldown: 0 },
{ type: 'Harvester', harvestingTime: 1000, harvestingValue: 20, harvestingProgress: 0 },
{ type: 'Harvester', harvestingTime: 2000, harvestingValue: 8, harvestingProgress: 0 },
{ type: 'Builder', buildingsProduced: [
{ buildingType: 'Base', buildTime: 5000, buildCost: 400 },
{ buildingType: 'Barracks', buildTime: 5000, buildCost: 150},
Expand Down
32 changes: 32 additions & 0 deletions packages/server/test/behaviour.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,38 @@ describe('harvest action', () => {
expect(game.units[1].state.action).toBe('Moving');
expect(game.players[0].resources).toBe(8);
});

test('compete for one resource', () => {
const game = createBasicGame({}, 40);

spawnUnit(game, 0, "ResourceNode", {x: 15, y: 15});
// the first one is closer
spawnUnit(game, 1, "Harvester", {x: 10, y: 14 }); // id 2
spawnUnit(game, 1, "Harvester", {x: 25, y: 16 }); // id 3

command({
command: { typ: 'Harvest', target: 1 },
unitIds: [2, 3],
shift: false,
},
game,
1
);

for (let i = 0; i < 2 * 10; i++) {
tick(TICK_MS, game);
}

expect(game.units[1].state.action).toBe('Harvesting');
expect(game.units[2].state.action).toBe('Idle');

for (let i = 0; i < 4 * 10; i++) {
tick(TICK_MS, game);
}

expect(game.units[2].state.action).toBe('Harvesting');
expect(game.units[1].state.action).toBe('Idle');
});
});

describe('build action', () => {
Expand Down

0 comments on commit 2c6e4f6

Please sign in to comment.