fun: an actual chest
This commit is contained in:
parent
99b8d0f633
commit
4777a2a3a5
|
@ -152,8 +152,8 @@ export default class Component {
|
|||
}
|
||||
}
|
||||
destroy() {}
|
||||
toNet() {
|
||||
return Component.constructor.filterDefaults(this);
|
||||
toNet(recipient, data) {
|
||||
return data || Component.constructor.filterDefaults(this);
|
||||
}
|
||||
toJSON() {
|
||||
return Component.constructor.filterDefaults(this);
|
||||
|
|
|
@ -137,12 +137,14 @@ export default class Inventory extends Component {
|
|||
}
|
||||
}
|
||||
if (swapped) {
|
||||
for (const [l, r] of swapped) {
|
||||
for (const [l, otherEntityId, r] of swapped) {
|
||||
const otherInstance = this.get(otherEntityId);
|
||||
const {$$items: $$otherItems, slots: otherSlots} = otherInstance;
|
||||
const tmp = [$$items[l], slots[l]];
|
||||
[$$items[l], slots[l]] = [$$items[r], slots[r]];
|
||||
[$$items[r], slots[r]] = tmp;
|
||||
if ($$items[r]) {
|
||||
$$items[r].slot = r;
|
||||
[$$items[l], slots[l]] = [$$otherItems[r], otherSlots[r]];
|
||||
[$$otherItems[r], otherSlots[r]] = tmp;
|
||||
if ($$otherItems[r]) {
|
||||
$$otherItems[r].slot = r;
|
||||
}
|
||||
if ($$items[l]) {
|
||||
$$items[l].slot = l;
|
||||
|
@ -189,18 +191,26 @@ export default class Inventory extends Component {
|
|||
}
|
||||
}
|
||||
}
|
||||
swapSlots(l, r) {
|
||||
swapSlots(l, OtherInventory, r) {
|
||||
const {$$items, slots} = this;
|
||||
const {$$items: $$otherItems, slots: otherSlots} = OtherInventory;
|
||||
const tmp = [$$items[l], slots[l]];
|
||||
[$$items[l], slots[l]] = [$$items[r], slots[r]];
|
||||
[$$items[r], slots[r]] = tmp;
|
||||
[$$items[l], slots[l]] = [$$otherItems[r], otherSlots[r]];
|
||||
[$$otherItems[r], otherSlots[r]] = tmp;
|
||||
if (undefined === slots[l]) {
|
||||
delete slots[l];
|
||||
}
|
||||
if (undefined === slots[r]) {
|
||||
delete slots[r];
|
||||
if (undefined === otherSlots[r]) {
|
||||
delete otherSlots[r];
|
||||
}
|
||||
Component.markChange(this.entity, 'swapped', [[l, r]]);
|
||||
Component.markChange(this.entity, 'swapped', [[l, OtherInventory.entity, r]]);
|
||||
Component.markChange(OtherInventory.entity, 'swapped', [[r, this.entity, l]]);
|
||||
}
|
||||
toNet(recipient, data) {
|
||||
if (recipient.id !== this.entity && this !== recipient.Player.openInventory) {
|
||||
return {};
|
||||
}
|
||||
return super.toNet(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,51 @@
|
|||
import Component from '@/ecs/component.js';
|
||||
|
||||
export default class Player extends Component {}
|
||||
const State = {
|
||||
CLOSED: 0,
|
||||
OPENING: 1,
|
||||
OPEN: 2,
|
||||
CLOSING: 3,
|
||||
};
|
||||
|
||||
export default class Player extends Component {
|
||||
instanceFromSchema() {
|
||||
const {ecs} = this;
|
||||
return class PlayerInstance extends super.instanceFromSchema() {
|
||||
$$openInventory;
|
||||
$$openInventoryState = State.CLOSED;
|
||||
closeInventory() {
|
||||
this.$$openInventoryState = State.CLOSING;
|
||||
}
|
||||
updateAttachments(update) {
|
||||
if (!this.$$openInventory) {
|
||||
return;
|
||||
}
|
||||
if (![State.OPENING, State.CLOSING].includes(this.$$openInventoryState)) {
|
||||
return;
|
||||
}
|
||||
if (!ecs.get(this.$$openInventory.entity)) {
|
||||
return;
|
||||
}
|
||||
if (!update[this.$$openInventory.entity]) {
|
||||
update[this.$$openInventory.entity] = {};
|
||||
}
|
||||
if (this.$$openInventoryState === State.OPENING) {
|
||||
update[this.$$openInventory.entity].Inventory = this.$$openInventory.toNet(ecs.get(this.entity))
|
||||
this.$$openInventoryState = State.OPEN;
|
||||
}
|
||||
else {
|
||||
update[this.$$openInventory.entity].Inventory = {closed: true};
|
||||
this.$$openInventory = undefined;
|
||||
this.$$openInventoryState = State.CLOSED;
|
||||
}
|
||||
}
|
||||
get openInventory() {
|
||||
return this.$$openInventory;
|
||||
}
|
||||
set openInventory(Inventory) {
|
||||
this.$$openInventoryState = State.OPENING;
|
||||
this.$$openInventory = Inventory;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,9 +58,9 @@ export default class Sprite extends Component {
|
|||
get scale() {
|
||||
return {x: this.scaleX, y: this.scaleY};
|
||||
}
|
||||
toNet() {
|
||||
toNet(recipient, data) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const {elapsed, ...rest} = super.toNet();
|
||||
const {elapsed, ...rest} = super.toNet(recipient, data);
|
||||
return rest;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -43,14 +43,23 @@ export default class EntityFactory {
|
|||
};
|
||||
}
|
||||
Object.defineProperties(Entity.prototype, properties);
|
||||
Entity.prototype.updateAttachments = new Function('update', `
|
||||
${
|
||||
sorted
|
||||
.filter((componentName) => (
|
||||
Components[componentName].Instance.prototype.updateAttachments
|
||||
))
|
||||
.map((type) => `this.${type}.updateAttachments(update)`).join('; ')
|
||||
}
|
||||
`);
|
||||
Entity.prototype.toJSON = new Function('', `
|
||||
return {
|
||||
${sorted.map((type) => `${type}: this.${type}.toJSON()`).join(', ')}
|
||||
};
|
||||
`);
|
||||
Entity.prototype.toNet = new Function('', `
|
||||
Entity.prototype.toNet = new Function('recipient', `
|
||||
return {
|
||||
${sorted.map((type) => `${type}: this.${type}.toNet()`).join(', ')}
|
||||
${sorted.map((type) => `${type}: this.${type}.toNet(recipient)`).join(', ')}
|
||||
};
|
||||
`);
|
||||
walk.class = Entity;
|
||||
|
|
24
app/ecs/systems/inventory-closer.js
Normal file
24
app/ecs/systems/inventory-closer.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import {System} from '@/ecs/index.js';
|
||||
import {distance} from '@/util/math.js';
|
||||
|
||||
export default class InventoryCloser extends System {
|
||||
|
||||
static queries() {
|
||||
return {
|
||||
default: ['Player'],
|
||||
};
|
||||
}
|
||||
|
||||
tick() {
|
||||
for (const {Player, Position} of this.select('default')) {
|
||||
const {openInventory} = Player;
|
||||
if (openInventory) {
|
||||
const {Position: inventoryPosition} = this.ecs.get(openInventory.entity);
|
||||
if (distance(Position, inventoryPosition) > 64) {
|
||||
Player.closeInventory();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,6 @@ export default function Bag({
|
|||
className={styles.bag}
|
||||
style={isInventoryOpen ? {transition: 'opacity 50ms'} : {opacity: 0, left: '-440px'}}
|
||||
>
|
||||
|
||||
<Grid
|
||||
color="rgba(02, 02, 28, 0.6)"
|
||||
columns={10}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
.bag {
|
||||
align-self: left;
|
||||
display: inline-block;
|
||||
left: 20px;
|
||||
line-height: 0;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
top: 74px;
|
||||
|
|
27
app/react/components/dom/external.jsx
Normal file
27
app/react/components/dom/external.jsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import styles from './external.module.css';
|
||||
|
||||
import Grid from './grid.jsx';
|
||||
|
||||
/**
|
||||
* External inventory.
|
||||
*/
|
||||
export default function External({
|
||||
isInventoryOpen,
|
||||
onActivate,
|
||||
slots,
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={styles.external}
|
||||
style={isInventoryOpen ? {transition: 'opacity 50ms'} : {opacity: 0, top: '450px'}}
|
||||
>
|
||||
<Grid
|
||||
color="rgba(57, 02, 02, 0.6)"
|
||||
columns={10}
|
||||
label="Chest"
|
||||
onActivate={onActivate}
|
||||
slots={slots}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
7
app/react/components/dom/external.module.css
Normal file
7
app/react/components/dom/external.module.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
.external {
|
||||
left: 20px;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
top: 274px;
|
||||
transition: top 150ms, opacity 200ms;
|
||||
}
|
|
@ -49,7 +49,7 @@ export default function Grid({
|
|||
));
|
||||
return (
|
||||
<div className={styles.gridWrapper}>
|
||||
<p className={styles.label}>{label}</p>
|
||||
<p className={styles.label}> {label} </p>
|
||||
<div
|
||||
className={styles.grid}
|
||||
style={{
|
||||
|
|
|
@ -1,27 +1,7 @@
|
|||
.hotbar {
|
||||
align-self: left;
|
||||
display: inline-block;
|
||||
left: 20px;
|
||||
line-height: 0;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
transition: top 150ms, opacity 200ms;
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
|
||||
left: 50%;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
text-shadow:
|
||||
0px -1px 0px black,
|
||||
1px 0px 0px black,
|
||||
0px 1px 0px black,
|
||||
-1px 0px 0px black
|
||||
;
|
||||
top: -17.5px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import Chat from './dom/chat/chat.jsx';
|
|||
import Bag from './dom/bag.jsx';
|
||||
import Dom from './dom/dom.jsx';
|
||||
import Entities from './dom/entities.jsx';
|
||||
import Grid from './dom/grid.jsx';
|
||||
import External from './dom/external.jsx';
|
||||
import HotBar from './dom/hotbar.jsx';
|
||||
import Pixi from './pixi/pixi.jsx';
|
||||
import Devtools from './devtools.jsx';
|
||||
|
@ -26,7 +26,7 @@ const KEY_MAP = {
|
|||
};
|
||||
|
||||
function emptySlots() {
|
||||
return Array(40).fill(undefined);
|
||||
return Array(50).fill(undefined);
|
||||
}
|
||||
|
||||
const devEventsChannel = new EventEmitter();
|
||||
|
@ -60,6 +60,8 @@ function Ui({disconnected}) {
|
|||
const [hotbarIsHidden, setHotbarIsHidden] = useState(true);
|
||||
const [hotbarHideHandle, setHotbarHideHandle] = useState();
|
||||
const [isInventoryOpen, setIsInventoryOpen] = useState(false);
|
||||
const [externalInventory, setExternalInventory] = useState();
|
||||
const [externalInventorySlots, setExternalInventorySlots] = useState();
|
||||
useEffect(() => {
|
||||
async function setEcsStuff() {
|
||||
const {default: Components} = await import('@/ecs/components/index.js');
|
||||
|
@ -333,8 +335,8 @@ function Ui({disconnected}) {
|
|||
if (update.MainEntity) {
|
||||
setMainEntity(localMainEntity = id);
|
||||
}
|
||||
if (localMainEntity === id) {
|
||||
if (update.Inventory) {
|
||||
if (update.Inventory) {
|
||||
if (localMainEntity === id) {
|
||||
setBufferSlot(entity.Inventory.item(0));
|
||||
const newInventorySlots = emptySlots();
|
||||
for (let i = 1; i < 41; ++i) {
|
||||
|
@ -342,6 +344,25 @@ function Ui({disconnected}) {
|
|||
}
|
||||
setInventorySlots(newInventorySlots);
|
||||
}
|
||||
else if (update.Inventory.slots) {
|
||||
const newInventorySlots = Array(30).fill(undefined);
|
||||
for (let i = 0; i < 30; ++i) {
|
||||
newInventorySlots[i] = entity.Inventory.item(i);
|
||||
}
|
||||
setExternalInventory(entity.id)
|
||||
setExternalInventorySlots(newInventorySlots);
|
||||
setIsInventoryOpen(true);
|
||||
setHotbarIsHidden(false);
|
||||
if (hotbarHideHandle) {
|
||||
clearTimeout(hotbarHideHandle);
|
||||
}
|
||||
}
|
||||
else if (update.Inventory.closed) {
|
||||
setExternalInventory();
|
||||
setExternalInventorySlots();
|
||||
}
|
||||
}
|
||||
if (localMainEntity === id) {
|
||||
if (update.Wielder && 'activeSlot' in update.Wielder) {
|
||||
setActiveSlot(update.Wielder.activeSlot);
|
||||
}
|
||||
|
@ -355,7 +376,7 @@ function Ui({disconnected}) {
|
|||
setCamera({x, y});
|
||||
}
|
||||
}
|
||||
}, [camera, ecs, mainEntity, scale]);
|
||||
}, [camera, ecs, hotbarHideHandle, mainEntity, scale]);
|
||||
useEffect(() => {
|
||||
function onContextMenu(event) {
|
||||
event.preventDefault();
|
||||
|
@ -507,7 +528,7 @@ function Ui({disconnected}) {
|
|||
keepHotbarOpen();
|
||||
client.send({
|
||||
type: 'Action',
|
||||
payload: {type: 'swapSlots', value: [0, i + 1]},
|
||||
payload: {type: 'swapSlots', value: [0, mainEntity, i + 1]},
|
||||
});
|
||||
}}
|
||||
slots={inventorySlots.slice(0, 10)}
|
||||
|
@ -515,14 +536,25 @@ function Ui({disconnected}) {
|
|||
<Bag
|
||||
isInventoryOpen={isInventoryOpen}
|
||||
onActivate={(i) => {
|
||||
keepHotbarOpen();
|
||||
client.send({
|
||||
type: 'Action',
|
||||
payload: {type: 'swapSlots', value: [0, i + 11]},
|
||||
payload: {type: 'swapSlots', value: [0, mainEntity, i + 11]},
|
||||
});
|
||||
}}
|
||||
slots={inventorySlots.slice(10)}
|
||||
slots={inventorySlots.slice(10, 20)}
|
||||
/>
|
||||
{externalInventory && (
|
||||
<External
|
||||
isInventoryOpen={isInventoryOpen}
|
||||
onActivate={(i) => {
|
||||
client.send({
|
||||
type: 'Action',
|
||||
payload: {type: 'swapSlots', value: [0, externalInventory, i]},
|
||||
});
|
||||
}}
|
||||
slots={externalInventorySlots}
|
||||
/>
|
||||
)}
|
||||
<Entities
|
||||
camera={camera}
|
||||
scale={scale}
|
||||
|
|
|
@ -22,6 +22,7 @@ export default function createEcs(Ecs) {
|
|||
'RunTickingPromises',
|
||||
'Water',
|
||||
'Interactions',
|
||||
'InventoryCloser',
|
||||
];
|
||||
defaultSystems.forEach((defaultSystem) => {
|
||||
const System = ecs.system(defaultSystem);
|
||||
|
|
|
@ -98,13 +98,14 @@ export default async function createHomestead(id) {
|
|||
Interactive: {
|
||||
interacting: 1,
|
||||
interactScript: `
|
||||
subject.Interlocutor.dialogue({
|
||||
body: "Sure, I'm a treasure chest. Probably. Do you really think that means you're about to get some treasure? Hah!",
|
||||
monopolizer: true,
|
||||
offset: {x: 0, y: -48},
|
||||
origin: 'track',
|
||||
position: 'track',
|
||||
})
|
||||
initiator.Player.openInventory = subject.Inventory;
|
||||
// subject.Interlocutor.dialogue({
|
||||
// body: "Sure, I'm a treasure chest. Probably. Do you really think that means you're about to get some treasure? Hah!",
|
||||
// monopolizer: true,
|
||||
// offset: {x: 0, y: -48},
|
||||
// origin: 'track',
|
||||
// position: 'track',
|
||||
// })
|
||||
`,
|
||||
},
|
||||
Interlocutor: {},
|
||||
|
@ -132,7 +133,7 @@ export default async function createHomestead(id) {
|
|||
Sprite: {
|
||||
anchorX: 0.5,
|
||||
anchorY: 0.7,
|
||||
source: '/assets/chest.json',
|
||||
source: '/assets/chest/chest.json',
|
||||
},
|
||||
Ticking: {},
|
||||
VisibleAabb: {},
|
||||
|
|
|
@ -201,7 +201,11 @@ export default class Engine {
|
|||
}
|
||||
case 'swapSlots': {
|
||||
if (!Controlled.locked) {
|
||||
Inventory.swapSlots(...payload.value);
|
||||
const [l, other, r] = payload.value;
|
||||
const {Inventory: OtherInventory} = ecs.get(other);
|
||||
if (OtherInventory) {
|
||||
Inventory.swapSlots(l, OtherInventory, r);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -413,7 +417,6 @@ export default class Engine {
|
|||
updateFor(connection) {
|
||||
const update = {};
|
||||
const {entity, memory} = this.connectedPlayers.get(connection);
|
||||
const mainEntityId = entity.id;
|
||||
const ecs = this.ecses[entity.Ecs.path];
|
||||
// Entities within half a screen offscreen.
|
||||
const x0 = entity.Position.x - RESOLUTION.x;
|
||||
|
@ -429,17 +432,24 @@ export default class Engine {
|
|||
nearby.add(master);
|
||||
const lastNearby = new Set(memory.nearby.values());
|
||||
const firstUpdate = 0 === lastNearby.size;
|
||||
for (const entity of nearby) {
|
||||
const {id} = entity;
|
||||
for (const nearbyEntity of nearby) {
|
||||
const {id} = nearbyEntity;
|
||||
lastNearby.delete(id);
|
||||
if (!memory.nearby.has(id)) {
|
||||
update[id] = entity.toNet();
|
||||
if (mainEntityId === id) {
|
||||
update[id] = nearbyEntity.toNet(entity);
|
||||
if (entity.id === id) {
|
||||
update[id].MainEntity = {};
|
||||
}
|
||||
}
|
||||
else if (ecs.diff[id]) {
|
||||
update[id] = ecs.diff[id];
|
||||
const nearbyEntityDiff = {};
|
||||
for (const componentName in ecs.diff[id]) {
|
||||
nearbyEntityDiff[componentName] = nearbyEntity[componentName].toNet(
|
||||
entity,
|
||||
ecs.diff[id][componentName],
|
||||
);
|
||||
}
|
||||
update[id] = nearbyEntityDiff;
|
||||
}
|
||||
memory.nearby.add(id);
|
||||
}
|
||||
|
@ -447,6 +457,7 @@ export default class Engine {
|
|||
memory.nearby.delete(id);
|
||||
update[id] = false;
|
||||
}
|
||||
entity.updateAttachments(update);
|
||||
// Tile layer chunking
|
||||
const {TileLayers} = master;
|
||||
const {layers} = TileLayers;
|
||||
|
|
Loading…
Reference in New Issue
Block a user