22 Commits a1564f94ca ... b32b4c6056

Author SHA1 Message Date
  caryoscelus b32b4c6056 DJII: fix attack atamina 3 months ago
  caryoscelus 5fcf0b2b14 debugEntity utility 3 months ago
  caryoscelus 18b3fb5482 DJII: Some clothes descriptions 3 months ago
  caryoscelus 76fa15e083 DJII: make selecting melee style work 3 months ago
  caryoscelus a3dbf2a865 DJII: Fix spawning on player entry point 3 months ago
  caryoscelus 250d41b0c8 DJII: spawn a thug for testing 3 months ago
  caryoscelus 5ff67d7f24 DJII: some attack fixes (WIP) 3 months ago
  caryoscelus d246016a63 Fix typo in Select 3 months ago
  caryoscelus a287b15e81 DJII: use skills from profession in char creation, attack WIP 3 months ago
  caryoscelus eb986aba51 Select: hide list bullets 3 months ago
  caryoscelus 1623da3269 DJII: basic UI borders (WIP) 3 months ago
  caryoscelus 736af932a5 DJII: Attack WIP, display blood level 3 months ago
  caryoscelus 6c1b3e8359 DJII: Stamina regen 3 months ago
  caryoscelus 81c6eeace9 Fix previous commit 3 months ago
  caryoscelus eec03f3054 DJII: remove character used for preview in char creation 3 months ago
  caryoscelus e85cb9ae80 DJII: introduce Stamina 3 months ago
  caryoscelus 293a569f43 Improve Select: get rid of duplicate index/value 3 months ago
  caryoscelus 12fe56f13e DJII: fix equipment display 3 months ago
  caryoscelus 9ddc9720b5 DJII: char creation equipment preview 3 months ago
  caryoscelus 49be17c31a DJII: content 3 months ago
  caryoscelus 01c35f4a2e DJII: char creation inventory preview 3 months ago
  caryoscelus 300bdae48c Minor undefined sanitation 3 months ago

+ 4 - 0
src/lib/debug.ts

@@ -0,0 +1,4 @@
+import type { Entity, IWorld } from './ecs';
+import { entityToObject } from './serialize';
+
+export const debugEntity = (world: IWorld, e: Entity) => console.log(entityToObject(world, e));

+ 9 - 1
src/lib/inventory.ts

@@ -1,4 +1,4 @@
-import { Types, defineComponent, addComponent, removeComponent } from './ecs';
+import { Types, defineComponent, addComponent, removeComponent, removeEntity } from './ecs';
 import { playerQuery } from './components';
 import { log } from './log';
 import { describe } from '$lib';
@@ -96,3 +96,11 @@ export const removeFromInventory = (world, e, item) => {
   removeComponent(world, Stored, item);
   return true;
 };
+
+/// Remove all items from inventory and delete them
+export const purgeInventory = (world, e) => {
+  for (let item of getInventory(world, e)) {
+    removeEntity(world, item);
+  }
+  Inventory.count[e] = 0;
+};

+ 1 - 1
src/routes/EntityLine.svelte

@@ -6,12 +6,12 @@
   import { getPos3, hasPos3 } from '$lib/pos3';
   import { decode } from '$lib/util';
 
+  const debug = false; // TODO: use global debug
   let { item, world, onclick, selected, zoomlevel } = $props();
   let selectedClass = $derived(item === selected ? 'selected' : '');
   let emptyClass = $derived((!debug && !hasComponent(world, Glyph, item)) ? 'empty' : '');
   let pc = $derived(getPlayer(world));
   let style = $derived(zoomlevel ? `font-size: ${zoomlevel*0.6}px` : '');
