Skip to content
This repository was archived by the owner on Oct 16, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions contracts/src/example-plugins/BaseBlue/BaseBlue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import ds from 'downstream';

export default async function update(state) {
// uncomment this to browse the state object in browser console
// this will be logged when selecting a unit and then selecting an instance of this building
//logState(state);

const selectedTile = getSelectedTile(state);
const selectedBuilding = selectedTile && getBuildingOnTile(state, selectedTile);
const canCraft = selectedBuilding && inputsAreCorrect(state, selectedBuilding)
// uncomment this to be restrictve about which units can craft
// this is a client only check - to enforce it in contracts make
// similar changes in BasicFactory.sol
// && unitIsFriendly(state, selectedBuilding)
;

const craft = () => {
const mobileUnit = getMobileUnit(state);

if (!mobileUnit) {
console.log('no selected unit');
return;
}

ds.dispatch({
name: 'BUILDING_USE',
args: [selectedBuilding.id, mobileUnit.id, []],
});

console.log('Craft dispatched');
};

return {
version: 1,
components: [
{
id: 'basic-factory',
type: 'building',
content: [
{
id: 'default',
type: 'inline',
html: '<p>Fill the input slots to enable crafing</p>',
buttons: [
{
text: 'Craft',
type: 'action',
action: craft,
disabled: !canCraft,
},
],
},
],
},
],
};
}

function getMobileUnit(state) {
return state?.selected?.mobileUnit;
}

function getSelectedTile(state) {
const tiles = state?.selected?.tiles || {};
return tiles && tiles.length === 1 ? tiles[0] : undefined;
}

function getBuildingOnTile(state, tile) {
return (state?.world?.buildings || []).find((b) => tile && b.location?.tile?.id === tile.id);
}

// returns an array of items the building expects as input
function getRequiredInputItems(building) {
return building?.kind?.inputs || [];
}

// search through all the bags in the world to find those belonging to this building
function getBuildingBags(state, building) {
return building ? (state?.world?.bags || []).filter((bag) => bag.equipee?.node.id === building.id) : [];
}

// get building input slots
function getInputSlots(state, building) {
// inputs are the bag with key 0 owned by the building
const buildingBags = getBuildingBags(state, building);
const inputBag = buildingBags.find((bag) => bag.equipee.key === 0);

// slots used for crafting have sequential keys startng with 0
return inputBag && inputBag.slots.sort((a, b) => a.key - b.key);
}

// are the required craft input items in the input slots?
function inputsAreCorrect(state, building) {
const requiredInputItems = getRequiredInputItems(building);
const inputSlots = getInputSlots(state, building);

return (
inputSlots &&
inputSlots.length >= requiredInputItems.length &&
requiredInputItems.every(
(requiredItem) =>
inputSlots[requiredItem.key].item.id == requiredItem.item.id &&
inputSlots[requiredItem.key].balance == requiredItem.balance
)
);
}

function logState(state) {
console.log('State sent to pluging:', state);
}

const friendlyPlayerAddresses = [
// 0x402462EefC217bf2cf4E6814395E1b61EA4c43F7
];

function unitIsFriendly(state, selectedBuilding) {
const mobileUnit = getMobileUnit(state);
return (
unitIsBuildingOwner(mobileUnit, selectedBuilding) ||
unitIsBuildingAuthor(mobileUnit, selectedBuilding) ||
friendlyPlayerAddresses.some((addr) => unitOwnerConnectedToWallet(state, mobileUnit, addr))
);
}

function unitIsBuildingOwner(mobileUnit, selectedBuilding) {
//console.log('unit owner id:', mobileUnit?.owner?.id, 'building owner id:', selectedBuilding?.owner?.id);
return mobileUnit?.owner?.id && mobileUnit?.owner?.id === selectedBuilding?.owner?.id;
}

function unitIsBuildingAuthor(mobileUnit, selectedBuilding) {
//console.log('unit owner id:', mobileUnit?.owner?.id, 'building author id:', selectedBuilding?.kind?.owner?.id);
return mobileUnit?.owner?.id && mobileUnit?.owner?.id === selectedBuilding?.kind?.owner?.id;
}

function unitOwnerConnectedToWallet(state, mobileUnit, walletAddress) {
//console.log('Checking player:', state?.player, 'controls unit', mobileUnit, walletAddress);
return mobileUnit?.owner?.id == state?.player?.id && state?.player?.addr == walletAddress;
}

// the source for this code is on github where you can find other example buildings:
// https://github.com/playmint/ds/tree/main/contracts/src/example-plugins
145 changes: 145 additions & 0 deletions contracts/src/example-plugins/BaseBlue/BaseBlue.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Game} from "cog/IGame.sol";
import {State} from "cog/IState.sol";
import {Schema} from "@ds/schema/Schema.sol";
import {Actions} from "@ds/actions/Actions.sol";
import {BuildingKind} from "@ds/ext/BuildingKind.sol";

