From 6843b19c61d69833bbc7aa685be4fe5230ea5b6b Mon Sep 17 00:00:00 2001 From: John Edvard Reiten Date: Mon, 22 Aug 2022 21:35:57 +0900 Subject: [PATCH] feat: Integrate Arcadian headpieces --- package.json | 4 ++-- src/Player.js | 42 +++++++++++++++++++++++++++++++++++-- src/arcadianApi.js | 44 +++++++++++++++++++++++++++++++++++++++ src/assets/img/heart.png | Bin 0 -> 150 bytes src/gameEvents.js | 1 + src/index.js | 19 ++++++++++++++++- src/store.js | 1 + 7 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 src/arcadianApi.js create mode 100644 src/assets/img/heart.png create mode 100644 src/store.js diff --git a/package.json b/package.json index ff59efc..9cb0b23 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "skeleton rope physics game", "source": "src/index.html", "scripts": { - "start": "parcel serve", - "build": "rm -rf dist && parcel build src/index.html", + "start": "parcel serve --no-cache", + "build": "rm -rf dist && parcel build --no-cache src/index.html", "zip": "cd dist && zip game.zip *.png *.html *.css *.js static/level/*.json", "build:zip": "npm run build && cd dist && zip game.zip *.png *.html *.css *.js static/level/*.json", "build:roadroller": "npm run build && roadroller dist/index.*.js -o dist/index.*.js && npm run zip", diff --git a/src/Player.js b/src/Player.js index 01043f3..7de9d2e 100644 --- a/src/Player.js +++ b/src/Player.js @@ -4,16 +4,20 @@ import { getPointer, Sprite, on } from 'kontra'; import { PlayerControls } from './PlayerControls'; import { PointMass } from './PointMass'; import { fgc2, RESTING_DISTANCE } from './constants'; -import { GOAL_COLLISION } from './gameEvents'; +import { ARCADIAN_ADDED, GOAL_COLLISION } from './gameEvents'; export class Player { game; rope = []; // list of pointmasses pointMass; // used to attach to the end of the rope playerControls; - sprite = { render: () => {}, x: 0, y: 0 }; // draw sprite on pointmass position scale = 4; + sprite = { render: () => {}, x: 0, y: 0 }; // draw sprite on pointmass position + headSprite = { render: () => {}, x: 0, y: 0 }; // From Arcadian API hasWon = false; + headImg = { width: 0, height: 0 }; + headOffset = { x: 10, y: 38 }; + isLeft = false; constructor({ game, levelData }) { this.game = game; @@ -64,6 +68,21 @@ export class Player { }); }; } + createHeadSprite(img) { + const scale = this.scale / 2; + let scaleX = scale; + if (this.isLeft) scaleX = scaleX * -1; + this.headSprite = Sprite({ + x: this.pointMass.x - img.width, + y: this.pointMass.y - img.height, + anchor: { x: 0.5, y: 0.5 }, + width: 8, + height: 8, + image: img, + scaleX: scaleX, + scaleY: scale, + }); + } createRope({ startX, startY, ropeLength }) { const anchor = new PointMass(startX, startY, { @@ -95,6 +114,7 @@ export class Player { renderPlayer(_ctx) { this.sprite.render(); + this.headSprite.render(); } applyForce(fX, fY) { @@ -102,11 +122,18 @@ export class Player { } changePlayerDirection(isLeft) { + this.isLeft = isLeft; if (isLeft) { this.sprite.scaleX = -this.scale; + this.headSprite.scaleX = -this.scale / 2; } else { this.sprite.scaleX = this.scale; + this.headSprite.scaleX = this.scale / 2; } + // prevent headpiece from flashing + this.headSprite.x = + this.sprite.x - + (this.headImg.width - this.headOffset.x) * Math.sign(this.sprite.scaleX); } render(ctx) { @@ -131,6 +158,12 @@ export class Player { update() { this.sprite.x = this.pointMass.x; this.sprite.y = this.pointMass.y; + this.headSprite.x = + this.pointMass.x - + (this.headImg.width - this.headOffset.x) * Math.sign(this.sprite.scaleX); + + this.headSprite.y = + this.pointMass.y - this.headImg.height + +this.headOffset.y; this.updateRope(); this.dragRope(); // TODO (johnedvard) Only enable in local and beta env @@ -162,8 +195,13 @@ export class Player { listenForGameEvents() { on(GOAL_COLLISION, this.onGoalCollision); + on(ARCADIAN_ADDED, this.onArcadianAdded); } onGoalCollision = () => { this.hasWon = true; }; + onArcadianAdded = ({ img }) => { + this.headImg = img; + this.createHeadSprite(img); + }; } diff --git a/src/arcadianApi.js b/src/arcadianApi.js new file mode 100644 index 0000000..e1d07ca --- /dev/null +++ b/src/arcadianApi.js @@ -0,0 +1,44 @@ +export const arcadianHeadImages = []; + +export async function queryArcadian(id) { + const partsUrl = + 'https://nftstorage.link/ipfs/bafybeib2ir3zpgd3cmizcv7shekjeftcwq7apjib6i5gz2sjc4vajwtgiy'; + + // this is the same url that you get from querying token uri from the contract + const url = 'https://api.arcadians.io/' + id; + + return new Promise((resolve, reject) => { + // create a GET request + var xhr = new XMLHttpRequest(); + xhr.onload = () => { + if (xhr.readyState === xhr.DONE) { + if (xhr.status !== 200) reject(null); + let gender = 'female'; + var data = JSON.parse(xhr.response); + console.log('data', data); + + const headPart = data.attributes.find( + (trait) => trait.trait_type === 'Head' + ); + const genderPart = data.attributes.find( + (trait) => trait.trait_type === 'Class' + ); + if (genderPart.value.match('Male')) gender = 'male'; + const headUrl = headPart.value.toLowerCase().replaceAll(' ', '-'); + var img = new Image(); + img.src = `${partsUrl}/${gender}/head/${headUrl}.png`; + img.addEventListener( + 'load', + () => { + localStorage.setItem('Arcadian #' + id, xhr.response); + arcadianHeadImages.push(img); + resolve(img); + }, + false + ); + } + }; + xhr.open('GET', url); + xhr.send(); + }); +} diff --git a/src/assets/img/heart.png b/src/assets/img/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..25d26e61505b1559ab82311987d18052d8def1d4 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|0z6$DLnNjq zCn&J(srmox|LO1f{}ykSl#q~!h}&2H`Tz1mVmq8tw(VHJp)9<~)4((3K*zetjX?0X tk-r%v+GZ{HQQu*s{OaV+PohQ)3?iZ?x8@t%nGQ6Y!PC{xWt~$(69A6|G(rFX literal 0 HcmV?d00001 diff --git a/src/gameEvents.js b/src/gameEvents.js index 91c2fb8..15187ef 100644 --- a/src/gameEvents.js +++ b/src/gameEvents.js @@ -1 +1,2 @@ export const GOAL_COLLISION = 'gc'; +export const ARCADIAN_ADDED = 'aa'; diff --git a/src/index.js b/src/index.js index b87ec3a..6c10262 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,15 @@ +import { emit } from 'kontra'; + import { Game } from './Game'; -import { NearConnection } from './near/nearConnection'; +import { ARCADIAN_ADDED } from './gameEvents'; +import { queryArcadian } from './arcadianApi'; import { initLoginLogout } from './near/nearLogin'; +import { NearConnection } from './near/nearConnection'; + const init = () => { new Game(); initNear(); + fetchArcadianHeads(); }; const initNear = () => { @@ -26,4 +32,15 @@ const loadNearApi = () => { document.head.appendChild(script); }); }; + +const fetchArcadianHeads = () => { + // TODO (johnedvard) get from localStorage so we don't call the api too much + // get many headpices + for (let i = 0; i < 60; i += 5) + queryArcadian(i).then((img) => { + if (img) { + emit(ARCADIAN_ADDED, { img }); + } + }); +}; init(); diff --git a/src/store.js b/src/store.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/store.js @@ -0,0 +1 @@ +