Compare commits
4 Commits
684d752b08
...
cabfc5e758
| Author | SHA1 | Date | |
|---|---|---|---|
| cabfc5e758 | |||
| 56e4b5ce26 | |||
| 294052070a | |||
| 20fa42cfca |
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Idle game & shitcoin advanture dedicated to cohort VII of Raid Guild.
|
Idle game & shitcoin advanture dedicated to cohort VII of Raid Guild.
|
||||||
|
|
||||||
## Set up
|
## Set up for local DEV
|
||||||
|
|
||||||
### 1. Run `anvil` to setup local RPC
|
### 1. Run `anvil` to setup local RPC
|
||||||
|
|
||||||
@ -14,7 +14,11 @@ Either use `./deploy_contract.sh` script (!! change contract values and set priv
|
|||||||
|
|
||||||
Move to `app` dir, install deps via `npm install` and run `npm run dev` to start the dev server.
|
Move to `app` dir, install deps via `npm install` and run `npm run dev` to start the dev server.
|
||||||
|
|
||||||
#### 3. 1. Point Metamask to Anvil network for local dev
|
#### 3. 1. Run `cast rpc anvil_mine`
|
||||||
|
|
||||||
|
This is so time gets set on the local chain, otherwise you will start at 0 time and first mint will give you bajillion GELD.
|
||||||
|
|
||||||
|
#### 3. 2. Point Metamask to Anvil network for local dev
|
||||||
|
|
||||||
### 4. Local development requires mining blocks by hand
|
### 4. Local development requires mining blocks by hand
|
||||||
|
|
||||||
|
|||||||
BIN
app/public/roles/druid2.png
Normal file
BIN
app/public/roles/druid2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
app/public/roles/ranger2.png
Normal file
BIN
app/public/roles/ranger2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
app/public/roles/scribe2.png
Normal file
BIN
app/public/roles/scribe2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.8 KiB |
BIN
app/public/roles/warrior2.png
Normal file
BIN
app/public/roles/warrior2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
25
app/src/components/Army.tsx
Normal file
25
app/src/components/Army.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { usePlayer } from '../providers/PlayerProvider';
|
||||||
|
import styles from '../styles/Army.module.css';
|
||||||
|
|
||||||
|
const Army = () => {
|
||||||
|
|
||||||
|
const { army, addUnit } = usePlayer()
|
||||||
|
|
||||||
|
return <div className="styles.armyGathering">
|
||||||
|
<div className={`${styles.tavern_keeper} ${styles.person} ${styles.static}`} />
|
||||||
|
<div onClick={() => addUnit(0)} className={`${styles.scribe} ${styles.person} ${styles.moloch_denier} ${styles.static}`}>
|
||||||
|
<div className={styles.supply}>Moloch denier: {army?.moloch_denier.level}</div>
|
||||||
|
</div>
|
||||||
|
<div onClick={() => addUnit(1)} className={`${styles.druid} ${styles.person} ${styles.apprentice} ${styles.static}`} >
|
||||||
|
<div className={styles.supply}>Apprentice: {army?.apprentice.level}</div>
|
||||||
|
</div>
|
||||||
|
<div onClick={() => addUnit(2)} className={`${styles.ranger} ${styles.person} ${styles.anointed} ${styles.static}`} >
|
||||||
|
<div className={styles.supply}>Anointed: {army?.anointed.level}</div>
|
||||||
|
</div>
|
||||||
|
<div onClick={() => addUnit(3)} className={`${styles.warrior} ${styles.person} ${styles.champion} ${styles.static}`} >
|
||||||
|
<div className={styles.supply}>Champion: {army?.champion.level}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Army
|
||||||
75
app/src/components/Counter.tsx
Normal file
75
app/src/components/Counter.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { useEffect, useReducer, useRef } from "react";
|
||||||
|
import { usePlayer } from "../providers/PlayerProvider"
|
||||||
|
import styles from "../styles/Header.module.css"
|
||||||
|
|
||||||
|
const calculateBalance = (balance: bigint, perSecond: bigint, lastRaidedAt: bigint) => {
|
||||||
|
// convert to milliseconds trick so we get a more smooth counter
|
||||||
|
const millisecondsSinceLastRaid =
|
||||||
|
(new Date()).getTime() - parseInt(lastRaidedAt.toString()) * 1000;
|
||||||
|
return (
|
||||||
|
balance +
|
||||||
|
(BigInt(millisecondsSinceLastRaid) * (perSecond * BigInt(10000) /* decimals */)
|
||||||
|
/ BigInt(1000) /* deduct milliseconds*/))
|
||||||
|
}
|
||||||
|
|
||||||
|
const toReadable = (value: bigint) => {
|
||||||
|
value = value / BigInt(10000);
|
||||||
|
const suffixes = [
|
||||||
|
{ value: BigInt('1000'), suffix: 'thousand' },
|
||||||
|
{ value: BigInt('1000000'), suffix: 'million' },
|
||||||
|
{ value: BigInt('1000000000'), suffix: 'billion' },
|
||||||
|
{ value: BigInt('1000000000000'), suffix: 'trillion' },
|
||||||
|
{ value: BigInt('1000000000000000'), suffix: 'quadrillion' },
|
||||||
|
{ value: BigInt('1000000000000000000'), suffix: 'quintillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000'), suffix: 'sextillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000'), suffix: 'septillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000'), suffix: 'octillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000000'), suffix: 'nonillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000000000'), suffix: 'decillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000000000000'), suffix: 'undecillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000000000000000'), suffix: 'duodecillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000000000000000000'), suffix: 'tredecillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000000000000000000000'), suffix: 'quattuordecillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000000000000000000000000'), suffix: 'quindecillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000000000000000000000000000'), suffix: 'sexdecillion' },
|
||||||
|
{ value: BigInt('1000000000000000000000000000000000000000000000000000000'), suffix: 'septendecillion' },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < suffixes.length; i++) {
|
||||||
|
if (value < suffixes[i].value) {
|
||||||
|
if (i == 0) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
const divided = value / suffixes[i - 1].value;
|
||||||
|
const remainder = value % suffixes[i - 1].value;
|
||||||
|
return `${divided.toString()}.${remainder.toString()} ${suffixes[i - 1].suffix}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Counter = () => {
|
||||||
|
const { balance, army, player } = usePlayer();
|
||||||
|
const [, render] = useReducer(p => !p, false);
|
||||||
|
const balanceCount = useRef(balance.toString() ?? "0")
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const tickInterval = setInterval(() => {
|
||||||
|
balanceCount.current = toReadable(calculateBalance(
|
||||||
|
balance,
|
||||||
|
army?.profit_per_second ?? BigInt(0),
|
||||||
|
player?.last_raided_at ?? BigInt(0)
|
||||||
|
)).toString();
|
||||||
|
render();
|
||||||
|
}, 100);
|
||||||
|
return () => clearInterval(tickInterval)
|
||||||
|
}, [balance, army?.profit_per_second, player?.last_raided_at])
|
||||||
|
|
||||||
|
return <p className={styles.counter}>
|
||||||
|
{balanceCount.current} GELD
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Counter
|
||||||
@ -1,21 +1,13 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
|
import React, { useCallback, useMemo } from "react"
|
||||||
import styles from "../styles/Header.module.css"
|
import styles from "../styles/Header.module.css"
|
||||||
import { usePlayer } from "../providers/PlayerProvider";
|
import { usePlayer } from "../providers/PlayerProvider";
|
||||||
import { useAccount } from 'wagmi';
|
import { useAccount } from 'wagmi';
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { formatUnits } from "viem";
|
import Counter from "./Counter";
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const { isConnected } = useAccount();
|
const { isConnected } = useAccount();
|
||||||
const { isRegistered, register, balance } = usePlayer();
|
const { isRegistered, register, army } = usePlayer();
|
||||||
const [count, setCount] = useState("0")
|
|
||||||
const [perSecond, setPerSecond] = useState("0")
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (balance != null) {
|
|
||||||
setCount(formatUnits(balance, 4))
|
|
||||||
}
|
|
||||||
}, [balance])
|
|
||||||
|
|
||||||
const title = useMemo(() => {
|
const title = useMemo(() => {
|
||||||
return isRegistered ? `SLAY THE MOLOCH` :
|
return isRegistered ? `SLAY THE MOLOCH` :
|
||||||
@ -25,17 +17,17 @@ const Header = () => {
|
|||||||
|
|
||||||
const subtitle = useMemo(() => {
|
const subtitle = useMemo(() => {
|
||||||
if (isRegistered) {
|
if (isRegistered) {
|
||||||
return <p className={styles.counter}>{count} GELD</p>
|
return <Counter />
|
||||||
} else {
|
} else {
|
||||||
return <p className={styles.counter}>SLAY THE MOLOCH</p>
|
return <p className={styles.counter}>SLAY THE MOLOCH</p>
|
||||||
}
|
}
|
||||||
}, [isRegistered, count])
|
}, [isRegistered])
|
||||||
|
|
||||||
const perSecondParagraph = useMemo(() => {
|
const perSecondParagraph = useMemo(() => {
|
||||||
return (isRegistered) ?
|
return (isRegistered) ?
|
||||||
<p className={styles.counter_per_seconds}>per second: {perSecond}</p>
|
<p className={styles.counter_per_seconds}>per second: {army?.profit_per_second.toString()}</p>
|
||||||
: null
|
: null
|
||||||
}, [isRegistered, perSecond])
|
}, [isRegistered, army?.profit_per_second])
|
||||||
|
|
||||||
const onRegister = useCallback(() => {
|
const onRegister = useCallback(() => {
|
||||||
if (isRegistered) return
|
if (isRegistered) return
|
||||||
@ -43,7 +35,6 @@ const Header = () => {
|
|||||||
}, [isRegistered, register])
|
}, [isRegistered, register])
|
||||||
|
|
||||||
return <header onClick={onRegister} className={styles.header}>
|
return <header onClick={onRegister} className={styles.header}>
|
||||||
{count.current} {balance}
|
|
||||||
<h1 className={styles.title}>{title}</h1>
|
<h1 className={styles.title}>{title}</h1>
|
||||||
{subtitle}
|
{subtitle}
|
||||||
{perSecondParagraph}
|
{perSecondParagraph}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import styles from '../styles/Background.module.css';
|
import styles from '../styles/Background.module.css';
|
||||||
import Tower from "./Tower";
|
import Tower from "./Tower";
|
||||||
|
import Army from "./Army";
|
||||||
|
|
||||||
const Background = () => {
|
const Scene = () => {
|
||||||
return <div className={styles.frame}>
|
return <div className={styles.frame}>
|
||||||
<div className={`${styles.air} ${styles.background_asset}`} />
|
<div className={`${styles.air} ${styles.background_asset}`} />
|
||||||
<div className={`${styles.clouds_small} ${styles.background_asset}`} />
|
<div className={`${styles.clouds_small} ${styles.background_asset}`} />
|
||||||
@ -10,9 +11,9 @@ const Background = () => {
|
|||||||
<Tower />
|
<Tower />
|
||||||
<div className={`${styles.mountains} ${styles.background_asset}`} />
|
<div className={`${styles.mountains} ${styles.background_asset}`} />
|
||||||
<div className={`${styles.village} ${styles.background_asset}`} />
|
<div className={`${styles.village} ${styles.background_asset}`} />
|
||||||
<div className={`${styles.tavern_keeper} ${styles.person} ${styles.static_keeper}`} />
|
|
||||||
<div className={`${styles.bonfire} ${styles.background_asset}`} />
|
<div className={`${styles.bonfire} ${styles.background_asset}`} />
|
||||||
|
<Army />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Background
|
export default Scene
|
||||||
@ -1,9 +1,33 @@
|
|||||||
|
import { useEffect, useReducer, useRef } from 'react';
|
||||||
import { usePlayer } from '../providers/PlayerProvider';
|
import { usePlayer } from '../providers/PlayerProvider';
|
||||||
import styles from '../styles/Background.module.css';
|
import styles from '../styles/Background.module.css';
|
||||||
|
|
||||||
|
const onCooldown = (lastRaidedAt: bigint) => (
|
||||||
|
((new Date()).getTime()
|
||||||
|
- (parseInt(lastRaidedAt.toString()) * 1000 /* convert block time to seconds */))
|
||||||
|
/ 1000 /* convert milliseconds back to seconds*/
|
||||||
|
) <= 15
|
||||||
|
|
||||||
|
const emptyFn = () => { }
|
||||||
|
|
||||||
const Tower = () => {
|
const Tower = () => {
|
||||||
const { raid } = usePlayer();
|
const { raid, player } = usePlayer();
|
||||||
return <div onClick={raid} className={`${styles.tower} ${styles.background_asset}`} />
|
const isOnCooldown = useRef(false);
|
||||||
|
const [, render] = useReducer(p => !p, false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const checkCooldownInterval = setInterval(() => {
|
||||||
|
isOnCooldown.current = onCooldown(player?.last_raided_at ?? BigInt(0))
|
||||||
|
render()
|
||||||
|
}, 1000);
|
||||||
|
return () => clearInterval(checkCooldownInterval)
|
||||||
|
}, [player?.last_raided_at])
|
||||||
|
|
||||||
|
return <div onClick={isOnCooldown.current ? emptyFn : raid} className={`
|
||||||
|
${styles.tower}
|
||||||
|
${styles.background_asset}
|
||||||
|
${isOnCooldown.current ? styles.cooldown : ""}
|
||||||
|
`} />
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Tower
|
export default Tower
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import type { NextPage } from 'next';
|
|||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import styles from '../styles/Home.module.css';
|
import styles from '../styles/Home.module.css';
|
||||||
import Header from '../components/Header';
|
import Header from '../components/Header';
|
||||||
import Background from '../components/Background';
|
import Scene from '../components/Scene';
|
||||||
|
|
||||||
const Home: NextPage = () => {
|
const Home: NextPage = () => {
|
||||||
return (
|
return (
|
||||||
@ -22,7 +22,7 @@ const Home: NextPage = () => {
|
|||||||
<ConnectButton />
|
<ConnectButton />
|
||||||
</div>
|
</div>
|
||||||
<Header />
|
<Header />
|
||||||
<Background />
|
<Scene />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
|
|||||||
@ -6,26 +6,46 @@ import { parseEther } from 'viem'
|
|||||||
const contractAddress = "0xbd06B0878888bf4c6895704fa603a5ADf7e65c66"
|
const contractAddress = "0xbd06B0878888bf4c6895704fa603a5ADf7e65c66"
|
||||||
const abi = contractAbi.abi
|
const abi = contractAbi.abi
|
||||||
|
|
||||||
|
export interface Player {
|
||||||
|
created_at: bigint,
|
||||||
|
last_raided_at: bigint,
|
||||||
|
total_minted: bigint
|
||||||
|
}
|
||||||
|
export interface Army {
|
||||||
|
anointed: { level: number }
|
||||||
|
apprentice: { level: number }
|
||||||
|
champion: { level: number }
|
||||||
|
moloch_denier: { level: number }
|
||||||
|
profit_per_second: bigint
|
||||||
|
}
|
||||||
|
|
||||||
export interface PlayerContextType {
|
export interface PlayerContextType {
|
||||||
isRegistered: boolean,
|
isRegistered: boolean,
|
||||||
user: null | string,
|
player: null | Player,
|
||||||
|
army: null | Army,
|
||||||
balance: bigint,
|
balance: bigint,
|
||||||
register: () => void;
|
register: () => void,
|
||||||
raid: () => void
|
raid: () => void,
|
||||||
|
addUnit: (unit: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PlayerContext = createContext<PlayerContextType>({
|
const PlayerContext = createContext<PlayerContextType>({
|
||||||
isRegistered: false,
|
isRegistered: false,
|
||||||
user: null,
|
player: null,
|
||||||
|
army: null,
|
||||||
balance: BigInt(0),
|
balance: BigInt(0),
|
||||||
register: () => { },
|
register: () => { },
|
||||||
raid: () => { },
|
raid: () => { },
|
||||||
|
addUnit: () => { }
|
||||||
});
|
});
|
||||||
|
|
||||||
const PlayerProvider = ({ children }: { children: ReactNode }) => {
|
const PlayerProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const { address, isConnected } = useAccount();
|
const { address, isConnected } = useAccount();
|
||||||
const { writeContract, error } = useWriteContract();
|
const { writeContract, error } = useWriteContract();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.warn(error)
|
||||||
|
}, [error])
|
||||||
|
|
||||||
const { data: isRegistered } = useReadContract({
|
const { data: isRegistered } = useReadContract({
|
||||||
address: contractAddress,
|
address: contractAddress,
|
||||||
@ -49,7 +69,7 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: playerData } = useReadContract({
|
const { data: player } = useReadContract({
|
||||||
address: contractAddress,
|
address: contractAddress,
|
||||||
abi,
|
abi,
|
||||||
functionName: 'getPlayer',
|
functionName: 'getPlayer',
|
||||||
@ -60,11 +80,16 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(playerData)
|
const { data: army } = useReadContract({
|
||||||
|
address: contractAddress,
|
||||||
useEffect(() => {
|
abi,
|
||||||
console.warn(error, playerData)
|
functionName: 'getArmy',
|
||||||
}, [error])
|
args: [address],
|
||||||
|
query: {
|
||||||
|
enabled: isConnected,
|
||||||
|
refetchInterval: 15
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const register = useCallback(() => {
|
const register = useCallback(() => {
|
||||||
writeContract({
|
writeContract({
|
||||||
@ -83,13 +108,24 @@ const PlayerProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
})
|
})
|
||||||
}, [writeContract])
|
}, [writeContract])
|
||||||
|
|
||||||
|
const addUnit = useCallback((unit: number) => {
|
||||||
|
writeContract({
|
||||||
|
abi,
|
||||||
|
address: contractAddress,
|
||||||
|
functionName: 'addUnit',
|
||||||
|
args: [unit, 1]
|
||||||
|
})
|
||||||
|
}, [writeContract])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PlayerContext.Provider value={{
|
<PlayerContext.Provider value={{
|
||||||
isRegistered: isRegistered as boolean,
|
isRegistered: isRegistered as boolean,
|
||||||
user: null,
|
player: player as Player,
|
||||||
|
army: army as Army,
|
||||||
balance: balance as bigint,
|
balance: balance as bigint,
|
||||||
register,
|
register,
|
||||||
raid
|
raid,
|
||||||
|
addUnit
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</PlayerContext.Provider>
|
</PlayerContext.Provider>
|
||||||
|
|||||||
124
app/src/styles/Army.module.css
Normal file
124
app/src/styles/Army.module.css
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
.army-gathering {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 22px;
|
||||||
|
left: 22px;
|
||||||
|
right: 22px;
|
||||||
|
}
|
||||||
|
.person {
|
||||||
|
position: absolute;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: bottom center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
transform-origin: bottom center;
|
||||||
|
}
|
||||||
|
.tavern_keeper {
|
||||||
|
background-image: url("/roles/tavern-keeper.svg");
|
||||||
|
}
|
||||||
|
.alchemist {
|
||||||
|
background-image: url("/roles/alchemist.svg");
|
||||||
|
}
|
||||||
|
.archer {
|
||||||
|
background-image: url("/roles/archer.svg");
|
||||||
|
}
|
||||||
|
.cleric {
|
||||||
|
background-image: url("/roles/cleric.svg");
|
||||||
|
}
|
||||||
|
.druid {
|
||||||
|
background-image: url("/roles/druid.svg");
|
||||||
|
}
|
||||||
|
.dwarf {
|
||||||
|
background-image: url("/roles/dwarf.svg");
|
||||||
|
}
|
||||||
|
.monk {
|
||||||
|
background-image: url("/roles/monk.svg");
|
||||||
|
}
|
||||||
|
.necromancer {
|
||||||
|
background-image: url("/roles/necromancer.svg");
|
||||||
|
}
|
||||||
|
.paladin {
|
||||||
|
background-image: url("/roles/paladin.svg");
|
||||||
|
}
|
||||||
|
.ranger {
|
||||||
|
background-image: url("/roles/ranger.svg");
|
||||||
|
}
|
||||||
|
.rogue {
|
||||||
|
background-image: url("/roles/rogue.svg");
|
||||||
|
}
|
||||||
|
.scribe {
|
||||||
|
background-image: url("/roles/scribe.svg");
|
||||||
|
}
|
||||||
|
.warrior {
|
||||||
|
background-image: url("/roles/warrior.svg");
|
||||||
|
}
|
||||||
|
.wizard {
|
||||||
|
background-image: url("/roles/wizard.svg");
|
||||||
|
}
|
||||||
|
.healer {
|
||||||
|
background-image: url("/roles/healer.svg");
|
||||||
|
}
|
||||||
|
.hunter {
|
||||||
|
background-image: url("/roles/hunter.svg");
|
||||||
|
}
|
||||||
|
.static {
|
||||||
|
width: 110px;
|
||||||
|
height: 110px;
|
||||||
|
transition: all 100ms cubic-bezier(0.265, 1.4, 0.68, 1.65);
|
||||||
|
&:not(.locked) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.08, 1.08);
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
transform: scale(1.2, 1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.static.tavern_keeper {
|
||||||
|
right: 130px;
|
||||||
|
bottom: 160px;
|
||||||
|
width: 90px;
|
||||||
|
height: 90px;
|
||||||
|
}
|
||||||
|
.static.moloch_denier {
|
||||||
|
left: calc(20% - 60px);
|
||||||
|
bottom: 70px;
|
||||||
|
background-image: url("/roles/scribe2.png");
|
||||||
|
}
|
||||||
|
.static.apprentice {
|
||||||
|
left: calc(36% - 60px);
|
||||||
|
background-image: url("/roles/druid2.png");
|
||||||
|
bottom: 72px;
|
||||||
|
}
|
||||||
|
.static.anointed {
|
||||||
|
left: calc(50% - 60px);
|
||||||
|
bottom: 64px;
|
||||||
|
background-image: url("/roles/ranger2.png");
|
||||||
|
}
|
||||||
|
.static.champion {
|
||||||
|
left: calc(64% - 60px);
|
||||||
|
bottom: 66px;
|
||||||
|
background-image: url("/roles/warrior2.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
.moloch_denier {
|
||||||
|
filter: sepia(0.1);
|
||||||
|
}
|
||||||
|
.apprentice {
|
||||||
|
}
|
||||||
|
.anointed {
|
||||||
|
filter: saturate(1.1);
|
||||||
|
}
|
||||||
|
.champion {
|
||||||
|
filter: saturate(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.supply {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -40px;
|
||||||
|
background: rgba(0, 0, 0, 0.89);
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
@ -50,7 +50,8 @@
|
|||||||
height: 240px;
|
height: 240px;
|
||||||
top: 200px;
|
top: 200px;
|
||||||
animation: thunder_hue_hard 12s linear infinite;
|
animation: thunder_hue_hard 12s linear infinite;
|
||||||
transition: all ease-in-out 0.05s;
|
transition: all 0.1s cubic-bezier(0.265, 1.4, 0.68, 1.65);
|
||||||
|
transform-origin: bottom center;
|
||||||
&:hover {
|
&:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transform: scale(1.05, 1.1);
|
transform: scale(1.05, 1.1);
|
||||||
@ -63,6 +64,21 @@
|
|||||||
&:active {
|
&:active {
|
||||||
transform: scale(1.1, 1.22);
|
transform: scale(1.1, 1.22);
|
||||||
}
|
}
|
||||||
|
&.cooldown {
|
||||||
|
transition: all 1s cubic-bezier(0.265, 1.4, 0.68, 1.65);
|
||||||
|
transform: scale(1.1, 1.22);
|
||||||
|
&::after {
|
||||||
|
content: "RAID IN PROGRESS";
|
||||||
|
color: var(--hover-color);
|
||||||
|
top: calc(50% - 15px);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-align: center;
|
||||||
|
animation: excited 0.5s infinite linear;
|
||||||
|
text-shadow: #000 1px 1px 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.tower::after {
|
.tower::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -94,67 +110,6 @@
|
|||||||
bonfire 12s linear infinite,
|
bonfire 12s linear infinite,
|
||||||
bonfire_skew 5s infinite linear;
|
bonfire_skew 5s infinite linear;
|
||||||
}
|
}
|
||||||
.person {
|
|
||||||
position: absolute;
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
background-size: contain;
|
|
||||||
background-position: bottom center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
.tavern_keeper {
|
|
||||||
background-image: url("/roles/tavern-keeper.svg");
|
|
||||||
}
|
|
||||||
.alchemist {
|
|
||||||
background-image: url("/roles/alchemist.svg");
|
|
||||||
}
|
|
||||||
.archer {
|
|
||||||
background-image: url("/roles/archer.svg");
|
|
||||||
}
|
|
||||||
.cleric {
|
|
||||||
background-image: url("/roles/cleric.svg");
|
|
||||||
}
|
|
||||||
.druid {
|
|
||||||
background-image: url("/roles/druid.svg");
|
|
||||||
}
|
|
||||||
.dwarf {
|
|
||||||
background-image: url("/roles/dwarf.svg");
|
|
||||||
}
|
|
||||||
.monk {
|
|
||||||
background-image: url("/roles/monk.svg");
|
|
||||||
}
|
|
||||||
.necromancer {
|
|
||||||
background-image: url("/roles/necromancer.svg");
|
|
||||||
}
|
|
||||||
.paladin {
|
|
||||||
background-image: url("/roles/paladin.svg");
|
|
||||||
}
|
|
||||||
.ranger {
|
|
||||||
background-image: url("/roles/ranger.svg");
|
|
||||||
}
|
|
||||||
.rogue {
|
|
||||||
background-image: url("/roles/rogue.svg");
|
|
||||||
}
|
|
||||||
.scribe {
|
|
||||||
background-image: url("/roles/scribe.svg");
|
|
||||||
}
|
|
||||||
.warrior {
|
|
||||||
background-image: url("/roles/warrior.svg");
|
|
||||||
}
|
|
||||||
.wizard {
|
|
||||||
background-image: url("/roles/wizard.svg");
|
|
||||||
}
|
|
||||||
.healer {
|
|
||||||
background-image: url("/roles/healer.svg");
|
|
||||||
}
|
|
||||||
.hunter {
|
|
||||||
background-image: url("/roles/hunter.svg");
|
|
||||||
}
|
|
||||||
.static_keeper {
|
|
||||||
right: 130px;
|
|
||||||
bottom: 160px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes scrollBackground {
|
@keyframes scrollBackground {
|
||||||
0% {
|
0% {
|
||||||
background-position: 0 0;
|
background-position: 0 0;
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin: 0.5rem 0 0.2rem;
|
margin: 0.5rem 0 0.2rem;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
|
color: var(--hover-color);
|
||||||
}
|
}
|
||||||
.counter_per_seconds {
|
.counter_per_seconds {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ struct Raider {
|
|||||||
struct Army {
|
struct Army {
|
||||||
Raider moloch_denier;
|
Raider moloch_denier;
|
||||||
Raider apprentice;
|
Raider apprentice;
|
||||||
Raider annointed;
|
Raider anointed;
|
||||||
Raider champion;
|
Raider champion;
|
||||||
uint256 profit_per_second;
|
uint256 profit_per_second;
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ contract RaidGeld is ERC20, Ownable {
|
|||||||
armies[msg.sender] = Army({
|
armies[msg.sender] = Army({
|
||||||
moloch_denier: Raider({level: 0}),
|
moloch_denier: Raider({level: 0}),
|
||||||
apprentice: Raider({level: 0}),
|
apprentice: Raider({level: 0}),
|
||||||
annointed: Raider({level: 0}),
|
anointed: Raider({level: 0}),
|
||||||
champion: Raider({level: 0}),
|
champion: Raider({level: 0}),
|
||||||
profit_per_second: 0
|
profit_per_second: 0
|
||||||
});
|
});
|
||||||
@ -111,8 +111,8 @@ contract RaidGeld is ERC20, Ownable {
|
|||||||
// apprentice
|
// apprentice
|
||||||
currentLevel = army.apprentice.level;
|
currentLevel = army.apprentice.level;
|
||||||
} else if (unit == 2) {
|
} else if (unit == 2) {
|
||||||
// annointed
|
// anointed
|
||||||
currentLevel = army.annointed.level;
|
currentLevel = army.anointed.level;
|
||||||
} else if (unit == 3) {
|
} else if (unit == 3) {
|
||||||
// champion
|
// champion
|
||||||
currentLevel = army.champion.level;
|
currentLevel = army.champion.level;
|
||||||
@ -130,8 +130,8 @@ contract RaidGeld is ERC20, Ownable {
|
|||||||
// apprentice
|
// apprentice
|
||||||
army.apprentice.level += n_units;
|
army.apprentice.level += n_units;
|
||||||
} else if (unit == 2) {
|
} else if (unit == 2) {
|
||||||
// annointed
|
// anointed
|
||||||
army.annointed.level += n_units;
|
army.anointed.level += n_units;
|
||||||
} else if (unit == 3) {
|
} else if (unit == 3) {
|
||||||
// champion
|
// champion
|
||||||
army.champion.level += n_units;
|
army.champion.level += n_units;
|
||||||
|
|||||||
@ -24,8 +24,8 @@ library RaidGeldUtils {
|
|||||||
// Each next unit scales progressivelly better
|
// Each next unit scales progressivelly better
|
||||||
uint256 moloch_denier_profit = army.moloch_denier.level;
|
uint256 moloch_denier_profit = army.moloch_denier.level;
|
||||||
uint256 apprentice_profit = army.apprentice.level * 61 / 10;
|
uint256 apprentice_profit = army.apprentice.level * 61 / 10;
|
||||||
uint256 annointed_profit = army.annointed.level * 6 * 64 / 10;
|
uint256 anointed_profit = army.anointed.level * 6 * 64 / 10;
|
||||||
uint256 champion_profit = army.champion.level * 61 / 10 * 64 / 10 * 67 / 10;
|
uint256 champion_profit = army.champion.level * 61 / 10 * 64 / 10 * 67 / 10;
|
||||||
return moloch_denier_profit + apprentice_profit + annointed_profit + champion_profit;
|
return moloch_denier_profit + apprentice_profit + anointed_profit + champion_profit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,7 +61,7 @@ contract raid_geldTest is Test {
|
|||||||
|
|
||||||
assertEq(army.moloch_denier.level, 0);
|
assertEq(army.moloch_denier.level, 0);
|
||||||
assertEq(army.apprentice.level, 0);
|
assertEq(army.apprentice.level, 0);
|
||||||
assertEq(army.annointed.level, 0);
|
assertEq(army.anointed.level, 0);
|
||||||
assertEq(army.champion.level, 0);
|
assertEq(army.champion.level, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ contract raid_geldTest is Test {
|
|||||||
|
|
||||||
// Add 2 units
|
// Add 2 units
|
||||||
Army memory army = raid_geld.getArmy(player1);
|
Army memory army = raid_geld.getArmy(player1);
|
||||||
uint256 unit_level = army.annointed.level;
|
uint256 unit_level = army.moloch_denier.level;
|
||||||
uint256 balance = raid_geld.balanceOf(player1);
|
uint256 balance = raid_geld.balanceOf(player1);
|
||||||
uint256 income_per_sec = army.profit_per_second;
|
uint256 income_per_sec = army.profit_per_second;
|
||||||
raid_geld.addUnit(0, 2);
|
raid_geld.addUnit(0, 2);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user