using Schema for State;

contract BaseBlue is BuildingKind {
function use(
Game ds,
bytes24 buildingInstance,
bytes24,
/*actor*/ bytes memory /*payload*/
) public {
ds.getDispatcher().dispatch(
abi.encodeCall(Actions.CRAFT, (buildingInstance))
);
}

// version of use that restricts crafting to building owner, author or allow list
// these restrictions will not be reflected in the UI unless you make
// similar changes in BasicFactory.js
/*function use(Game ds, bytes24 buildingInstance, bytes24 actor, bytes memory ) public {
State state = GetState(ds);
CheckIsFriendlyUnit(state, actor, buildingInstance);

ds.getDispatcher().dispatch(abi.encodeCall(Actions.CRAFT, (buildingInstance)));
}*/

// version of use that restricts crafting to units carrying a certain item
/*function use(Game ds, bytes24 buildingInstance, bytes24 actor, bytes memory ) public {
// require carrying an idCard
// you can change idCardItemId to another item id
CheckIsCarryingItem(state, actor, idCardItemId);

ds.getDispatcher().dispatch(abi.encodeCall(Actions.CRAFT, (buildingInstance)));
}*/

function GetState(Game ds) internal returns (State) {
return ds.getState();
}

function GetBuildingOwner(
State state,
bytes24 buildingInstance
) internal view returns (bytes24) {
return state.getOwner(buildingInstance);
}

function GetBuildingAuthor(
State state,
bytes24 buildingInstance
) internal view returns (bytes24) {
bytes24 buildingKind = state.getBuildingKind(buildingInstance);
return state.getOwner(buildingKind);
}

function CheckIsFriendlyUnit(
State state,
bytes24 actor,
bytes24 buildingInstance
) internal view {
require(
UnitOwnsBuilding(state, actor, buildingInstance) ||
UnitAuthoredBuilding(state, actor, buildingInstance) ||
UnitOwnedByFriendlyPlayer(state, actor),
"Unit does not have permission to use this building"
);
}

function UnitOwnsBuilding(
State state,
bytes24 actor,
bytes24 buildingInstance
) internal view returns (bool) {
return
state.getOwner(actor) == GetBuildingOwner(state, buildingInstance);
}

function UnitAuthoredBuilding(
State state,
bytes24 actor,
bytes24 buildingInstance
) internal view returns (bool) {
return
state.getOwner(actor) == GetBuildingAuthor(state, buildingInstance);
}

address[] private friendlyPlayerAddresses = [
0x402462EefC217bf2cf4E6814395E1b61EA4c43F7
];

function UnitOwnedByFriendlyPlayer(
State state,
bytes24 actor
) internal view returns (bool) {
address ownerAddress = state.getOwnerAddress(actor);
for (uint256 i = 0; i < friendlyPlayerAddresses.length; i++) {
if (friendlyPlayerAddresses[i] == ownerAddress) {
return true;
}
}
return false;
}

// use cli command 'ds get items' for all current possible ids.
bytes24 idCardItemId = 0x6a7a67f0b29554460000000100000064000000640000004c;

function CheckIsCarryingItem(
State state,
bytes24 actor,
bytes24 item
) internal view {
require(
(UnitIsCarryingItem(state, actor, item)),
"Unit must be carrying specified item"
);
}

function UnitIsCarryingItem(
State state,
bytes24 actor,
bytes24 item
) internal view returns (bool) {
for (uint8 bagIndex = 0; bagIndex < 2; bagIndex++) {
bytes24 bag = state.getEquipSlot(actor, bagIndex);
if (bag != 0) {
for (uint8 slot = 0; slot < 4; slot++) {
(bytes24 resource /*uint64 balance*/, ) = state.getItemSlot(
bag,
slot
);
if (resource == item) {
return true;
}
}
}
}
return false;
}
}
34 changes: 34 additions & 0 deletions contracts/src/example-plugins/BaseBlue/BaseBlue.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
kind: BuildingKind
spec:
category: factory
name: Blue Base
description: This is the Base of Team Blue
model: 14-14
color: 0
contract:
file: ./BaseBlue.sol
plugin:
file: ./BaseBlue.js
materials:
- name: Red Goo
quantity: 100
- name: Green Goo
quantity: 100
- name: Blue Goo
quantity: 100
inputs: []
outputs:
- name: Nothing
quantity: 1

---
kind: Item
spec:
name: Nothing
icon: xx-01
goo:
red: 0
green: 0
blue: 0
stackable: false
Loading