-  const debug = false;
 
   const describeDirection = (e) => {
     let xy, pov;

+ 8 - 11
src/routes/Select.svelte

@@ -1,36 +1,32 @@
 <script>
-  let { options, index, value } = $props();
+  let { options, index } = $props();
 
   const onkeydown = (ev) => {
     // TODO: support custom controls
-    const ix = options.findIndex(({name}) => name === value.name);
     if (ev.key === 'ArrowUp') {
-      const newIx = Math.max(0, ix-1);
-      value = options[newIx];
+      const newIx = Math.max(0, index-1);
       index = newIx;
     } else if (ev.key === 'ArrowDown') {
-      const newIx = Math.min(options.length-1, ix+1);
-      value = options[newIx];
+      const newIx = Math.min(options.length-1, index+1);
       index = newIx;
     }
   };
 
-  const selectedClass = (option) => {
-    if (option.name === value.name) {
+  const selectedClass = (ix) => {
+    if (ix === index) {
       return 'selectable-selected';
     }
     return '';
   };
 
-  const select = (option, ix) => {
-    value = option;
+  const select = (ix) => {
     index = ix;
   };
 </script>
 
 <ul tabindex="0" {onkeydown}>
   {#each options as option, ix}
-    <li class="selectable {selectedClass(option)}" onclick={() => select(option, ix)}>
+    <li class="selectable {selectedClass(ix)}" onclick={() => select(ix)}>
       {option.name}
     </li>
   {/each}
@@ -38,6 +34,7 @@
 
 <style>
   .selectable {
+    list-style-type: none;
   }
 
   .selectable:hover {

+ 63 - 15
src/routes/djii/CharCreation.svelte

@@ -1,19 +1,26 @@
 <script>
   import { Name } from '$lib/components';
-  import { addComponent } from '$lib/ecs';
+  import { addComponent, removeEntity } from '$lib/ecs';
   import { onMount } from 'svelte';
   import Select from '../Select.svelte';
   import { newGame } from './game';
   import { applyProfession, enumProfessions } from './profession';
-  import { enumSkills } from './skill';
+  import { enumSkills, purgeSkills } from './skill';
   import Stats from './Stats.svelte';
   import Skills from './Skills.svelte';
+  import { getInventory, purgeInventory } from '$lib/inventory';
+  import Equipment from './Equipment.svelte';
+  import Inventory from './Inventory.svelte';
+  import { fromTemplate } from '$lib/builder';
+  import { getEquipped, purgeEquipment } from './equipment';
+  import { playerTemplate } from './player';
+  import { notNullish } from '$lib/util';
 
   let { game } = $props();
 
   const game0 = newGame();
 
-  let charNameWidget
+  let charNameWidget;
   let tick = $state(false);
   
   let error = $state('');
@@ -26,22 +33,44 @@
   let professionIx = $state(0);
   let selectedProfession = $state(professions[0]);
 
+  let tmpChar = $state();
+
   // skip char-creation (for debug purposes)
   const autostart = false;
 
   onMount(() => {
     charNameWidget.focus();
+    tmpChar = fromTemplate(game0.world, playerTemplate);
     if (autostart) {
       start();
     }
   });
 
+  let inventory = $state([]);
+  let equipment = $state([]);
+
+  $effect(() => {
+    if (notNullish(tmpChar)) {
+      purgeInventory(game0.world, tmpChar);
+      purgeEquipment(game0.world, tmpChar);
+      purgeSkills(game0.world, tmpChar);
+      applyProfession(game0.world, tmpChar, professions[professionIx]);
+      inventory = getInventory(game0.world, tmpChar);
+      equipment = getEquipped(game0.world, tmpChar);
+      selectedProfession = professions[professionIx];
+    }
+  });
+
   $effect(() => {
     tick;
     error = '';
   });
 
   const start = () => {
+    purgeEquipment(game0.world, tmpChar);
+    purgeInventory(game0.world, tmpChar);
+    removeEntity(game0.world, tmpChar);
+    tmpChar = undefined;
     addComponent(game0.world, Name, game0.pc, {
       name: playerName,
     });
@@ -62,21 +91,40 @@
   };
 </script>
 
-<section>
-  <h2>New character</h2>
-  <input bind:this={charNameWidget} bind:value={playerName} /> the {selectedProfession.name}
-  <div>
-    <h3>Profession</h3>
-    <Select options={professions} bind:value={selectedProfession} bind:index={professionIx} />
+<section class="cc-grid">
+  <div class="cc-leftside">
+    <h2>New character</h2>
+    <input bind:this={charNameWidget} bind:value={playerName} /> the {selectedProfession.name}
+    <div>
+      <h3>Profession</h3>
+      <Select options={professions} bind:index={professionIx} />
+    </div>
+    <div>
+      <h3>Hobby</h3>
+    </div>
+    <Stats game={game0} bind:tick />
+    <Skills world={game0.world} char={tmpChar} tick={selectedProfession} />
+    <div>{error}</div>
+    <button onclick={finishCharacter}>Ok</button>
   </div>
-  <div>
-    <h3>Hobby</h3>
+  <div class="cc-rightside">
+    Starting equipment
+    <Inventory world={game0.world} {inventory} />
+    <Equipment world={game0.world} {equipment} />
   </div>
-  <Stats game={game0} bind:tick />
-  <Skills game={game0} bind:tick />
-  <div>{error}</div>
-  <button onclick={finishCharacter}>Ok</button>
 </section>
 
 <style>
+  .cc-grid {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    grid-template-areas:
+      "leftside rightside";
+  }
+  .cc-leftside {
+    grid-area: leftside;
+  }
+  .cc-rightside {
+    grid-area: rightside;
+  }
 </style>

+ 33 - 5
src/routes/djii/CharStatus.svelte

@@ -1,10 +1,14 @@
 <script>
   import { decode } from '$lib/util';
+  import Select from '../Select.svelte';
   import BodyPartIcon from './BodyPartIcon.svelte';
+  import { enumAvaliableMeleeStyles, getStamina, setSelectedMeleeStyle } from './attack';
+  import { renderBlocks } from './blocks';
+  import { getBloodLevel } from './blood';
   import { Cash } from './cash';
   import { Profession } from './profession';
 
-  let { world, char } = $props();
+  let { world, char, tick } = $props();
 
   const health = {
     head: 0.75,
@@ -31,6 +35,14 @@
   let dynamicStyle = $derived(Object.entries(styles).map(([k, v]) => `--${k}: ${v}`).join(';'));
   let profession = $derived(decode(Profession.profession[char]));
 
+  let staminaLine = $derived((tick, renderBlocks(getStamina(world, char))));
+  let bloodLine = $derived((tick, renderBlocks(getBloodLevel(world, char))));
+  let meleeStyles = $derived((tick, enumAvaliableMeleeStyles(world, char).map(([name, style]) => { return {name, style} })));
+  let meleeStyleIndex = $state(0);
+  $effect(() => {
+    setSelectedMeleeStyle(world, char, meleeStyles[meleeStyleIndex].name);
+  });
+
   const select = (part) => () => {
     if (selected === part) {
       selected = null;
@@ -56,6 +68,7 @@
   ┌╨┐
   ╜ ╙
 -->
+<!-- ░▒▓█ -->
 
 <section class="stat" style="{dynamicStyle}">
   ${Cash.amount[char]} L1+23%
@@ -65,15 +78,20 @@
  ≥─╥─≤
   ┌╨┐
   ╜ ╙
-  </span>
+  </span></pre>
 
   <!-- <BodyPartIcon {health} {selected} {select} part="head">(^_^)</BodyPartIcon>
     <BodyPartIcon {health} {selected} {select} part="larm">╓</BodyPartIcon><BodyPartIcon {health} {selected} {select} part="body">─╥─</BodyPartIcon><BodyPartIcon {health} {selected} {select} part="rarm">╖</BodyPartIcon>
     <BodyPartIcon {health} {selected} {select} part="body">║║║</BodyPartIcon>
     <BodyPartIcon {health} {selected} {select} part="lleg">╜</BodyPartIcon> <BodyPartIcon {health} {selected} {select} part="rleg">╙</BodyPartIcon> -->
-  </pre>
-  <br/>
-  {description}
+  <!-- </pre> -->
+  <div>{description}</div>
+  <div>[<span class="stamina">{staminaLine}</span>]</div>
+  <div>[<span class="blood">{bloodLine}</span>]</div>
+  <div>
+    <h3>Style</h3>
+    <Select options={meleeStyles} bind:index={meleeStyleIndex} />
+  </div>
   <button>More</button>
 </section>
 
@@ -94,4 +112,14 @@
     }
   }
 
+  .stamina {
+    font-family: 'Square';
+    color: #384;
+  }
+
+  .blood {
+    font-family: 'Square';
+    color: #834;
+  }
+
 </style>

+ 2 - 4
src/routes/djii/Equipment.svelte

@@ -1,17 +1,15 @@
 <script>
   import { describe, nullEntity } from '$lib';
-  import { getEquipped } from './equipment';
 
-  let { world, char } = $props();
+  let { world, equipment } = $props();
 
-  let equipment = $derived(getEquipped(world, char));
 </script>
 
 <div>
   <h3>─ Equipment ─</h3>
   {#each equipment as [slot, item]}
     <div>
-      {slot}: {console.log(item, nullEntity), item === nullEntity ? "none" : describe(world, item)}
+      {slot}: {item === nullEntity ? "none" : describe(world, item)}
     </div>
   {/each}
 </div>

+ 4 - 10
src/routes/djii/Game.svelte

@@ -25,6 +25,7 @@
   import Inventory from './Inventory.svelte';
   import { getInventory } from '$lib/inventory';
   import Equipment from './Equipment.svelte';
+  import { getEquipped } from './equipment';
 
   let { game } = $props();
 
@@ -56,6 +57,7 @@
   };
 
   let inventory = $derived((tick, getInventory(game.world, game.pc)));
+  let equipment = $derived((tick, getEquipped(game.world, game.pc)));
 
   let inspectTile = $state(undefined);
   const selectInspectTile = (tile) => {
@@ -162,8 +164,8 @@
   </Scrollable>
   <TileMap bind:this={tileMapWidget} bind:tiles={tileMap} zoomlevel={mapZoom} {inspectTile} {select} {selectInspectTile} onKeyDown={onKeyPressTiles} />
   <div class="rightside">
-    <CharStatus world={game.world} char={game.pc} />
-    <Equipment world={game.world} char={game.pc} />
+    <CharStatus world={game.world} char={game.pc} bind:tick />
+    <Equipment world={game.world} char={game.pc} {equipment} />
     <Inventory world={game.world} char={game.pc} {inventory} />
     <Scrollable height="50vh" scrollToEnd={true}>
       <Log bind:log={theLog} {zoomlevel} />
@@ -178,12 +180,4 @@
     grid-template-areas:
       "leftside main rightside";
   }
-
-  .leftside {
-    grid-area: leftside;
-  }
-
-  .rightside {
-    grid-area: rightside;
-  }
 </style>

+ 7 - 5
src/routes/djii/Skills.svelte

@@ -1,20 +1,22 @@
 <script>
+  import { entityToObject } from '$lib/serialize';
   import ItemList from '../ItemList.svelte';
   import Skill from './Skill.svelte';
-  import { FreeSkills, enumSkills } from './skill';
+  import { FreeSkills, enumAvailableSkills, enumSkills } from './skill';
+  import {} from './skills';
 
-  const skills = enumSkills();
+  let { tick, world, char } = $props();
 
-  let { tick, game } = $props();
+  let skills = $derived(char ? (tick, enumAvailableSkills(world, char)) : []);
 
-  let freePoints = $derived((tick, FreeSkills.amount[game.pc]));
+  let freePoints = $derived((tick, FreeSkills.amount[char]));
 </script>
 
 <div>
   <h3>Skills</h3>
   <ItemList>
     {#each skills as [_, skill]}
-      <Skill {skill} character={game.pc} bind:tick />
+      <Skill {skill} character={char} bind:tick />
     {/each}
   </ItemList>
   {freePoints} left

+ 0 - 0
src/routes/djii/Stats.svelte


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