Force the player to clear an oasis before they can pillage it.
This commit is contained in:
parent
0caa40bdca
commit
8bed89b6dc
@ -26,6 +26,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function clear() {
|
||||||
|
if (!region) return;
|
||||||
|
moves.clear(region.id, numberOfUnits);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function toggleMissionRepeat() {
|
function toggleMissionRepeat() {
|
||||||
if (!region) return;
|
if (!region) return;
|
||||||
moves.toggleMissionRepeat(region.id);
|
moves.toggleMissionRepeat(region.id);
|
||||||
@ -49,14 +55,34 @@
|
|||||||
<button on:click={ close }>X</button>
|
<button on:click={ close }>X</button>
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
|
{ #if region.state.cleared }
|
||||||
|
<p>Region cleared</p>
|
||||||
|
{ :else }
|
||||||
|
<p>Defensive strength: { region.strength }</p>
|
||||||
|
{ /if }
|
||||||
{ #if region.state.mission }
|
{ #if region.state.mission }
|
||||||
<div>
|
<div>
|
||||||
<p>{ region.state.mission.unitCount } soldiers are on a mission here.</p>
|
<p>{ region.state.mission.unitCount } soldiers are on a mission here.</p>
|
||||||
<p>Remaining: { Math.ceil(region.state.mission.remainingTime / 1000) }</p>
|
<p>Remaining: { Math.ceil(region.state.mission.remainingTime / 1000) }</p>
|
||||||
|
{ #if region.state.mission.type === 'pillage' }
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" checked={ region.state.mission.repeat } on:change={ toggleMissionRepeat }>
|
<input type="checkbox" checked={ region.state.mission.repeat } on:change={ toggleMissionRepeat }>
|
||||||
<span title="Send the same troops again when they come back">Repeat</span>
|
<span title="Send the same troops again when they come back">Repeat</span>
|
||||||
</label>
|
</label>
|
||||||
|
{ /if }
|
||||||
|
</div>
|
||||||
|
{ :else if !region.state.cleared }
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
name="units"
|
||||||
|
min="0"
|
||||||
|
max={ maximumUnits }
|
||||||
|
bind:value={ numberOfUnits }
|
||||||
|
/>
|
||||||
|
{ numberOfUnits }
|
||||||
|
<button on:click={ setMaxUnits }>↑</button>
|
||||||
|
<button on:click={ clear } disabled={ numberOfUnits === 0 }>Clear</button>
|
||||||
</div>
|
</div>
|
||||||
{ :else }
|
{ :else }
|
||||||
<div>
|
<div>
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
class:empty={ region.type === WORLDMAP_TYPES.EMPTY }
|
class:empty={ region.type === WORLDMAP_TYPES.EMPTY }
|
||||||
class:bourgade={ region.type === WORLDMAP_TYPES.BOURGADE }
|
class:bourgade={ region.type === WORLDMAP_TYPES.BOURGADE }
|
||||||
class:oasis={ region.type === WORLDMAP_TYPES.OASIS }
|
class:oasis={ region.type === WORLDMAP_TYPES.OASIS }
|
||||||
|
class:cleared={ region.type === WORLDMAP_TYPES.OASIS && region.state.cleared }
|
||||||
>
|
>
|
||||||
{ #if region.type === WORLDMAP_TYPES.BOURGADE }
|
{ #if region.type === WORLDMAP_TYPES.BOURGADE }
|
||||||
<button class="invisible" on:click={ () => gameTab.set('resources') }>
|
<button class="invisible" on:click={ () => gameTab.set('resources') }>
|
||||||
@ -63,6 +64,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.region.oasis {
|
.region.oasis {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.region.oasis.cleared {
|
||||||
background-color: cyan;
|
background-color: cyan;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -17,6 +17,11 @@ export function resolveMission(V: VillageState, region: RegionType) {
|
|||||||
resolvePillageOasis(V, region);
|
resolvePillageOasis(V, region);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'clear':
|
||||||
|
if (region.type === WORLDMAP_TYPES.OASIS) {
|
||||||
|
resolveClearOasis(V, region);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown mission type: "${ mission.type }"`);
|
throw new Error(`Unknown mission type: "${ mission.type }"`);
|
||||||
}
|
}
|
||||||
@ -30,6 +35,36 @@ export function resolveMission(V: VillageState, region: RegionType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function resolveClearOasis(V: VillageState, region: OasisType) {
|
||||||
|
const mission = region.state.mission;
|
||||||
|
if (!mission) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ratio = mission.unitCount / region.strength;
|
||||||
|
const lostRatio = 1 / (ratio * ratio);
|
||||||
|
const lostUnits = Math.min(
|
||||||
|
Math.round(mission.unitCount * lostRatio),
|
||||||
|
mission.unitCount
|
||||||
|
);
|
||||||
|
|
||||||
|
mission.unitCount -= lostUnits;
|
||||||
|
V.units.soldier -= lostUnits;
|
||||||
|
|
||||||
|
if (ratio >= 1) {
|
||||||
|
region.state.cleared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unit = getUnitSource('soldier');
|
||||||
|
const maxResources = region.distance * region.distance * 100;
|
||||||
|
|
||||||
|
V.resources[region.resource] += Math.min(
|
||||||
|
mission.unitCount * unit.behavior.caryingCapacity,
|
||||||
|
maxResources
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function resolvePillageOasis(V: VillageState, region: OasisType) {
|
function resolvePillageOasis(V: VillageState, region: OasisType) {
|
||||||
const mission = region.state.mission;
|
const mission = region.state.mission;
|
||||||
if (!mission) {
|
if (!mission) {
|
||||||
|
28
src/moves/clear.ts
Normal file
28
src/moves/clear.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { WORLDMAP_TYPES } from "../types";
|
||||||
|
import { assert, getRemainingUnitCount } from "../utils";
|
||||||
|
import type { VillageState } from "../village";
|
||||||
|
|
||||||
|
|
||||||
|
export default function clear(
|
||||||
|
V: VillageState, regionIndex: number, soldiersCount: number
|
||||||
|
) {
|
||||||
|
if (soldiersCount > getRemainingUnitCount(V, 'soldier')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const region = V.worldmap[regionIndex];
|
||||||
|
assert(region.type === WORLDMAP_TYPES.OASIS);
|
||||||
|
|
||||||
|
if (region.state.mission) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
region.state.mission = {
|
||||||
|
type: 'clear',
|
||||||
|
unitType: 'soldier',
|
||||||
|
unitCount: soldiersCount,
|
||||||
|
remainingTime: region.distance * 10 * 1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { produce } from 'immer';
|
|||||||
|
|
||||||
import village, { type VillageState } from '../village';
|
import village, { type VillageState } from '../village';
|
||||||
import build from './build';
|
import build from './build';
|
||||||
|
import clear from './clear';
|
||||||
import pillage from './pillage';
|
import pillage from './pillage';
|
||||||
import recruitUnits from './recruitUnits';
|
import recruitUnits from './recruitUnits';
|
||||||
import startQuest from './startQuest';
|
import startQuest from './startQuest';
|
||||||
@ -33,6 +34,7 @@ export function makeMove(move: (...args: any[]) => boolean) {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
build: makeMove(build),
|
build: makeMove(build),
|
||||||
|
clear: makeMove(clear),
|
||||||
upgradeBuilding: makeMove(upgradeBuilding),
|
upgradeBuilding: makeMove(upgradeBuilding),
|
||||||
pillage: makeMove(pillage),
|
pillage: makeMove(pillage),
|
||||||
recruitUnits: makeMove(recruitUnits),
|
recruitUnits: makeMove(recruitUnits),
|
||||||
|
@ -3,7 +3,9 @@ import { assert, getRemainingUnitCount } from "../utils";
|
|||||||
import type { VillageState } from "../village";
|
import type { VillageState } from "../village";
|
||||||
|
|
||||||
|
|
||||||
export default function pillage(V: VillageState, regionIndex: number, soldiersCount: number, repeat: boolean) {
|
export default function pillage(
|
||||||
|
V: VillageState, regionIndex: number, soldiersCount: number, repeat: boolean
|
||||||
|
) {
|
||||||
if (soldiersCount > getRemainingUnitCount(V, 'soldier')) {
|
if (soldiersCount > getRemainingUnitCount(V, 'soldier')) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -20,7 +22,7 @@ export default function pillage(V: VillageState, regionIndex: number, soldiersCo
|
|||||||
type: 'pillage',
|
type: 'pillage',
|
||||||
unitType: 'soldier',
|
unitType: 'soldier',
|
||||||
unitCount: soldiersCount,
|
unitCount: soldiersCount,
|
||||||
remainingTime: 1 * 10 * 1000,
|
remainingTime: region.distance * 10 * 1000,
|
||||||
repeat,
|
repeat,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -80,11 +80,11 @@ export interface UnitType {
|
|||||||
|
|
||||||
|
|
||||||
export interface MissionType {
|
export interface MissionType {
|
||||||
type: string;
|
type: 'pillage' | 'clear';
|
||||||
unitType: string;
|
unitType: string;
|
||||||
unitCount: number;
|
unitCount: number;
|
||||||
remainingTime: number;
|
remainingTime: number;
|
||||||
repeat: boolean;
|
repeat?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -110,7 +110,9 @@ export interface EmptyRegionType extends BaseRegionType {
|
|||||||
export interface OasisType extends BaseRegionType {
|
export interface OasisType extends BaseRegionType {
|
||||||
type: WORLDMAP_TYPES.OASIS;
|
type: WORLDMAP_TYPES.OASIS;
|
||||||
resource: keyof CostType;
|
resource: keyof CostType;
|
||||||
|
strength: number;
|
||||||
state: {
|
state: {
|
||||||
|
cleared: boolean;
|
||||||
mission?: MissionType;
|
mission?: MissionType;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -213,6 +213,6 @@ export function distanceBetweenCells(a: Point, b: Point) {
|
|||||||
export function indexToPoint(index: number): Point {
|
export function indexToPoint(index: number): Point {
|
||||||
return {
|
return {
|
||||||
x: index % WORLD_MAP_WIDTH,
|
x: index % WORLD_MAP_WIDTH,
|
||||||
y: index / WORLD_MAP_WIDTH,
|
y: Math.floor(index / WORLD_MAP_WIDTH),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -82,10 +82,9 @@ function getInitialWorldmap(): RegionType[] {
|
|||||||
}
|
}
|
||||||
board[centerIndex].type = WORLDMAP_TYPES.BOURGADE;
|
board[centerIndex].type = WORLDMAP_TYPES.BOURGADE;
|
||||||
|
|
||||||
const adj = shuffle(getAdjacentWorldmapCells(centerIndex));
|
|
||||||
worldmap.forEach(region => {
|
worldmap.forEach(region => {
|
||||||
const candidates = board.filter(c =>
|
const candidates = board.filter(c =>
|
||||||
c.type !== WORLDMAP_TYPES.EMPTY
|
c.type === WORLDMAP_TYPES.EMPTY
|
||||||
&& c.distance >= region.distance[0]
|
&& c.distance >= region.distance[0]
|
||||||
&& c.distance < region.distance[1]
|
&& c.distance < region.distance[1]
|
||||||
);
|
);
|
||||||
@ -95,7 +94,10 @@ function getInitialWorldmap(): RegionType[] {
|
|||||||
id: cell.id,
|
id: cell.id,
|
||||||
resource: region.resource as keyof CostType,
|
resource: region.resource as keyof CostType,
|
||||||
distance: cell.distance,
|
distance: cell.distance,
|
||||||
state: {},
|
strength: Math.round(cell.distance * cell.distance * 50),
|
||||||
|
state: {
|
||||||
|
cleared: false,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user