5 次代碼提交 074ba998c7 ... e3d775fa81

作者 SHA1 備註 提交日期
  caryoscelus e3d775fa81 DJII: melee attacks WIP 10 月之前
  caryoscelus 3f91d1d938 Some types in random 10 月之前
  caryoscelus 18b7a300bb Inventory refactor WIP 10 月之前
  caryoscelus 70ae382538 DJII: GAME OVER 10 月之前
  caryoscelus 37cb2b5b49 Update Svelte 10 月之前
共有 10 個文件被更改,包括 103 次插入30 次删除
  1. 3 3
      package-lock.json
  2. 10 2
      src/lib/clock.ts
  3. 0 3
      src/lib/components.ts
  4. 8 2
      src/lib/content/heart.ts
  5. 34 0
      src/lib/death.ts
  6. 3 7
      src/lib/index.ts
  7. 26 10
      src/lib/inventory.ts
  8. 16 0
      src/lib/player.ts
  9. 3 3
      src/lib/random.ts
  10. 0 0
      src/lib/render.ts

+ 3 - 3
package-lock.json

@@ -4617,9 +4617,9 @@
       }
     },
     "node_modules/svelte": {
-      "version": "5.0.0-next.54",
-      "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.54.tgz",
-      "integrity": "sha512-ik+hXhlKdZozs+EruogLXb5PVZD8X8ekNBwQGBYcTpj1FD0sgWZxvUEQV4m0rE5LaUL3J5cmO4e8daOuPxIK/A==",
+      "version": "5.0.0-next.65",
+      "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.0.0-next.65.tgz",
+      "integrity": "sha512-meJJKuOStSWsJDEZLPi02/hB0ujblItxrVEj1W3y1274/BS0pgRIcMhHUDwnzNe0uJTvc7/qKRPZR1TFQ+hE4g==",
       "dev": true,
       "dependencies": {
         "@ampproject/remapping": "^2.2.1",

+ 10 - 2
src/lib/clock.ts

@@ -1,5 +1,8 @@
-import { Types, defineComponent, defineQuery, pipe } from '$lib/ecs';
-import { Global } from './components';
+import { Types, defineComponent, defineQuery, pipe, type IWorld } from '$lib/ecs';
+
+export const Global = defineComponent('Global', {
+  gameOver: Types.ui8,
+});
 
 export const Clock = defineComponent('Clock', {
   created: Types.ui32,
@@ -8,6 +11,7 @@ export const Clock = defineComponent('Clock', {
 
 export const clockQuery = defineQuery([Clock]);
 export const clockOnlyQuery = defineQuery([Clock, Global]);
+export const getClock = (world: IWorld) => clockOnlyQuery(world)[0];
 
 const onTick = [];
 
@@ -23,3 +27,7 @@ const onTickActive = new Map();
 
 export const getOnTickActive = (component) => onTickActive.get(component);
 export const registerOnTickActive = (component, action) => onTickActive.set(component, action);
+
+export const isGameOver = (world: IWorld) => {
+  return !!Global.gameOver[getClock(world)];
+};

+ 0 - 3
src/lib/components.ts

@@ -30,14 +30,11 @@ export const Cover = defineComponent('Cover', {
 export const Opaque = defineComponent('Opaque', {});
 export const Feature = defineComponent('Feature', {});
 export const Creature = defineComponent('Creature', {});
-export const Player = defineComponent('Player', {});
 export const AI = defineComponent('AI', {});
-export const Global = defineComponent('Global', {});
 export const Null = defineComponent('Null', {});
 export const Collision = defineComponent('Collision', {});
 export const Impassable = defineComponent('Impassable', {});
 export const Immobile = defineComponent('Immobile', {});
 
-export const playerQuery = defineQuery([Player]);
 export const nullQuery = defineQuery([Null]);
 export const collisionQuery = coordPlusQuery([Collision]);

+ 8 - 2
src/lib/content/heart.ts

@@ -1,5 +1,5 @@
 import { Clock, registerOnTick } from '$lib/clock';
-import { Types, defineComponent, defineQuery, hasComponent, type Component } from '$lib/ecs';
+import { Types, defineComponent, defineQuery, hasComponent, type Component, type Entity, type IWorld } from '$lib/ecs';
 
 /**
  * Heart is essentially creature's inner clock. All time-consuming action
@@ -35,11 +35,17 @@ export const processHeatBeat = (world) => {
 };
 
 export const withHeartBeats = (n: number, e, action) => {
+  // TODO
   return () => {
-    const delay = Heart.heartbeat[e];
+    const delay = n * Heart.heartbeat[e];
     Clock.time[e] += delay;
     action();
   };
 };
 
+export const applyHeartDelay = (world: IWorld, e: Entity, n: number) => {
+  const delay = n * Heart.heartbeat[e];
+  Clock.time[e] += delay;
+};
+
 registerOnTick(processHeatBeat);

+ 34 - 0
src/lib/death.ts

@@ -0,0 +1,34 @@
+import { describe } from '$lib';
+import { cleanup } from '$lib/cleanup';
+import { removeEntity, type Entity, type IWorld, type Component, hasComponent } from '$lib/ecs';
+import { logVisible } from '$lib/render3';
+import { Global, getClock } from './clock';
+import { Player } from './player';
+
+type Drop = {
+  target: Component,
+  drop: (world: IWorld, e: Entity) => void,
+};
+
+const corpseDrop = new Map<Component, Drop>();
+
+export const registerCorpseDrop = (drop: Drop) => {
+  corpseDrop.set(drop.target, drop);
+};
+
+export const creatureDies = (world: IWorld, e: Entity) => {
+  const isPlayer = hasComponent(world, Player, e);
+  logVisible(e, `${describe(world, e)} dies`);
+  for (let [component, drop] of corpseDrop) {
+    if (hasComponent(world, component, e)) {
+      drop.drop(world, e);
+    }
+  }
+  if (isPlayer) {
+    const clock = getClock(world);
+    Global.gameOver[clock] = 1;
+  } else {
+    cleanup(world, e);
+    removeEntity(world, e);
+  }
+};

+ 3 - 7
src/lib/index.ts

@@ -1,9 +1,9 @@
-import { addEntity, addComponent, hasComponent, type IWorld } from '$lib/ecs';
+import { addEntity, addComponent, hasComponent, type IWorld, type Entity } from '$lib/ecs';
 import { decode, encode } from './util';
-import { Global, Null, Feature, Creature, Name, playerQuery, Description, type Entity } from './components';
+import { Null, Feature, Creature, Name, Description } from './components';
 import { Item } from './item';
 import { Ground } from './content/ground';
-import { Clock, clockOnlyQuery } from './clock';
+import { Clock, clockOnlyQuery, Global } from './clock';
 
 export const nullEntity = 0;
 
@@ -40,10 +40,6 @@ export const getTime = (world) => {
   return Clock.time[clockOnlyQuery(world)[0]];
 };
 
-export const getPlayer = (world) => {
-  return playerQuery(world)[0];
-};
-
 export const createNullEntity = (world) => {
   const e = addEntity(world);
   if (e !== nullEntity)

+ 26 - 10
src/lib/inventory.ts

@@ -1,10 +1,11 @@
-import { Types, defineComponent, addComponent, removeComponent, removeEntity } from './ecs';
-import { playerQuery } from './components';
+import { Types, defineComponent, addComponent, removeComponent, removeEntity, type IWorld, type Entity } from './ecs';
 import { log } from './log';
 import { describe } from '$lib';
-import { getPos2, placeOnMap, removeFromMap } from './pos2';
 import { Clock } from './clock';
 import { registerAction } from './actions';
+import { registerCorpseDrop } from './death';
+import { getPos3, hasPos3, placeOnMap, removeFromMap } from './pos3';
+import { logVisible } from './render3';
 
 const MAX_INVENTORY = 64;
 
@@ -18,7 +19,7 @@ export const Stored = defineComponent('Stored', {
   parent: Types.eid,
 });
 
-export const getInventory = (world, char) => {
+export const getInventory = (world: IWorld, char: Entity) => {
   const sz = Inventory.count[char];
   let res = [];
   for (let i = 0; i < sz; ++i) {
@@ -29,8 +30,8 @@ export const getInventory = (world, char) => {
 
 export const PickupAction = (item) => (world, e) => {
   let error = false;
-  const epos = getPos2(e);
-  const ipos = getPos2(item);
+  const epos = getPos3(e);
+  const ipos = getPos3(item);
   if (epos.x !== ipos.x || epos.y !== ipos.y) {
     error = "Cannot pick up remotely";
   }
@@ -48,7 +49,7 @@ export const PickupAction = (item) => (world, e) => {
       removeFromMap(world, item);
       addComponent(world, Stored, item);
       Stored.parent[item] = e;
-      log(`${describe(world, e)} picks up ${describe(world, item)}`);
+      logVisible(e, `${describe(world, e)} picks up ${describe(world, item)}`);
     },
   };
 };
@@ -64,9 +65,9 @@ export const DropAction = (item) => (world, e) => {
         log('DROPPING ITEM NOT IN INVENTORY!');
         return;
       }
-      const { x, y } = getPos2(e);
-      placeOnMap(world, item, x, y);
-      log(`${describe(world, e)} drops ${describe(world, item)}`);
+      const { x, y, z } = getPos3(e);
+      placeOnMap(world, item, x, y, z);
+      logVisible(e, `${describe(world, e)} drops ${describe(world, item)}`);
     },
   };
 };
@@ -104,3 +105,18 @@ export const purgeInventory = (world, e) => {
   }
   Inventory.count[e] = 0;
 };
+
+registerCorpseDrop({
+  target: Inventory,
+  drop: (world, e) => {
+    // TODO: configurable pos component
+    if (hasPos3(world, e)) {
+      const {x, y, z} = getPos3(e);
+      for (let item of getInventory(world, e)) {
+        placeOnMap(world, item, x, y, z);
+      }
+    } else {
+      console.warn("Dead creature had inventory, but no Pos3, inventory items lost");
+    }
+  },
+});

+ 16 - 0
src/lib/player.ts

@@ -0,0 +1,16 @@
+import { defineComponent, defineQuery, hasComponent, type Entity, type IWorld } from './ecs';
+import { log } from './log';
+
+export const Player = defineComponent('Player', {});
+
+export const playerQuery = defineQuery([Player]);
+
+export const getPlayer = (world) => {
+  return playerQuery(world)[0];
+};
+
+export const logPlayer = (world: IWorld, e: Entity, msg: string, style?: string) => {
+  if (hasComponent(world, Player, e)) {
+    log(msg, style);
+  }
+};

+ 3 - 3
src/lib/random.ts

@@ -2,7 +2,7 @@ import * as seedrandom from 'seedrandom';
 
 export let random;
 
-export const randomSeed = (seed) => {
+export const randomSeed = (seed: number) => {
   random = seedrandom(seed);
 };
 
@@ -22,11 +22,11 @@ export const distribute = (value: number, probabilities: [any, number][]) => {
   return probabilities[probabilities.length-1][0];
 };
 
-export const randomChoice = (values) => {
+export const randomChoice = (values: any[]) => {
   return values[Math.floor(values.length*random())];
 };
 
-export const shuffle = (values) => {
+export const shuffle = (values: any[]) => {
   const rest = [...values];
   let res = [];
   while (rest.length) {

+ 0 - 0
src/lib/render.ts


Some files were not shown because too many files changed in this diff