diff --git a/app/src/components/MarchingBand.tsx b/app/src/components/MarchingBand.tsx new file mode 100644 index 0000000..0bc09cb --- /dev/null +++ b/app/src/components/MarchingBand.tsx @@ -0,0 +1,115 @@ +import { useCallback, useEffect, useMemo, useState } from 'react'; +import styles from '../styles/Army.module.css'; + +interface MarchingGroupType { + id: number, + people: [number, number][] + removeGroup: (id: number) => void +} + +const MarchingGroup = ({ people, id, removeGroup }: MarchingGroupType) => { + useEffect(() => { + const destroyTimeout = setTimeout(() => { + removeGroup(id) + }, 18000); + return () => clearTimeout(destroyTimeout) + }, [id, removeGroup]) + const groupHtml = useMemo(() => { + return
+ {people.map(([personStatus, role], index) => { + const cssRole = intToRole(role); + const cssStatus = intToStatus(personStatus); + return
+ })} +
+ }, [people, id]) + return groupHtml +} + +const MarchingBand = () => { + const [groups, setGroups] = useState([]); + const removeGroup = useCallback((id: number) => { + setGroups(p => p.filter(p => p.id !== id)); + }, [setGroups]) + const addGroup = useCallback(() => { + const nPeople = getRandomInt(2, 4); + const people: [number, number][] = [] + for (let i = 0; i < nPeople; i++) { + people.push([getRandomInt(0, 3), getRandomInt(0, 14)]) + } + setGroups(g => [...g, { id: Math.random(), people, removeGroup }]); + }, [removeGroup]) + + useEffect(() => { + const addingInterval = setInterval(() => { + addGroup() + }, 8000) + return () => clearInterval(addingInterval) + }, [addGroup]) + + return
+ {groups.map(({ id, people }) => + )} +
+} + +const getRandomInt = (min: number, max: number) => Math.floor(Math.random() * (max + 1 - min) + min) + +const intToRole = (val: number) => { + switch (val) { + case 0: + return styles.alchemist + case 1: + return styles.archer + case 2: + return styles.cleric + case 3: + return styles.druid + case 4: + return styles.dwarf + case 5: + return styles.monk + case 6: + return styles.necromancer + case 7: + return styles.paladin + case 8: + return styles.ranger + case 9: + return styles.rogue + case 10: + return styles.scribe + case 11: + return styles.warrior + case 12: + return styles.wizard + case 13: + return styles.healer + case 14: + return styles.hunter + default: + return styles.wizard + } +} +const intToStatus = (val: number) => { + switch (val) { + case 0: + return styles.moloch_denier; + case 1: + return styles.apprentice; + case 2: + return styles.anointed; + case 3: + return styles.champion; + default: + return styles.moloch_denier; + } +} + +export default MarchingBand + diff --git a/app/src/components/Scene.tsx b/app/src/components/Scene.tsx index 4702daa..1561e61 100644 --- a/app/src/components/Scene.tsx +++ b/app/src/components/Scene.tsx @@ -2,6 +2,7 @@ import React from "react" import styles from '../styles/Background.module.css'; import Tower from "./Tower"; import Army from "./Army"; +import MarchingBand from "./MarchingBand"; const Scene = () => { return
@@ -11,6 +12,7 @@ const Scene = () => {
+ {/* */}
diff --git a/app/src/styles/Army.module.css b/app/src/styles/Army.module.css index bfdb598..1bf445c 100644 --- a/app/src/styles/Army.module.css +++ b/app/src/styles/Army.module.css @@ -1,3 +1,11 @@ +.marchingBand { + position: absolute; + left: 22px; + right: 22px; + bottom: 22px; + top: 22px; + overflow: hidden; +} .army-gathering { position: absolute; bottom: 22px; @@ -13,6 +21,45 @@ background-repeat: no-repeat; transform-origin: bottom center; } +.marchingGroup { + position: absolute; + user-select: none; + pointer-events: none; + left: 0; + bottom: 84px; + animation: marching 20s linear; + width: 200px; + & > div { + position: absolute; + } + & > div:nth-child(1) { + left: 30px; + animation: + marchingPerson 20s linear, + marchingPersonSkew 1s ease infinite, + marchingPersonLeft1 20s linear; + } + & > div:nth-child(2) { + left: 0px; + animation: + marchingPerson 20s linear, + marchingPersonSkew 1s ease infinite; + } + & > div:nth-child(3) { + left: -30px; + animation: + marchingPerson 20s linear, + marchingPersonSkew 1s ease infinite, + marchingPersonLeft3 20s linear; + } + & > div:nth-child(4) { + left: 60px; + animation: + marchingPerson 20s linear, + marchingPersonSkew 1s ease infinite, + marchingPersonLeft4 20s linear; + } +} .tavern_keeper { background-image: url("/roles/tavern-keeper.svg"); } @@ -122,3 +169,156 @@ font-size: 0.8rem; user-select: none; } + +@keyframes marching { + 0% { + transform: translate(-100px, -84px); + } + 8% { + /* approaches fire */ + transform: translate(72px, -84px); + } + 15% { + /* approaches road */ + transform: translate(152px, -174px); + } + 25% { + /* first road turn */ + transform: translate(122px, -284px); + } + 45% { + /* second road turn */ + transform: translate(256px, -374px); + } + 75% { + /* third road turn */ + transform: translate(159px, -416px); + } + 100% { + /* vanishes into distance */ + transform: translate(180px, -425px); + } +} + +@keyframes marchingPerson { + 0% { + background-size: 100% 100%; + } + 8% { + /* approaches fire */ + } + 15% { + /* approaches road */ + background-size: 95% 95%; + } + 25% { + /* first road turn */ + background-size: 80% 80%; + } + 45% { + /* second road turn */ + background-size: 65% 65%; + } + 75% { + /* third road turn */ + background-size: 15% 15%; + } + 100% { + /* vanishes into distance */ + background-size: 2% 2%; + } +} + +@keyframes marchingPersonLeft1 { + 0% { + left: 30px; + } + 15% { + /* approaches road */ + left: calc(30px * 0.95); + } + 25% { + /* first road turn */ + left: calc(30px * 0.8); + } + 45% { + /* second road turn */ + left: calc(30px * 0.65); + } + 75% { + /* third road turn */ + left: calc(30px * 0.15); + } + 100% { + /* vanishes into distance */ + left: calc(30px * 0.02); + } +} + +@keyframes marchingPersonLeft3 { + 0% { + left: -30px; + } + 15% { + /* approaches road */ + left: calc(-30px * 0.95); + } + 25% { + /* first road turn */ + left: calc(-30px * 0.8); + } + 45% { + /* second road turn */ + left: calc(-30px * 0.65); + } + 75% { + /* third road turn */ + left: calc(-30px * 0.15); + } + 100% { + /* vanishes into distance */ + left: calc(-30px * 0.02); + } +} + +@keyframes marchingPersonLeft4 { + 0% { + left: 60px; + } + 15% { + /* approaches road */ + left: calc(60px * 0.95); + } + 25% { + /* first road turn */ + left: calc(60px * 0.8); + } + 45% { + /* second road turn */ + left: calc(60px * 0.65); + } + 75% { + /* third road turn */ + left: calc(60px * 0.15); + } + 100% { + /* vanishes into distance */ + left: calc(60px * 0.02); + } +} + +@keyframes marchingPersonSkew { + 0%, + 100% { + transform: skew(0deg, 0deg); + } + 25% { + transform: skew(5deg, -5deg); + } + 50% { + transform: skew(0deg, 0deg); + } + 75% { + transform: skew(-5deg, 5deg); + } +} diff --git a/deploy_contract.sh b/deploy_contract.sh index 1d29a44..8dc4b9a 100755 --- a/deploy_contract.sh +++ b/deploy_contract.sh @@ -1,3 +1,4 @@ #!/bin/sh cast rpc anvil_setBalance 0x3295CCA2d922c637d35b258fc6c9C7e471803b45 0xDE0B6B3A7640000 --rpc-url http://127.0.0.1:8545 forge script script/RaidGeld.s.sol:RaidGeldScript --rpc-url 127.0.0.1:8545 --broadcast --private-key $DEV_PRIVATE_KEY +cast rpc anvil_mine