├── .gitignore ├── .travis.yml ├── README.md ├── package-lock.json ├── package.json ├── public ├── closed-eyes-1@2x.png ├── eyes-10@2x.png ├── eyes-11@2x.png ├── eyes-1@2x.png ├── eyes-2@2x.png ├── eyes-3@2x.png ├── eyes-4@2x.png ├── eyes-5@2x.png ├── eyes-6@2x.png ├── eyes-7@2x.png ├── eyes-8@2x.png ├── eyes-9@2x.png ├── favicon.ico ├── grenade-1@2x.png ├── hand-2-1@2x.png ├── hand1-1@2x.png ├── head-1@2x.png ├── head-2@2x.png ├── head-3@2x.png ├── head-4@2x.png ├── head-5@2x.png ├── head-6@2x.png ├── head-7@2x.png ├── head-8@2x.png ├── index.html ├── left-feet-1@2x.png ├── left-forearm-1@2x.png ├── left-leg-1@2x.png ├── left-thigh-1@2x.png ├── left-upper-arm-1@2x.png ├── logo192.png ├── logo512.png ├── manifest.json ├── mouth-1@2x.png ├── open-mouth-1@2x.png ├── right-feet-1@2x.png ├── right-forearm-1@2x.png ├── right-leg-1@2x.png ├── right-thigh-1@2x.png ├── right-upper-arm-1@2x.png ├── robots.txt ├── shirt-1@2x.png ├── shirt-2@2x.png ├── shirt-3@2x.png ├── shirt-4@2x.png ├── shirt-5@2x.png ├── shirt-6@2x.png └── torso-1@2x.png ├── src ├── App.js ├── ContractAddress.json ├── ContractAdmin.js ├── MyWeb3.js ├── MyZombie.js ├── Page.js ├── ZombieArmy.js ├── ZombieAttack.js ├── ZombieCard.js ├── ZombieCore.json ├── ZombieDetail.js ├── ZombieMarket.js ├── ZombiePreview.js ├── ZombieSimulator.js ├── ZombieToggler.js ├── index.js └── static │ ├── App.css │ ├── Btn_Register_Normal.png │ ├── Btn_Register_Over.png │ ├── Index.css │ ├── ZombiePreview.css │ ├── bg.png │ ├── catlegs.png │ ├── get_start_btn.png │ ├── homecard@2x.png │ ├── index_bg.png │ ├── learn_more_btn.png │ ├── lesson-complete-bg.png │ ├── lesson_number_completed.png │ ├── lesson_panel_complete.png │ ├── start_btn.png │ ├── tester-bg@2x.png │ ├── walls.jpg │ └── zombierun.png └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | # production 11 | /build 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | webpack.config.js 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | install: yarn install 5 | script: 6 | - yarn build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 区块链收藏游戏:僵尸之谜 2 | [![Build Status](https://travis-ci.org/Fankouzu/my-crypto-zombie.svg?branch=master)](https://travis-ci.org/Fankouzu/my-crypto-zombie) 3 | ## Demo 4 | https://fankouzu.github.io/my-crypto-zombie/
5 | `需要使用Chrome浏览器和Metamask钱包插件` 6 | 7 | 8 | ## 背景 9 | - 这是一个真正的区块链去中心化游戏 10 | - 下载运行之后就可以连接到以太坊智能合约 11 | - 所有人的游戏数据都保存在区块链上 12 | - 无论是谁,无论在何地运行的游戏都可以连接到相同的数据 13 | 14 | 这个程序是根据 https://cryptozombies.io/ 这里提供的以太坊智能合约课程开发的。并使用了其中的游戏素材,素材版权归 https://cryptozombies.io/ 所有,本项目仅供学习智能合约使用。 15 | 16 | 17 | ## 安装 18 | 使用yarn安装更为有效
19 | `yarn install` 20 | ## 运行 21 | `yarn start` 22 | ## 使用说明 23 | - 这个项目是使用React开发的,运行之后在Chrome浏览器中打开地址 [http://localhost:3000](http://localhost:3000) 请确认你安装了[Metamask插件](https://metamask.io/)
24 | - 项目中已经在[这个文件](https://github.com/Fankouzu/my-crypto-zombie/blob/master/src/ContractAddress.json)中设置好了智能合约的地址,你也可以修改成你自己部署的智能合约合约地址 25 | 26 | ```json 27 | { 28 | "3":"0x1a3cA7AbE6370D33986b2D2aC6F1F9A656f87b4D", 29 | "4":"0xbca6885699Ee9ae9B2255538B5a3EfB3082bE5ac", 30 | "5":"0x6817c8475Ad33Aa86422160C3d1C673c453A76dE", 31 | "42":"0x6817c8475Ad33Aa86422160C3d1C673c453A76dE", 32 | "5777":"0x8b11Af05bdBB4848b59f2C3A5Bf3E2BB24c744fD" 33 | } 34 | ``` 35 | - 你可以在Metamask中切换你的以太坊网络,每个网络都有我部署的合约,不同的合约地址就像不同的数据库,拥有着不同的游戏数据,地址前面的数字是网络的ID,其中(1)主网我没有部署,(2)已经废弃,(5777)是本地的Ganache软件的模拟网络,需要安装Ganache 36 | ``` 37 | '1': Ethereum Main Network 38 | '2': Morden Test network 39 | '3': Ropsten Test Network 40 | '4': Rinkeby Test Network 41 | '5': Goerli Test Network 42 | '42': Kovan Test Network 43 | '5777': Ganache localhost:7545 44 | ``` 45 | ## 智能合约 46 | 学习智能合约开发,请看[我的B站视频](https://www.bilibili.com/video/av75230620)
47 | 48 | 本项目所使用的[智能合约源码在这里](https://github.com/Fankouzu/smart-contract/tree/master/Solidity%20Lesson%2004)
49 | 50 | ipfs上也有 ipfs://QmTYNvirSHmoHtjCbH77Zc64xNLFUpMq2JSjUmQeDgLvhY 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-crypto-zombie", 3 | "version": "0.1.0", 4 | "private": false, 5 | "license": "Apache-2.0", 6 | "homepage": "https://fankouzu.github.io/my-crypto-zombie", 7 | "dependencies": { 8 | "@testing-library/jest-dom": "^4.2.4", 9 | "@testing-library/react": "^9.3.2", 10 | "@testing-library/user-event": "^7.1.2", 11 | "gh-pages": "^2.1.1", 12 | "moment": "^2.24.0", 13 | "react": "^16.12.0", 14 | "react-dom": "^16.12.0", 15 | "react-redux": "^7.1.3", 16 | "react-router-dom": "^5.1.2", 17 | "react-scripts": "3.3.0", 18 | "serialize-javascript": "3.1.0", 19 | "web3": "^1.6.0" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject", 26 | "predeploy": "yarn build", 27 | "deploy": "gh-pages -d build" 28 | }, 29 | "eslintConfig": { 30 | "extends": "react-app" 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /public/closed-eyes-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/closed-eyes-1@2x.png -------------------------------------------------------------------------------- /public/eyes-10@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-10@2x.png -------------------------------------------------------------------------------- /public/eyes-11@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-11@2x.png -------------------------------------------------------------------------------- /public/eyes-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-1@2x.png -------------------------------------------------------------------------------- /public/eyes-2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-2@2x.png -------------------------------------------------------------------------------- /public/eyes-3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-3@2x.png -------------------------------------------------------------------------------- /public/eyes-4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-4@2x.png -------------------------------------------------------------------------------- /public/eyes-5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-5@2x.png -------------------------------------------------------------------------------- /public/eyes-6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-6@2x.png -------------------------------------------------------------------------------- /public/eyes-7@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-7@2x.png -------------------------------------------------------------------------------- /public/eyes-8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-8@2x.png -------------------------------------------------------------------------------- /public/eyes-9@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/eyes-9@2x.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/favicon.ico -------------------------------------------------------------------------------- /public/grenade-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/grenade-1@2x.png -------------------------------------------------------------------------------- /public/hand-2-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/hand-2-1@2x.png -------------------------------------------------------------------------------- /public/hand1-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/hand1-1@2x.png -------------------------------------------------------------------------------- /public/head-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/head-1@2x.png -------------------------------------------------------------------------------- /public/head-2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/head-2@2x.png -------------------------------------------------------------------------------- /public/head-3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/head-3@2x.png -------------------------------------------------------------------------------- /public/head-4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/head-4@2x.png -------------------------------------------------------------------------------- /public/head-5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/head-5@2x.png -------------------------------------------------------------------------------- /public/head-6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/head-6@2x.png -------------------------------------------------------------------------------- /public/head-7@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/head-7@2x.png -------------------------------------------------------------------------------- /public/head-8@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/head-8@2x.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 僵尸之谜 || 迷恋尸 || 区块链收藏游戏 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /public/left-feet-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/left-feet-1@2x.png -------------------------------------------------------------------------------- /public/left-forearm-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/left-forearm-1@2x.png -------------------------------------------------------------------------------- /public/left-leg-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/left-leg-1@2x.png -------------------------------------------------------------------------------- /public/left-thigh-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/left-thigh-1@2x.png -------------------------------------------------------------------------------- /public/left-upper-arm-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/left-upper-arm-1@2x.png -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "./favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "./logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "./logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/mouth-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/mouth-1@2x.png -------------------------------------------------------------------------------- /public/open-mouth-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/open-mouth-1@2x.png -------------------------------------------------------------------------------- /public/right-feet-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/right-feet-1@2x.png -------------------------------------------------------------------------------- /public/right-forearm-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/right-forearm-1@2x.png -------------------------------------------------------------------------------- /public/right-leg-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/right-leg-1@2x.png -------------------------------------------------------------------------------- /public/right-thigh-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/right-thigh-1@2x.png -------------------------------------------------------------------------------- /public/right-upper-arm-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/right-upper-arm-1@2x.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /public/shirt-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/shirt-1@2x.png -------------------------------------------------------------------------------- /public/shirt-2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/shirt-2@2x.png -------------------------------------------------------------------------------- /public/shirt-3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/shirt-3@2x.png -------------------------------------------------------------------------------- /public/shirt-4@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/shirt-4@2x.png -------------------------------------------------------------------------------- /public/shirt-5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/shirt-5@2x.png -------------------------------------------------------------------------------- /public/shirt-6@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/shirt-6@2x.png -------------------------------------------------------------------------------- /public/torso-1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/public/torso-1@2x.png -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React,{Component,Fragment} from 'react' 2 | import './static/App.css' 3 | import Page from "./Page"; 4 | import { 5 | BrowserRouter as Router, 6 | Route, 7 | Link 8 | } from "react-router-dom" 9 | 10 | class App extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { AdminArea:()=>{return()} } 14 | } 15 | 16 | componentDidMount(){ 17 | let ethereum = window.ethereum 18 | if (typeof ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) { 19 | ethereum.on('accountsChanged', function (accounts) { 20 | console.log("accountsChanged:"+accounts) 21 | //window.location.reload() 22 | }) 23 | ethereum.on('chainChanged', function (chainId) { 24 | console.log("chainChanged:"+chainId) 25 | //window.location.reload() 26 | }) 27 | ethereum.on('networkChanged', function (networkVersion) { 28 | console.log("networkChanged:"+networkVersion) 29 | //window.location.reload() 30 | }) 31 | }else { 32 | alert('You have to install MetaMask !') 33 | } 34 | } 35 | 36 | render() { 37 | let AdminArea = this.state.AdminArea 38 | return ( 39 | 40 | 41 |
42 |
43 |
44 |
    45 |
  • 46 | 49 |
  • 50 |
  • 51 | 54 |
  • 55 |
  • 56 | 59 |
  • 60 |
  • 61 | 64 |
  • 65 | 66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | 74 |
75 |
76 |
77 |
78 |
79 | ); 80 | } 81 | } 82 | 83 | 84 | export default App 85 | -------------------------------------------------------------------------------- /src/ContractAddress.json: -------------------------------------------------------------------------------- 1 | { 2 | "3":"0x1a3cA7AbE6370D33986b2D2aC6F1F9A656f87b4D", 3 | "4":"0xbca6885699Ee9ae9B2255538B5a3EfB3082bE5ac", 4 | "5":"0x6817c8475Ad33Aa86422160C3d1C673c453A76dE", 5 | "42":"0x6817c8475Ad33Aa86422160C3d1C673c453A76dE", 6 | "5777":"0x8b11Af05bdBB4848b59f2C3A5Bf3E2BB24c744fD", 7 | "8285":"0xa14FcCa92Cb160284287dCe12803f2A1949A8E8B" 8 | } 9 | -------------------------------------------------------------------------------- /src/ContractAdmin.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import MyWeb3 from './MyWeb3' 3 | 4 | class ContractAdmin extends Component { 5 | constructor(props) { 6 | super(props) 7 | this.state = { 8 | contractAddress:'', 9 | contractOwner:'', 10 | ContractName:'', 11 | ContractSymbol:'', 12 | ContractBalance:'', 13 | attackVictoryProbability:70, 14 | levelUpFee:0, 15 | minPrice:0, 16 | tax:0, 17 | zombiePrice:0 18 | } 19 | this.inputAttackVictoryProbability = this.inputAttackVictoryProbability.bind(this) 20 | this.setAttackVictoryProbability = this.setAttackVictoryProbability.bind(this) 21 | this.inputLevelUpFee = this.inputLevelUpFee.bind(this) 22 | this.setLevelUpFee = this.setLevelUpFee.bind(this) 23 | this.inputMinPrice = this.inputMinPrice.bind(this) 24 | this.setMinPrice = this.setMinPrice.bind(this) 25 | this.inputTax = this.inputTax.bind(this) 26 | this.setTax = this.setTax.bind(this) 27 | this.inputZombiePrice = this.inputZombiePrice.bind(this) 28 | this.setZombiePrice = this.setZombiePrice.bind(this) 29 | this.withdraw = this.withdraw.bind(this) 30 | } 31 | 32 | componentDidMount(){ 33 | let that = this 34 | let ethereum = window.ethereum 35 | if (typeof ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) { 36 | MyWeb3.init().then(function(res){ 37 | // console.log(window.web3) 38 | // console.log(ethereum) 39 | // console.log(window.MyContract) 40 | MyWeb3.owner().then(function (contractOwner) { 41 | if(window.defaultAccount === contractOwner){ 42 | that.setState({contractOwner:contractOwner}) 43 | that.getContractName() 44 | that.getContractSymbol() 45 | that.checkBalance() 46 | that.levelUpFee() 47 | that.minPrice() 48 | that.tax() 49 | that.zombiePrice() 50 | that.setState({ 51 | contractAddress:window.MyContract._address 52 | }) 53 | } 54 | }) 55 | }) 56 | }else { 57 | alert('You have to install MetaMask !') 58 | } 59 | } 60 | getContractName(){ 61 | let that = this 62 | MyWeb3.name().then(function (name) { 63 | that.setState({ContractName:name}) 64 | }) 65 | } 66 | getContractSymbol(){ 67 | let that = this 68 | MyWeb3.symbol().then(function (symbol) { 69 | that.setState({ContractSymbol:symbol}) 70 | }) 71 | } 72 | checkBalance(){ 73 | let that = this 74 | MyWeb3.checkBalance().then(function (balance) { 75 | that.setState({ContractBalance:balance}) 76 | }) 77 | } 78 | withdraw(){ 79 | let that = this 80 | MyWeb3.withdraw().then(function (res) { 81 | that.checkBalance() 82 | }) 83 | } 84 | setAttackVictoryProbability(){ 85 | MyWeb3.setAttackVictoryProbability(this.state.attackVictoryProbability).then(function (res) { 86 | console.log(res) 87 | window.location.reload() 88 | }) 89 | } 90 | inputAttackVictoryProbability(event){ 91 | this.setState({ 92 | attackVictoryProbability:event.target.value 93 | }) 94 | } 95 | levelUpFee(){ 96 | let that = this 97 | MyWeb3.levelUpFee().then(function (levelUpFee) { 98 | that.setState({levelUpFee:levelUpFee}) 99 | }) 100 | } 101 | setLevelUpFee(){ 102 | MyWeb3.setLevelUpFee(this.state.levelUpFee).then(function (res) { 103 | console.log(res) 104 | window.location.reload() 105 | }) 106 | } 107 | inputLevelUpFee(event){ 108 | this.setState({ 109 | levelUpFee:event.target.value 110 | }) 111 | } 112 | minPrice(){ 113 | let that = this 114 | MyWeb3.minPrice().then(function (minPrice) { 115 | that.setState({minPrice:minPrice}) 116 | }) 117 | } 118 | setMinPrice(){ 119 | MyWeb3.setMinPrice(this.state.minPrice).then(function (res) { 120 | console.log(res) 121 | window.location.reload() 122 | }) 123 | } 124 | inputMinPrice(event){ 125 | this.setState({ 126 | minPrice:event.target.value 127 | }) 128 | } 129 | tax(){ 130 | let that = this 131 | MyWeb3.tax().then(function (tax) { 132 | that.setState({tax:tax}) 133 | }) 134 | } 135 | setTax(){ 136 | MyWeb3.setTax(this.state.tax).then(function (res) { 137 | console.log(res) 138 | window.location.reload() 139 | }) 140 | } 141 | inputTax(event){ 142 | this.setState({ 143 | tax:event.target.value 144 | }) 145 | } 146 | zombiePrice(){ 147 | let that = this 148 | MyWeb3.zombiePrice().then(function (zombiePrice) { 149 | that.setState({zombiePrice:zombiePrice}) 150 | }) 151 | } 152 | setZombiePrice(){ 153 | MyWeb3.setZombiePrice(this.state.zombiePrice).then(function (res) { 154 | console.log(res) 155 | window.location.reload() 156 | }) 157 | } 158 | inputZombiePrice(event){ 159 | this.setState({ 160 | zombiePrice:event.target.value 161 | }) 162 | } 163 | 164 | 165 | render() { 166 | if(window.defaultAccount === this.state.contractOwner){ 167 | return ( 168 |
169 |
170 |
合约地址
171 |
{this.state.contractAddress}
172 |
管理员
173 |
{this.state.contractOwner}
174 |
合约名称
175 |
{this.state.ContractName}
176 |
合约标识
177 |
{this.state.ContractSymbol}
178 |
合约余额
179 |
180 | {this.state.ContractBalance} 181 | 186 |
187 |
对战胜率
188 |
189 | 194 | 195 | 200 |
201 |
升级费
202 |
203 | 208 | 209 | 214 |
215 |
最低售价
216 |
217 | 222 | 223 | 228 |
229 |
税金
230 |
231 | 236 | 237 | 242 |
243 |
僵尸售价
244 |
245 | 250 | 251 | 256 |
257 |
258 |
259 | ) 260 | }else{ 261 | return( 262 |
263 | ) 264 | } 265 | } 266 | } 267 | 268 | export default ContractAdmin; -------------------------------------------------------------------------------- /src/MyWeb3.js: -------------------------------------------------------------------------------- 1 | import Web3 from "web3"; 2 | import abi from './ZombieCore.json' 3 | import ContractAddress from './ContractAddress' 4 | 5 | const MyWeb3 ={ 6 | init() { 7 | /* 8 | '1': Ethereum Main Network 9 | '2': Morden Test network 10 | '3': Ropsten Test Network 11 | '4': Rinkeby Test Network 12 | '5': Goerli Test Network 13 | '42': Kovan Test Network 14 | */ 15 | return new Promise((resolve, reject) => { 16 | //let currentChainId = parseInt(window.ethereum.chainId, 16) 17 | let ethereum = window.ethereum 18 | //禁止自动刷新,metamask要求写的 19 | ethereum.autoRefreshOnNetworkChange = false 20 | //开始调用metamask 21 | ethereum.enable().then(function (accounts) { 22 | //初始化provider 23 | let provider = window['ethereum'] || window.web3.currentProvider 24 | //初始化Web3 25 | window.web3 = new Web3(provider) 26 | //获取到当前以太坊网络id 27 | window.web3.eth.net.getId().then(function (result) { 28 | let currentChainId = result 29 | //设置最大监听器数量,否则出现warning 30 | window.web3.currentProvider.setMaxListeners(300) 31 | //从json获取到当前网络id下的合约地址 32 | let currentContractAddress = ContractAddress[currentChainId] 33 | if(currentContractAddress !== undefined){ 34 | //实例化合约 35 | window.MyContract = new window.web3.eth.Contract(abi.abi,currentContractAddress) 36 | //获取到当前默认的以太坊地址 37 | window.defaultAccount = accounts[0].toLowerCase() 38 | //that.allEvents(window.MyContract) 39 | resolve(true) 40 | }else{ 41 | reject('Unknow Your ChainId:'+currentChainId) 42 | } 43 | }) 44 | }).catch(function (error) { 45 | console.log(error) 46 | }) 47 | }) 48 | }, 49 | //僵尸总量 50 | zombieCount() { 51 | return new Promise((resolve, reject) => { 52 | window.MyContract.methods.zombieCount().call().then(function(zombieCount) { 53 | resolve(zombieCount) 54 | }) 55 | }) 56 | }, 57 | //获得单个僵尸数据 58 | zombies(zombieId){ 59 | return new Promise((resolve, reject) => { 60 | if(zombieId>=0){ 61 | window.MyContract.methods.zombies(zombieId).call().then(function(zombies) { 62 | resolve(zombies) 63 | }) 64 | } 65 | }) 66 | }, 67 | //获得僵尸拥有者地址 68 | zombieToOwner(zombieId){ 69 | return new Promise((resolve, reject) => { 70 | if(zombieId>=0){ 71 | window.MyContract.methods.zombieToOwner(zombieId).call().then(function(zombies) { 72 | resolve(zombies.toLowerCase()) 73 | }) 74 | } 75 | }) 76 | }, 77 | //获得当前用户的所有僵尸id 78 | getZombiesByOwner(){ 79 | return new Promise((resolve, reject) => { 80 | window.MyContract.methods.getZombiesByOwner(window.defaultAccount).call().then(function(zombies) { 81 | resolve(zombies) 82 | }) 83 | }) 84 | }, 85 | //创建随机僵尸 86 | createZombie(_name){ 87 | return new Promise((resolve, reject) => { 88 | window.MyContract.methods.createZombie(_name).send({from:window.defaultAccount}) 89 | .on('transactionHash', function(transactionHash){ 90 | resolve(transactionHash) 91 | }) 92 | .on('confirmation', function(confirmationNumber, receipt){ 93 | console.log({confirmationNumber:confirmationNumber,receipt:receipt}) 94 | }) 95 | .on('receipt', function(receipt){ 96 | console.log({receipt:receipt}) 97 | window.location.reload() 98 | }) 99 | .on('error', function(error,receipt){ 100 | console.log({error:error,receipt:receipt}) 101 | reject({error:error,receipt:receipt}) 102 | }) 103 | }) 104 | }, 105 | //购买僵尸 106 | buyZombie(_name){ 107 | return new Promise((resolve, reject) => { 108 | window.MyContract.methods.zombiePrice().call().then(function(zombiePrice) { 109 | window.MyContract.methods.buyZombie(_name).send({from:window.defaultAccount,value:zombiePrice}) 110 | .on('transactionHash', function(transactionHash){ 111 | resolve(transactionHash) 112 | }) 113 | .on('confirmation', function(confirmationNumber, receipt){ 114 | console.log({confirmationNumber:confirmationNumber,receipt:receipt}) 115 | }) 116 | .on('receipt', function(receipt){ 117 | console.log({receipt:receipt}) 118 | window.location.reload() 119 | }) 120 | .on('error', function(error,receipt){ 121 | console.log({error:error,receipt:receipt}) 122 | reject({error:error,receipt:receipt}) 123 | }) 124 | }) 125 | }) 126 | }, 127 | //僵尸对战 128 | attack(_zombieId,_targetId){ 129 | return new Promise((resolve, reject) => { 130 | window.MyContract.methods.attack(_zombieId,_targetId).send({from:window.defaultAccount}) 131 | .on('transactionHash', function(transactionHash){ 132 | resolve(transactionHash) 133 | }) 134 | .on('confirmation', function(confirmationNumber, receipt){ 135 | console.log({confirmationNumber:confirmationNumber,receipt:receipt}) 136 | }) 137 | .on('receipt', function(receipt){ 138 | console.log({receipt:receipt}) 139 | window.location.reload() 140 | }) 141 | .on('error', function(error,receipt){ 142 | console.log({error:error,receipt:receipt}) 143 | reject({error:error,receipt:receipt}) 144 | }) 145 | }) 146 | }, 147 | //僵尸改名 148 | changeName(_zombieId,_name){ 149 | return new Promise((resolve, reject) => { 150 | window.MyContract.methods.changeName(_zombieId,_name).send({from:window.defaultAccount}) 151 | .on('transactionHash', function(transactionHash){ 152 | resolve(transactionHash) 153 | }) 154 | .on('confirmation', function(confirmationNumber, receipt){ 155 | console.log({confirmationNumber:confirmationNumber,receipt:receipt}) 156 | }) 157 | .on('receipt', function(receipt){ 158 | console.log({receipt:receipt}) 159 | window.location.reload() 160 | }) 161 | .on('error', function(error,receipt){ 162 | console.log({error:error,receipt:receipt}) 163 | reject({error:error,receipt:receipt}) 164 | }) 165 | }) 166 | }, 167 | //僵尸喂食 168 | feed(_zombieId){ 169 | return new Promise((resolve, reject) => { 170 | window.MyContract.methods.feed(_zombieId).send({from:window.defaultAccount}) 171 | .on('transactionHash', function(transactionHash){ 172 | resolve(transactionHash) 173 | }) 174 | .on('confirmation', function(confirmationNumber, receipt){ 175 | console.log({confirmationNumber:confirmationNumber,receipt:receipt}) 176 | }) 177 | .on('receipt', function(receipt){ 178 | console.log({receipt:receipt}) 179 | window.location.reload() 180 | }) 181 | .on('error', function(error,receipt){ 182 | console.log({error:error,receipt:receipt}) 183 | reject({error:error,receipt:receipt}) 184 | }) 185 | }) 186 | }, 187 | //僵尸付费升级 188 | levelUp(_zombieId){ 189 | return new Promise((resolve, reject) => { 190 | window.MyContract.methods.levelUpFee().call().then(function(levelUpFee) { 191 | window.MyContract.methods.levelUp(_zombieId).send({from:window.defaultAccount,value:levelUpFee}) 192 | .on('transactionHash', function(transactionHash){ 193 | resolve(transactionHash) 194 | }) 195 | .on('confirmation', function(confirmationNumber, receipt){ 196 | console.log({confirmationNumber:confirmationNumber,receipt:receipt}) 197 | }) 198 | .on('receipt', function(receipt){ 199 | console.log({receipt:receipt}) 200 | window.location.reload() 201 | }) 202 | .on('error', function(error,receipt){ 203 | console.log({error:error,receipt:receipt}) 204 | reject({error:error,receipt:receipt}) 205 | }) 206 | }) 207 | }) 208 | }, 209 | //获取僵尸喂食次数 210 | zombieFeedTimes(_zombieId){ 211 | return new Promise((resolve, reject) => { 212 | window.MyContract.methods.zombieFeedTimes(_zombieId).call().then(function(zombieFeedTimes) { 213 | resolve(zombieFeedTimes) 214 | }) 215 | }) 216 | }, 217 | //获取最低售价 218 | minPrice(){ 219 | return new Promise((resolve, reject) => { 220 | window.MyContract.methods.minPrice().call().then(function(minPrice) { 221 | resolve(window.web3.utils.fromWei(minPrice,'ether')) 222 | }) 223 | }) 224 | }, 225 | //获取税金 226 | tax(){ 227 | return new Promise((resolve, reject) => { 228 | window.MyContract.methods.tax().call().then(function(tax) { 229 | resolve(window.web3.utils.fromWei(tax,'ether')) 230 | }) 231 | }) 232 | }, 233 | //出售我的僵尸 234 | saleMyZombie(_zombieId,_price){ 235 | return new Promise((resolve, reject) => { 236 | window.MyContract.methods.saleMyZombie(_zombieId,window.web3.utils.toWei(_price.toString())).send({from:window.defaultAccount}) 237 | .on('transactionHash', function(transactionHash){ 238 | resolve(transactionHash) 239 | }) 240 | .on('confirmation', function(confirmationNumber, receipt){ 241 | console.log({confirmationNumber:confirmationNumber,receipt:receipt}) 242 | }) 243 | .on('receipt', function(receipt){ 244 | console.log({receipt:receipt}) 245 | window.location.reload() 246 | }) 247 | .on('error', function(error,receipt){ 248 | console.log({error:error,receipt:receipt}) 249 | reject({error:error,receipt:receipt}) 250 | }) 251 | }) 252 | }, 253 | //获得商店里僵尸数据 254 | zombieShop(_zombieId){ 255 | return new Promise((resolve, reject) => { 256 | window.MyContract.methods.zombieShop(_zombieId).call().then(function(shopInfo) { 257 | shopInfo.price = window.web3.utils.fromWei(shopInfo.price,'ether') 258 | resolve(shopInfo) 259 | }) 260 | }) 261 | }, 262 | //获得商店所有僵尸 263 | getShopZombies(){ 264 | return new Promise((resolve, reject) => { 265 | window.MyContract.methods.getShopZombies().call().then(function(zombieIds) { 266 | resolve(zombieIds) 267 | }) 268 | }) 269 | }, 270 | //购买商店里的僵尸 271 | buyShopZombie(_zombieId,_price){ 272 | return new Promise((resolve, reject) => { 273 | window.MyContract.methods.buyShopZombie(_zombieId).send({from:window.defaultAccount,value:window.web3.utils.toWei(_price.toString())}) 274 | .on('transactionHash', function(transactionHash){ 275 | resolve(transactionHash) 276 | }) 277 | .on('confirmation', function(confirmationNumber, receipt){ 278 | console.log({confirmationNumber:confirmationNumber,receipt:receipt}) 279 | }) 280 | .on('receipt', function(receipt){ 281 | console.log({receipt:receipt}) 282 | window.location.reload() 283 | }) 284 | .on('error', function(error,receipt){ 285 | console.log({error:error,receipt:receipt}) 286 | reject({error:error,receipt:receipt}) 287 | }) 288 | }) 289 | }, 290 | //获得合约拥有者地址 291 | owner(){ 292 | return new Promise((resolve, reject) => { 293 | window.MyContract.methods.owner().call().then(function(owner) { 294 | resolve(owner.toLowerCase()) 295 | }) 296 | }) 297 | }, 298 | //获得合约名称 299 | name(){ 300 | return new Promise((resolve, reject) => { 301 | window.MyContract.methods.name().call().then(function(name) { 302 | resolve(name) 303 | }) 304 | }) 305 | }, 306 | //获得合约标识 307 | symbol(){ 308 | return new Promise((resolve, reject) => { 309 | window.MyContract.methods.symbol().call().then(function(symbol) { 310 | resolve(symbol) 311 | }) 312 | }) 313 | }, 314 | //查询余额 315 | checkBalance(){ 316 | return new Promise((resolve, reject) => { 317 | this.owner().then(function (owner) { 318 | if(window.defaultAccount === owner){ 319 | window.MyContract.methods.checkBalance().call({from:window.defaultAccount}).then(function(balance) { 320 | resolve(window.web3.utils.fromWei(balance,'ether')) 321 | }) 322 | }else{ 323 | reject('You are not contract owner') 324 | } 325 | }) 326 | }) 327 | }, 328 | //设置攻击胜率 329 | setAttackVictoryProbability(_attackVictoryProbability){ 330 | return new Promise((resolve, reject) => { 331 | window.MyContract.methods.setAttackVictoryProbability(_attackVictoryProbability).send({from:window.defaultAccount}) 332 | .then(function(result) { 333 | resolve(result) 334 | }) 335 | }) 336 | }, 337 | //获得升级费 338 | levelUpFee(){ 339 | return new Promise((resolve, reject) => { 340 | window.MyContract.methods.levelUpFee().call().then(function(levelUpFee) { 341 | resolve(window.web3.utils.fromWei(levelUpFee,'ether')) 342 | }) 343 | }) 344 | }, 345 | //设置升级费 346 | setLevelUpFee(_fee){ 347 | return new Promise((resolve, reject) => { 348 | window.MyContract.methods.setLevelUpFee(window.web3.utils.toWei(_fee.toString())).send({from:window.defaultAccount}) 349 | .then(function(result) { 350 | resolve(result) 351 | }) 352 | }) 353 | }, 354 | //设置最低售价 355 | setMinPrice(_value){ 356 | return new Promise((resolve, reject) => { 357 | window.MyContract.methods.setMinPrice(window.web3.utils.toWei(_value.toString())).send({from:window.defaultAccount}) 358 | .then(function(result) { 359 | resolve(result) 360 | }) 361 | }) 362 | }, 363 | //获得僵尸售价 364 | zombiePrice(){ 365 | return new Promise((resolve, reject) => { 366 | window.MyContract.methods.zombiePrice().call().then(function(zombiePrice) { 367 | resolve(window.web3.utils.fromWei(zombiePrice,'ether')) 368 | }) 369 | }) 370 | }, 371 | //设置僵尸售价 372 | setZombiePrice(_value){ 373 | return new Promise((resolve, reject) => { 374 | window.MyContract.methods.setZombiePrice(window.web3.utils.toWei(_value.toString())).send({from:window.defaultAccount}) 375 | .then(function(result) { 376 | resolve(result) 377 | }) 378 | }) 379 | }, 380 | //设置税金 381 | setTax(_value){ 382 | return new Promise((resolve, reject) => { 383 | window.MyContract.methods.setTax(window.web3.utils.toWei(_value.toString())).send({from:window.defaultAccount}) 384 | .then(function(result) { 385 | resolve(result) 386 | }) 387 | }) 388 | }, 389 | //提款 390 | withdraw(){ 391 | return new Promise((resolve, reject) => { 392 | this.owner().then(function (owner) { 393 | if(window.defaultAccount === owner){ 394 | window.MyContract.methods.withdraw().send({from:window.defaultAccount}).then(function(res) { 395 | resolve(res) 396 | }) 397 | }else{ 398 | reject('You are not contract owner') 399 | } 400 | }) 401 | }) 402 | }, 403 | //新僵尸事件 404 | EventNewZombie(){ 405 | return window.MyContract.events.NewZombie({},{fromBlock: 0, toBlock: 'latest'}) 406 | }, 407 | //出售僵尸事件 408 | EventSaleZombie(){ 409 | return new Promise((resolve, reject) => { 410 | window.MyContract.events.SaleZombie({fromBlock: 0, toBlock: 'latest'},function (error, event) { 411 | resolve(event) 412 | }) 413 | }) 414 | }, 415 | //所有事件 416 | allEvents(){ 417 | window.MyContract.events.allEvents({fromBlock: 0}, function(error, event){ 418 | console.log({allEvents:event}) 419 | }).on("connected", function(subscriptionId){ 420 | console.log({connected_subscriptionId:subscriptionId}) 421 | }).on('data', function(event){ 422 | console.log({event_data:event}) 423 | }).on('changed', function(event){ 424 | console.log({event_changed:event}) 425 | }).on('error', function(error, receipt) { 426 | console.log({event_error:error,receipt:receipt}) 427 | }) 428 | } 429 | } 430 | 431 | export default MyWeb3; -------------------------------------------------------------------------------- /src/MyZombie.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import ZombieCard from "./ZombieCard" 3 | import './static/ZombiePreview.css' 4 | import Page from "./Page" 5 | import MyWeb3 from './MyWeb3' 6 | import { 7 | BrowserRouter as 8 | Route, 9 | Link 10 | } from "react-router-dom" 11 | 12 | class MyZombie extends Component { 13 | constructor(props) { 14 | super(props); 15 | this.state = {zombieCount:"",zombies:[],zombieName:'',transactionHash:'',buyAreaDisp:1,createAreaDisp:1,txHashDisp:0} 16 | this.createZombie=this.createZombie.bind(this) 17 | this.buyZombie=this.buyZombie.bind(this) 18 | this.inputChange=this.inputChange.bind(this) 19 | } 20 | 21 | componentDidMount(){ 22 | let that = this 23 | let ethereum = window.ethereum 24 | if (typeof ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) { 25 | MyWeb3.init().then(function(res){ 26 | that.myZombies() 27 | }) 28 | }else { 29 | alert('You have to install MetaMask !') 30 | } 31 | } 32 | 33 | myZombies(){ 34 | let that = this 35 | MyWeb3.getZombiesByOwner().then(function(zombies){ 36 | if(zombies.length > 0){ 37 | for(let i=0;i0) { 79 | return ( 80 |
81 | {this.state.zombies.map((item,index)=>{ 82 | var name = item.name 83 | var level = item.level 84 | return( 85 | 86 | 87 | 88 | ) 89 | })} 90 | 91 |
92 |
93 | {this.input=input}} 98 | value={this.state.zombieName} 99 | onChange={this.inputChange}> 100 | 101 |
102 |
103 | 108 |
109 |
110 |
{this.state.transactionHash}

等待确认中...
111 |
112 | ) 113 | }else{ 114 | return(
115 |
116 |
117 | {this.input=input}} 122 | value={this.state.zombieName} 123 | onChange={this.inputChange}> 124 | 125 |
126 |
127 | 132 |
133 |
134 |
{this.state.transactionHash}

等待确认中...
135 |
) 136 | } 137 | } 138 | } 139 | export default MyZombie; -------------------------------------------------------------------------------- /src/Page.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ZombieArmy from "./ZombieArmy" 3 | import MyZombie from "./MyZombie" 4 | import ZombieMarket from "./ZombieMarket" 5 | import ZombieSimulator from "./ZombieSimulator" 6 | import ZombieDetail from "./ZombieDetail"; 7 | import ZombieAttack from "./ZombieAttack"; 8 | import ContractAdmin from "./ContractAdmin" 9 | 10 | class Page extends Component { 11 | constructor(props) { 12 | super(props) 13 | this.state = {page:'',id:0 } 14 | } 15 | componentDidMount(){ 16 | let search = this.props.location.search.replace(/\?/,'').split("&") 17 | let page = search[0] 18 | this.setState({page:page}) 19 | } 20 | UNSAFE_componentWillReceiveProps(nextProps){ 21 | if(nextProps!==this.props){ 22 | this.setState({nextProps}) 23 | let search = nextProps.location.search.replace(/\?/,'').split("&") 24 | let page = search[0] === '' ? 'ZombieArmy' : search[0] 25 | this.setState({page:page}) 26 | return true 27 | }else{ 28 | return false 29 | } 30 | } 31 | render() { 32 | switch (this.state.page){ 33 | case 'ZombieArmy': 34 | return() 35 | case 'MyZombie': 36 | return() 37 | case 'ZombieMarket': 38 | return() 39 | case 'ZombieSimulator': 40 | return() 41 | case 'ZombieDetail': 42 | return() 43 | case 'ZombieAttack': 44 | return() 45 | case 'ContractAdmin': 46 | return() 47 | default: 48 | return() 49 | } 50 | } 51 | } 52 | 53 | export default Page; -------------------------------------------------------------------------------- /src/ZombieArmy.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import ZombieCard from "./ZombieCard"; 3 | import './static/ZombiePreview.css'; 4 | import MyWeb3 from './MyWeb3' 5 | import { 6 | BrowserRouter as 7 | Route, 8 | Link 9 | } from "react-router-dom" 10 | import Page from "./Page"; 11 | 12 | class ZombieArmy extends Component { 13 | constructor(props) { 14 | super(props); 15 | this.state = { zombieCount:"",zombies:[] } 16 | } 17 | 18 | componentDidMount(){ 19 | let that = this 20 | let ethereum = window.ethereum 21 | if (typeof ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) { 22 | MyWeb3.init().then(function(res){ 23 | that.zombieArmy() 24 | }) 25 | }else { 26 | alert('You have to install MetaMask !') 27 | } 28 | } 29 | zombieArmy(){ 30 | let that = this 31 | MyWeb3.zombieCount().then(function(result){ 32 | if(result > 0){ 33 | for(let i=0;i { 46 | return 47 | } 48 | } 49 | render() { 50 | if(this.state.zombies.length>0) { 51 | return ( 52 |
53 | {this.state.zombies.map((item,index)=>{ 54 | var name = item.name 55 | var level = item.level 56 | return( 57 | 58 | 59 | 60 | ) 61 | })} 62 | 63 |
64 | ) 65 | }else{ 66 | return (
) 67 | } 68 | } 69 | } 70 | 71 | export default ZombieArmy; -------------------------------------------------------------------------------- /src/ZombieAttack.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import MyWeb3 from './MyWeb3' 3 | import ZombiePreview from "./ZombiePreview" 4 | import './static/ZombiePreview.css' 5 | import moment from "moment" 6 | 7 | class NewZombie extends Component { 8 | constructor(props) { 9 | super(props); 10 | const searchParams = new URLSearchParams(window.location.search) 11 | const id = searchParams.get('id') 12 | this.state = { 13 | targetId:id , 14 | targetZombie:{}, 15 | myZombies:[], 16 | myZombie:{}, 17 | myZombieId:'', 18 | active: {}, 19 | buttonTxt:'', 20 | modalDisplay:'none', 21 | transactionHash:'', 22 | AttackBtn:()=>{ 23 | return( 28 | ) 29 | } 30 | } 31 | this.selectZombie = this.selectZombie.bind(this) 32 | this.zombieAttack = this.zombieAttack.bind(this) 33 | } 34 | componentDidMount(){ 35 | let that = this 36 | let ethereum = window.ethereum 37 | if (typeof ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) { 38 | MyWeb3.init().then(function(res){ 39 | that.getZombie(that.state.targetId) 40 | that.getMyZombies() 41 | }) 42 | }else { 43 | alert('You have to install MetaMask !') 44 | } 45 | } 46 | 47 | getZombie(zombieId){ 48 | let that = this 49 | MyWeb3.zombies(zombieId).then(function (result) { 50 | that.setState({targetZombie:result}) 51 | }) 52 | } 53 | getMyZombies(){ 54 | let that = this 55 | MyWeb3.getZombiesByOwner().then(function(zombies){ 56 | if(zombies.length > 0){ 57 | for(let i=0;iresult.readyTime){ 62 | _zombies.push(result) 63 | } 64 | that.setState({myZombies:_zombies}) 65 | }) 66 | } 67 | } 68 | }) 69 | } 70 | selectZombie = index => { 71 | var _active = this.state.active 72 | var prev_active = _active[index] 73 | for(var i=0;i{ 82 | return( 87 | ) 88 | } 89 | }) 90 | } 91 | 92 | zombieAttack(){ 93 | let that = this 94 | if(this.state.myZombie !== undefined){ 95 | this.setState({modalDisplay:''}) 96 | MyWeb3.attack(this.state.myZombieId,this.state.targetId) 97 | .then(function(transactionHash){ 98 | that.setState({ 99 | transactionHash:transactionHash, 100 | AttackBtn : () =>{ 101 | return(
) 102 | } 103 | }) 104 | }) 105 | } 106 | } 107 | render() { 108 | let AttackBtn = this.state.AttackBtn 109 | if(this.state.myZombies.length>0) { 110 | return ( 111 |
112 |
118 |
119 |
120 | 121 |
122 |
123 | VS 124 |
125 |
126 | 127 |
128 |
129 |

{this.state.transactionHash}

130 |
131 |
132 |
133 |
134 | 135 |
136 |
137 |
138 |
139 | {this.state.myZombies.map((item,index)=>{ 140 | var name = item.name 141 | var level = item.level 142 | return( 143 |
this.selectZombie(index)} > 144 |
145 | 146 |
147 |
148 | {name} 149 |
150 | CryptoZombie{level}级 151 |
152 |
153 |
154 | ) 155 | })} 156 |
157 | 158 |
159 |
160 |
161 | ); 162 | }else{ 163 | return( 164 |
没有能干它的僵尸
165 | ) 166 | } 167 | } 168 | } 169 | 170 | export default NewZombie; -------------------------------------------------------------------------------- /src/ZombieCard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ZombiePreview from "./ZombiePreview" 3 | 4 | class ZombieCard extends Component { 5 | constructor(props) { 6 | super(props) 7 | this.state = { zombie:this.props.zombie,name:this.props.name,level:this.props.level} 8 | } 9 | UNSAFE_componentWillReceiveProps(nextProps){ 10 | if(nextProps!==this.props){ 11 | this.setState({ _className:nextProps._className,_style:nextProps._style}) 12 | return true 13 | }else{ 14 | return false 15 | } 16 | } 17 | render() { 18 | return ( 19 |
20 |
21 | 22 |
23 |
24 | {this.state.name} 25 |
26 | CryptoZombie{this.state.level}级 27 |
28 |
29 |
30 | ) 31 | } 32 | } 33 | 34 | export default ZombieCard; -------------------------------------------------------------------------------- /src/ZombieDetail.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import ZombiePreview from "./ZombiePreview" 3 | import './static/ZombiePreview.css' 4 | import MyWeb3 from './MyWeb3' 5 | import moment from "moment" 6 | import { 7 | BrowserRouter as 8 | Route, 9 | Link 10 | } from "react-router-dom" 11 | import Page from "./Page"; 12 | 13 | class Zombiedetail extends Component { 14 | constructor(props) { 15 | super(props) 16 | const searchParams = new URLSearchParams(window.location.search) 17 | 18 | const id = searchParams.get('id') 19 | this.state = { 20 | id:id , 21 | zombie:{},owner:'', 22 | zombieFeedTimes:0, 23 | myPrice:0, 24 | minPrice:0, 25 | AttackBtn: () =>{return(
)}, 26 | RenameArea: () =>{return(
)}, 27 | zombieNewname:'', 28 | FeedArea: () =>{return(
)}, 29 | LevelupArea: () =>{return(
)}, 30 | SaleArea: () =>{return(
)}, 31 | BuyArea: () =>{return(
)}, 32 | onShop:false, 33 | shopInfo:{} 34 | } 35 | this.zombieChangeName = this.zombieChangeName.bind(this) 36 | this.changeName = this.changeName.bind(this) 37 | this.feed = this.feed.bind(this) 38 | this.levelUp = this.levelUp.bind(this) 39 | this.saleZombie = this.saleZombie.bind(this) 40 | this.buyShopZombie = this.buyShopZombie.bind(this) 41 | this.setPrice = this.setPrice.bind(this) 42 | } 43 | 44 | componentDidMount(){ 45 | let that = this 46 | let ethereum = window.ethereum 47 | if (typeof ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) { 48 | MyWeb3.init().then(function(res){ 49 | that.getZombie(that.state.id) 50 | that.getZombieFeedTimes(that.state.id) 51 | that.getMinPrice() 52 | that.getZombieShop(that.state.id) 53 | }) 54 | }else { 55 | alert('You have to install MetaMask !') 56 | } 57 | } 58 | getZombieShop(zombieId){ 59 | let that = this 60 | MyWeb3.zombieShop(zombieId).then(function (shopInfo) { 61 | if(shopInfo.price>0){ 62 | that.setState({onShop:true,shopInfo:shopInfo}) 63 | } 64 | }) 65 | } 66 | getMinPrice(){ 67 | let that = this 68 | MyWeb3.minPrice().then(function (minPrice) { 69 | if(minPrice>0){ 70 | MyWeb3.tax().then(function (tax) { 71 | if(tax>0){ 72 | that.setState({myPrice:parseFloat(minPrice)+parseFloat(tax),minPrice:parseFloat(minPrice)+parseFloat(tax)}) 73 | } 74 | }) 75 | } 76 | }) 77 | } 78 | getZombieFeedTimes(zombieId){ 79 | let that = this 80 | MyWeb3.zombieFeedTimes(zombieId).then(function (result) { 81 | if(result>0){ 82 | that.setState({zombieFeedTimes:result}) 83 | } 84 | }) 85 | } 86 | setPrice(event){ 87 | this.setState({ 88 | myPrice:event.target.value 89 | }) 90 | } 91 | zombieChangeName(event){ 92 | this.setState({ 93 | zombieNewname:event.target.value 94 | }) 95 | } 96 | changeName(){ 97 | let that = this 98 | if(window.defaultAccount !== undefined){ 99 | MyWeb3.changeName(this.state.id,this.state.zombieNewname) 100 | .then(function(transactionHash){ 101 | that.setState({RenameArea : () =>{ 102 | return(
{transactionHash}
) 103 | } 104 | }) 105 | }) 106 | } 107 | } 108 | feed(){ 109 | let that = this 110 | if(window.defaultAccount !== undefined){ 111 | MyWeb3.feed(this.state.id) 112 | .then(function(transactionHash){ 113 | that.setState({FeedArea : () =>{ 114 | return(
{transactionHash}
) 115 | } 116 | }) 117 | }) 118 | } 119 | } 120 | levelUp(){ 121 | let that = this 122 | if(window.defaultAccount !== undefined){ 123 | MyWeb3.levelUp(this.state.id) 124 | .then(function(transactionHash){ 125 | that.setState({LevelupArea : () =>{ 126 | return(
{transactionHash}
) 127 | } 128 | }) 129 | }) 130 | } 131 | } 132 | saleZombie(){ 133 | let that = this 134 | if(window.defaultAccount !== undefined 135 | && this.state.myPrice*this.state.minPrice>0 136 | && this.state.myPrice>=this.state.minPrice){ 137 | MyWeb3.saleMyZombie(this.state.id,this.state.myPrice) 138 | .then(function(transactionHash){ 139 | that.setState({SaleArea : () =>{ 140 | return(
{transactionHash}
) 141 | } 142 | }) 143 | }) 144 | } 145 | } 146 | buyShopZombie(){ 147 | let that = this 148 | if(window.defaultAccount !== undefined){ 149 | MyWeb3.buyShopZombie(this.state.id,this.state.shopInfo.price) 150 | .then(function(transactionHash){ 151 | that.setState({BuyArea : () =>{ 152 | return(
{transactionHash}
) 153 | } 154 | }) 155 | }) 156 | } 157 | } 158 | getZombie(zombieId){ 159 | let that = this 160 | MyWeb3.zombies(zombieId).then(function (result) { 161 | that.setState({zombie:result}) 162 | that.setState({zombieNewname:result.name}) 163 | MyWeb3.zombieToOwner(zombieId).then(function (zombieOwner) { 164 | that.setState({owner:zombieOwner}) 165 | if(window.defaultAccount !== undefined && 166 | zombieOwner !== window.defaultAccount){ 167 | that.setState({AttackBtn : () =>{ 168 | return( 169 | ) 174 | } 175 | }) 176 | if(that.state.onShop){ 177 | that.setState({BuyArea : () =>{ 178 | return( 179 |
180 |
181 | 售价:{that.state.shopInfo.price} ether 182 |
183 |
184 | 189 |
190 |
191 | ) 192 | } 193 | }) 194 | } 195 | }else{ 196 | that.setState({AttackBtn : () =>{return(
)}}) 197 | if(that.state.zombie.level > 1){ 198 | that.setState({RenameArea : () =>{ 199 | return( 200 |
201 |
202 | 208 | 209 |
210 |
211 | 216 |
217 |
) 218 | } 219 | }) 220 | } 221 | if(that.state.zombie.readyTime === 0 || moment().format('X')>that.state.zombie.readyTime){ 222 | that.setState({FeedArea : () =>{ 223 | return( 224 |
225 | 230 |
) 231 | } 232 | }) 233 | } 234 | that.setState({LevelupArea : () =>{ 235 | return( 236 |
237 | 242 |
) 243 | } 244 | }) 245 | if(!that.state.onShop){ 246 | that.setState({SaleArea : () =>{ 247 | return( 248 |
249 |
250 | 256 | 257 |
258 |
259 | 264 |
265 |
266 | ) 267 | } 268 | }) 269 | } 270 | } 271 | }) 272 | }) 273 | } 274 | 275 | render() { 276 | var readyTime = '已冷却' 277 | if(this.state.zombie.readyTime !== undefined && moment().format('X') 288 |
289 |
290 |
291 |
292 | 293 |
294 |
295 |
296 | CryptoZombie第一级 297 |
298 |
299 |
300 |
301 |
302 |
{this.state.zombie.name}
303 |
主人
304 |
{this.state.owner}
305 |
等级
306 |
{this.state.zombie.level}
307 |
胜利次数
308 |
{this.state.zombie.winCount}
309 |
失败次数
310 |
{this.state.zombie.lossCount}
311 |
冷却时间
312 |
{readyTime}
313 |
喂食次数
314 |
{this.state.zombieFeedTimes}
315 |
316 |
317 | 318 | 319 | 320 | 321 | 322 | 323 |
324 |
325 |
326 | 327 |
328 | 329 | ); 330 | } 331 | } 332 | 333 | export default Zombiedetail; -------------------------------------------------------------------------------- /src/ZombieMarket.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import MyWeb3 from './MyWeb3' 3 | import ZombieCard from "./ZombieCard"; 4 | import { 5 | BrowserRouter as 6 | Route, 7 | Link 8 | } from "react-router-dom" 9 | import Page from "./Page"; 10 | 11 | class ZombieMarket extends Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = {shopZombies:[] } 15 | } 16 | 17 | componentDidMount(){ 18 | //console.log(window.web3._extend.utils) 19 | let that = this 20 | let ethereum = window.ethereum 21 | if (typeof ethereum !== 'undefined' || (typeof window.web3 !== 'undefined')) { 22 | MyWeb3.init().then(function(res){ 23 | that.zombieShop() 24 | }) 25 | }else { 26 | alert('You have to install MetaMask !') 27 | } 28 | } 29 | 30 | zombieShop(){ 31 | let that = this 32 | MyWeb3.getShopZombies().then(function(zombieIds){ 33 | if(zombieIds.length>0){ 34 | for(var i=0;i=0){ 37 | MyWeb3.zombies(zombieId).then(function(zombies) { 38 | let _shopZombies = that.state.shopZombies 39 | zombies.zombieId = zombieId 40 | _shopZombies.push(zombies); 41 | that.setState({shopZombies:_shopZombies}) 42 | }) 43 | } 44 | } 45 | } 46 | }) 47 | } 48 | 49 | render() { 50 | if(this.state.shopZombies.length>0) { 51 | return ( 52 |
53 | {this.state.shopZombies.map((item,index)=>{ 54 | var name = item.name 55 | var level = item.level 56 | return( 57 | 58 | 59 | 60 | ) 61 | })} 62 | 63 |
64 | ) 65 | }else{ 66 | return (
) 67 | } 68 | } 69 | } 70 | 71 | export default ZombieMarket; -------------------------------------------------------------------------------- /src/ZombiePreview.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | 3 | class ZombiePreview extends Component { 4 | constructor(props){ 5 | super(props) 6 | this.state = { zombie:this.props.zombie,_style:this.props._style,_className:this.props._className} 7 | } 8 | UNSAFE_componentWillReceiveProps(nextProps){ 9 | if(nextProps!==this.props){ 10 | this.setState({ 11 | zombie:nextProps.zombie, 12 | _style:nextProps._style, 13 | _className:nextProps._className, 14 | }) 15 | return true 16 | }else{ 17 | return false 18 | } 19 | } 20 | render(){ 21 | var _style = this.state._style || [] 22 | var _className = this.state._className 23 | if(this.state.zombie !== undefined){ 24 | _style['color'] = {filter:"hue-rotate(0deg)"} 25 | _style['skin'] = {filter:"hue-rotate(0deg)"} 26 | _style['eye_color'] = {filter:"hue-rotate(0deg)"} 27 | _className = "zombie-parts head-visible-1 eye-visible-1 shirt-visible-1" 28 | if(this.state.zombie.dna !== undefined){ 29 | var dna = this.state.zombie.dna 30 | var _head = dna.substring(0,2) % 8 +1 31 | var _eye = dna.substring(2,4) % 11 +1 32 | var _shirt = dna.substring(4,6) % 6 +1 33 | _className = "zombie-parts head-visible-"+_head+" eye-visible-"+_eye+" shirt-visible-"+_shirt 34 | _style['color'] = {filter:"hue-rotate("+dna.substring(6,9) % 360 +1+"deg)"} 35 | _style['skin'] = {filter:"hue-rotate("+dna.substring(9,12) % 360 +1+"deg)"} 36 | _style['eye_color'] = {filter:"hue-rotate("+dna.substring(12,15) % 360+"deg)"} 37 | } 38 | } 39 | return ( 40 |
41 | {/* */} 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
82 | ); 83 | } 84 | } 85 | 86 | 87 | export default ZombiePreview; 88 | -------------------------------------------------------------------------------- /src/ZombieSimulator.js: -------------------------------------------------------------------------------- 1 | import React,{Component} from 'react' 2 | import './static/ZombiePreview.css'; 3 | import ZombiePreview from "./ZombiePreview"; 4 | import ZombieToggler from "./ZombieToggler"; 5 | 6 | 7 | 8 | class ZombieSimulator extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | _value:{ 13 | "head":1, 14 | "eye":1, 15 | "shirt":1 16 | }, 17 | _className:"zombie-parts head-visible-1 eye-visible-1 shirt-visible-1", 18 | _style:{ 19 | eye_color:{filter:"hue-rotate(0deg)"}, 20 | skin:{filter:"hue-rotate(0deg)"}, 21 | color:{filter:"hue-rotate(0deg)"}, 22 | } 23 | }; 24 | this.handleChange = this.handleChange.bind(this); 25 | } 26 | 27 | handleChange(event) { 28 | var _id = event.target.id.replace(/_select/,""); 29 | 30 | var _value = this.state._value 31 | var _className = this.state._className 32 | var _style = this.state._style 33 | _value[_id] = event.target.value; 34 | 35 | if(_id === "head" || _id === "eye" || _id === "shirt"){ 36 | _className = "zombie-parts head-visible-"+ _value["head"] +" eye-visible-"+ _value["eye"] +" shirt-visible-" + _value["shirt"]; 37 | }else{ 38 | _style[_id] = {filter:"hue-rotate("+_value[_id]+"deg)"} 39 | } 40 | this.setState({_className,_style,_value}); 41 | } 42 | render() { 43 | return
44 |
45 |
46 |
47 | 48 |
49 |
50 | 51 |
52 |
; 53 | } 54 | } 55 | 56 | 57 | 58 | export default ZombieSimulator 59 | -------------------------------------------------------------------------------- /src/ZombieToggler.js: -------------------------------------------------------------------------------- 1 | import React,{Component,Fragment} from 'react' 2 | 3 | 4 | class ZombieToggler extends Component { 5 | constructor(props){ 6 | super(props) 7 | this.handleChange=this.handleChange.bind(this) 8 | this.state = { list: [ 9 | { 10 | "name":"head", 11 | "title":"头部基因", 12 | "max":8 13 | },{ 14 | "name":"eye", 15 | "title":"眼部基因", 16 | "max":11 17 | },{ 18 | "name":"shirt", 19 | "title":"上衣基因", 20 | "max":6 21 | },{ 22 | "name":"skin", 23 | "title":"皮肤颜色", 24 | "max":360 25 | },{ 26 | "name":"eye_color", 27 | "title":"眼睛颜色", 28 | "max":360 29 | },{ 30 | "name":"color", 31 | "title":"衣服颜色", 32 | "max":360 33 | } 34 | ],inputValue:[] 35 | } 36 | } 37 | handleChange(event){ 38 | var id = event.target.id.replace(/_select/,"") 39 | var _list = this.state.inputValue 40 | _list[id] = event.target.value; 41 | this.setState({ 42 | inputValue:_list 43 | }) 44 | this.props.handleChange(event); 45 | } 46 | render() { 47 | return ( 48 |
49 |
50 |
51 | {this.state.list.map((item,index)=>{ 52 | return( 53 | 54 | 55 | ) 56 | })} 57 |
58 |
59 |
60 | ); 61 | } 62 | } 63 | 64 | export default ZombieToggler; 65 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './static/Index.css' 4 | import App from './App'; 5 | require('events').EventEmitter.prototype._maxListeners = 100; 6 | ReactDOM.render(, document.getElementById('root')); -------------------------------------------------------------------------------- /src/static/App.css: -------------------------------------------------------------------------------- 1 | 2 | @media (min-width: 576px){ 3 | .block { 4 | padding: 120px 1rem; 5 | } 6 | } 7 | .block { 8 | position: relative; 9 | padding: 60px 30px; 10 | color: inherit; 11 | background-position: 50%; 12 | background-size: cover; 13 | background-repeat: no-repeat; 14 | } 15 | .zombies-hero.no-webp { 16 | background-image: url(./index_bg.png); 17 | } 18 | .zombies-hero { 19 | background-repeat: no-repeat; 20 | background-position: center top; 21 | background-size: cover; 22 | background-size: 100% auto; 23 | } 24 | .pt-5 { 25 | padding-top: 3rem!important; 26 | } 27 | .pb-0 { 28 | padding-bottom: 0!important; 29 | } 30 | article, aside, dialog, figcaption, figure, footer, header, hgroup, main, nav, section { 31 | display: block; 32 | } 33 | *, :after, :before { 34 | box-sizing: inherit; 35 | } 36 | .zombies-hero .container { 37 | padding-top: 35%; 38 | text-align: center; 39 | max-width: 1260px; 40 | overflow: auto; 41 | display: -webkit-box; 42 | } 43 | .container { 44 | margin-right: auto; 45 | margin-left: auto; 46 | padding-right: 10px; 47 | padding-left: 10px; 48 | width: 100%; 49 | } 50 | .zombies-hero .menu { 51 | background-color: rgba(0,0,0,.5); 52 | padding-top: 20px; 53 | height: min-content; 54 | display: flex; 55 | width: 100%; 56 | } 57 | .zombies-hero .menu ul { 58 | width: 100%; 59 | display: flex; 60 | } 61 | .zombies-hero a { 62 | color: #fff; 63 | } 64 | .zombies-hero a:hover { 65 | color: #fff; 66 | text-decoration: none; 67 | } 68 | .zombies-hero .menu li { 69 | width: 33%; 70 | } 71 | 72 | .start-course-btn:active { 73 | background: url(./Btn_Register_Normal.png); 74 | background-size: 100% 100%; 75 | } 76 | .start-course-btn:hover { 77 | background: url(./Btn_Register_Over.png); 78 | background-size: 100% 100%; 79 | } 80 | .start-course-btn { 81 | cursor: pointer; 82 | background: url(./Btn_Register_Normal.png); 83 | background-position: top; 84 | background-size: 100% 100%; 85 | background-color: transparent; 86 | border: none; 87 | outline: none; 88 | font-family: soleil,sans-serif; 89 | font-size: 20px; 90 | text-shadow: 0 2px 7px #000; 91 | color: #fff; 92 | padding: 40px 65px; 93 | } 94 | 95 | [type=reset], [type=submit], button, html [type=button] { 96 | -webkit-appearance: button; 97 | } 98 | #root{ 99 | background: url(./walls.jpg); 100 | background-size: cover; 101 | background-position: 50%; 102 | } 103 | .zombie-container { 104 | padding-top: 0px; 105 | } 106 | .zombie-container .container{ 107 | text-align: center; 108 | max-width: 1260px; 109 | } 110 | 111 | .zombie-container .area{ 112 | background-color: rgba(0,0,0,0.5); 113 | padding: 20px; 114 | height: min-content; 115 | } 116 | 117 | .start-admin-btn:hover { 118 | -webkit-transition: all .6s ease 0s; 119 | transition: all .6s ease 0s; 120 | -webkit-filter: brightness(130%); 121 | filter: brightness(130%); 122 | } 123 | .start-admin-btn { 124 | cursor: pointer; 125 | background: url(./learn_more_btn.png); 126 | background-position: 50% 20px; 127 | background-size: 90%; 128 | background-color: transparent; 129 | background-repeat: no-repeat; 130 | border: none; 131 | outline: none; 132 | font-family: soleil,sans-serif; 133 | font-size: 20px; 134 | text-shadow: 0 2px 7px #000; 135 | color: #fff; 136 | padding: 40px 65px; 137 | } 138 | 139 | .home-card{ 140 | background-image: url(./homecard@2x.png); 141 | background-repeat: no-repeat; 142 | } 143 | .game-card { 144 | height: 42vh; 145 | width: 29vh; 146 | margin: -1rem; 147 | background-size: 100%; 148 | position: relative; 149 | transform: scale(.8); 150 | cursor: pointer; 151 | transition: transform 0.5s; 152 | } 153 | .cards{ 154 | justify-content: space-around!important; 155 | flex-wrap: wrap!important; 156 | display: flex!important; 157 | } 158 | .cards a{ 159 | display: flex!important; 160 | color: unset; 161 | } 162 | .game-card .zombie-parts{ 163 | margin-left: 0vh; 164 | margin-top: 38vh; 165 | transform: scale(1.5); 166 | } 167 | .zombie-card { 168 | position: absolute; 169 | left: 50%; 170 | top: 51vh; 171 | font-size: 40px; 172 | width: 48vh; 173 | margin-left: -8vh; 174 | text-align: center; 175 | transform: scale(1); 176 | background-color: rgba(0,0,0,.2)!important; 177 | } 178 | .card-header:first-child { 179 | border-radius: calc(.25rem - 1px) calc(.25rem - 1px) 0 0; 180 | } 181 | .hide-overflow-text { 182 | text-overflow: ellipsis; 183 | white-space: nowrap; 184 | overflow: hidden; 185 | } 186 | .bg-dark { 187 | background-color: rgba(0,0,0,.7)!important; 188 | } 189 | .game-card[active='1'] { 190 | transform: rotate(5deg); 191 | } 192 | .game-card[active='0'] { 193 | transform: rotate(0deg); 194 | transform: scale(.8); 195 | } 196 | .game-card:hover { 197 | transform: rotate(5deg); 198 | } 199 | .zombie-detail{ 200 | padding-top: 1.5rem !important; 201 | position: relative; 202 | width: 70%; 203 | min-height: 1px; 204 | padding-right: 10px; 205 | padding-left: 10px; 206 | flex-basis: 0px; 207 | flex-grow: 1; 208 | max-width: 100%; 209 | margin-top: 20vh; 210 | } 211 | .zombie-detail dl{ 212 | display: inline-block; 213 | background-color: rgba(0,0,0,.3)!important; 214 | padding: 30px 0px; 215 | } 216 | .zombie-detail dt,dd{ 217 | float:left; 218 | padding: 10px 0px; 219 | } 220 | .zombie-detail dt{ 221 | width: 30%; 222 | } 223 | .zombie-detail dd{ 224 | width: 70%; 225 | } 226 | .zombie-detail dl>:first-child,.zombie-detail dl>:last-child{ 227 | 228 | width: 100%; 229 | } 230 | .zombie-detail dl>:last-child{ 231 | margin-top: 30px; 232 | } 233 | 234 | .attack-btn:hover { 235 | -webkit-transition: all .6s ease 0s; 236 | transition: all .6s ease 0s; 237 | -webkit-filter: brightness(130%); 238 | filter: brightness(130%); 239 | } 240 | .attack-btn { 241 | cursor: pointer; 242 | background: url(./start_btn.png); 243 | background-position: top; 244 | background-size: 100%; 245 | background-color: transparent; 246 | border: none; 247 | outline: none; 248 | font-family: soleil,sans-serif; 249 | font-size: 20px; 250 | text-shadow: 0 2px 7px #000; 251 | color: #fff; 252 | height: 77px; 253 | width: 528px; 254 | margin-top: 50px; 255 | } 256 | .attack-btn span{ 257 | margin-top: 10px; 258 | display: block; 259 | 260 | } 261 | .zombieInput{ 262 | margin: 30px 0px; 263 | width: 100%; 264 | } 265 | .zombieInput input{ 266 | width: 300px; 267 | line-height: 30px; 268 | font-size: 28px; 269 | text-align: center; 270 | background-color: #000; 271 | color: #fff; 272 | padding: 10px; 273 | border: 1px solid #999; 274 | } 275 | button a,button a:hover{ 276 | color: unset!important; 277 | text-decoration: none!important; 278 | background: none; 279 | } 280 | .flex{ 281 | display: flex!important; 282 | } 283 | 284 | .zombie-attack .zombie-preview { 285 | transform: scale(0.5); 286 | } 287 | .zombie-attack .zombie-detail{ 288 | margin-top: 0px; 289 | padding-top: 0px; 290 | } 291 | .target-card{ 292 | transform: none; 293 | background-image: url(./tester-bg@2x.png); 294 | background-size: 85%; 295 | background-repeat: no-repeat; 296 | background-position: center; 297 | } 298 | .target-card:hover{ 299 | transform: none; 300 | } 301 | .modal{ 302 | position: absolute; 303 | background: rgba(0,0,0,.8); 304 | top: 25px; 305 | left: 10%; 306 | right: 10%; 307 | padding: 15px; 308 | z-index: 999; 309 | } 310 | .battelArea{ 311 | display: flex; 312 | min-height: 300px; 313 | } 314 | .battelArea .targetZombie,.battelArea .myZombie{ 315 | width: 33%; 316 | margin-top: -200px; 317 | transform: scale(0.5); 318 | } 319 | .battelArea .myZombie{ 320 | transform: rotateY(180deg) scale(0.5); 321 | } 322 | .battelArea .vs{ 323 | width: 33%; 324 | font-size: 100px; 325 | color: #fff; 326 | font-weight: bolder; 327 | line-height: 300px; 328 | } 329 | .pay-btn:hover { 330 | -webkit-transition: all .6s ease 0s; 331 | transition: all .6s ease 0s; 332 | -webkit-filter: brightness(130%); 333 | filter: brightness(130%); 334 | } 335 | .pay-btn { 336 | cursor: pointer; 337 | background: url(./learn_more_btn.png); 338 | background-position: top; 339 | background-size: 100%; 340 | background-color: transparent; 341 | border: none; 342 | outline: none; 343 | font-family: soleil,sans-serif; 344 | font-size: 20px; 345 | text-shadow: 0 2px 7px #000; 346 | color: #fff; 347 | height: 66px; 348 | width: 210px; 349 | margin-top: 50px; 350 | } 351 | .pay-btn-last { 352 | margin-top: 0px; 353 | } 354 | .pay-btn span{ 355 | margin-top: 10px; 356 | display: block; 357 | 358 | } 359 | .zombie-simulator .zombie-parts{ 360 | width: 50%; 361 | margin-top: 16vh; 362 | } 363 | .zombie-simulator .zombie-preview{ 364 | background-repeat: no-repeat; 365 | background-size: 70%; 366 | background-position: center top; 367 | } 368 | .transactionHash{ 369 | display: none; 370 | } 371 | .transactionHash[display='1']{ 372 | display: block; 373 | } 374 | .buyArea[display='0'],.createArea[display='0']{ 375 | display: none; 376 | } 377 | .buyArea[display='1'],.createArea[display='1']{ 378 | display: block; 379 | } 380 | .contract-admin dl{ 381 | display: flex; 382 | flex-wrap: wrap; 383 | } 384 | .contract-admin dt{ 385 | width: 30%; 386 | padding: 10px 0px; 387 | } 388 | .contract-admin dd{ 389 | width: 70%; 390 | font-family: auto; 391 | font-size: 16px; 392 | letter-spacing: 1px; 393 | } 394 | .contract-admin .pay-btn{ 395 | height: 40px; 396 | width: 105px; 397 | font-size: 14px; 398 | background-position: 0px 8px; 399 | background-repeat: no-repeat; 400 | margin-left: 20px; 401 | } 402 | .contract-admin .pay-btn span{ 403 | margin-top: 15px; 404 | } 405 | .lowcase{ 406 | text-transform: lowercase; 407 | } -------------------------------------------------------------------------------- /src/static/Btn_Register_Normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/Btn_Register_Normal.png -------------------------------------------------------------------------------- /src/static/Btn_Register_Over.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/Btn_Register_Over.png -------------------------------------------------------------------------------- /src/static/Index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | font-size: 1rem; 9 | font-weight: 400; 10 | line-height: 1.5; 11 | color: rgb(207, 210, 218); 12 | text-align: left; 13 | } 14 | code { 15 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 16 | monospace; 17 | } 18 | ul,li{ 19 | list-style: none; 20 | margin: 0px!important; 21 | padding: 0px; 22 | } 23 | 24 | :root { 25 | --blue: #007bff; 26 | --indigo: #6610f2; 27 | --purple: #6f42c1; 28 | --pink: #e83e8c; 29 | --red: #dc3545; 30 | --orange: #fd7e14; 31 | --yellow: #ffc107; 32 | --green: #28a745; 33 | --teal: #20c997; 34 | --cyan: #17a2b8; 35 | --white: #fff; 36 | --gray: #6c757d; 37 | --gray-dark: #343a40; 38 | --primary: #1997c6; 39 | --secondary: #6c757d; 40 | --success: #1bc98e; 41 | --info: #9f86ff; 42 | --warning: #e4d836; 43 | --danger: #e64759; 44 | --light: #f8f9fa; 45 | --dark: #1a1c22; 46 | --black: #000; 47 | --light-grey: #30343e; 48 | --shaded: rgba(0, 0, 0, 0.2); 49 | --breakpoint-xs: 0; 50 | --breakpoint-sm: 576px; 51 | --breakpoint-md: 768px; 52 | --breakpoint-lg: 992px; 53 | --breakpoint-xl: 1200px; 54 | --font-family-sans-serif: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; 55 | --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 56 | } 57 | 58 | *, 59 | ::after, 60 | ::before { 61 | box-sizing: border-box; 62 | } 63 | 64 | html { 65 | font-family: sans-serif; 66 | line-height: 1.15; 67 | text-size-adjust: 100%; 68 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 69 | } 70 | 71 | article, 72 | aside, 73 | figcaption, 74 | figure, 75 | footer, 76 | header, 77 | hgroup, 78 | main, 79 | nav, 80 | section { 81 | display: block; 82 | } 83 | 84 | [tabindex="-1"]:focus { 85 | outline: 0px !important; 86 | } 87 | 88 | hr { 89 | box-sizing: content-box; 90 | height: 0px; 91 | overflow: visible; 92 | } 93 | 94 | h1, 95 | h2, 96 | h3, 97 | h4, 98 | h5, 99 | h6 { 100 | margin-top: 0px; 101 | margin-bottom: 0.5rem; 102 | } 103 | 104 | p { 105 | margin-top: 0px; 106 | margin-bottom: 1rem; 107 | } 108 | 109 | abbr[data-original-title], 110 | abbr[title] { 111 | text-decoration: underline dotted; 112 | cursor: help; 113 | border-bottom: 0px; 114 | } 115 | 116 | address { 117 | font-style: normal; 118 | line-height: inherit; 119 | } 120 | 121 | address, 122 | dl, 123 | ol, 124 | ul { 125 | margin-bottom: 1rem; 126 | } 127 | 128 | dl, 129 | ol, 130 | ul { 131 | margin-top: 0px; 132 | } 133 | 134 | ol ol, 135 | ol ul, 136 | ul ol, 137 | ul ul { 138 | margin-bottom: 0px; 139 | } 140 | 141 | dt { 142 | font-weight: 700; 143 | } 144 | 145 | dd { 146 | margin-bottom: 0.5rem; 147 | margin-left: 0px; 148 | } 149 | 150 | blockquote { 151 | margin: 0px 0px 1rem; 152 | } 153 | 154 | dfn { 155 | font-style: italic; 156 | } 157 | 158 | b, 159 | strong { 160 | font-weight: bolder; 161 | } 162 | 163 | small { 164 | font-size: 80%; 165 | } 166 | 167 | sub, 168 | sup { 169 | position: relative; 170 | font-size: 75%; 171 | line-height: 0; 172 | vertical-align: baseline; 173 | } 174 | 175 | sub { 176 | bottom: -0.25em; 177 | } 178 | 179 | sup { 180 | top: -0.5em; 181 | } 182 | 183 | a { 184 | color: rgb(0, 123, 255); 185 | text-decoration: none; 186 | background-color: transparent; 187 | } 188 | 189 | a:hover { 190 | color: rgb(0, 86, 179); 191 | text-decoration: underline; 192 | } 193 | 194 | a:not([href]):not([tabindex]), 195 | a:not([href]):not([tabindex]):focus, 196 | a:not([href]):not([tabindex]):hover { 197 | color: inherit; 198 | text-decoration: none; 199 | } 200 | 201 | a:not([href]):not([tabindex]):focus { 202 | outline: 0px; 203 | } 204 | 205 | code, 206 | kbd, 207 | pre, 208 | samp { 209 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 210 | font-size: 1em; 211 | } 212 | 213 | figure { 214 | margin: 0px 0px 1rem; 215 | } 216 | 217 | img { 218 | border-style: none; 219 | } 220 | 221 | img, 222 | svg { 223 | vertical-align: middle; 224 | } 225 | 226 | svg { 227 | overflow: hidden; 228 | } 229 | 230 | table { 231 | border-collapse: collapse; 232 | } 233 | 234 | caption { 235 | padding-top: 0.75rem; 236 | padding-bottom: 0.75rem; 237 | color: rgb(108, 117, 125); 238 | text-align: left; 239 | caption-side: bottom; 240 | } 241 | 242 | th { 243 | text-align: inherit; 244 | } 245 | 246 | label { 247 | display: inline-block; 248 | margin-bottom: 0.5rem; 249 | } 250 | 251 | button { 252 | border-radius: 0px; 253 | } 254 | 255 | 256 | button, 257 | input, 258 | optgroup, 259 | select, 260 | textarea { 261 | margin: 0px; 262 | font-family: inherit; 263 | font-size: inherit; 264 | line-height: inherit; 265 | } 266 | 267 | button, 268 | input { 269 | overflow: visible; 270 | } 271 | 272 | button, 273 | select { 274 | text-transform: none; 275 | } 276 | 277 | [type="reset"], 278 | [type="submit"], 279 | button, 280 | html [type="button"] { 281 | -webkit-appearance: button; 282 | } 283 | 284 | input[type="checkbox"], 285 | input[type="radio"] { 286 | box-sizing: border-box; 287 | padding: 0px; 288 | } 289 | 290 | input[type="date"], 291 | input[type="datetime-local"], 292 | input[type="month"], 293 | input[type="time"] { 294 | -webkit-appearance: listbox; 295 | } 296 | 297 | textarea { 298 | overflow: auto; 299 | resize: vertical; 300 | } 301 | 302 | fieldset { 303 | min-width: 0px; 304 | padding: 0px; 305 | margin: 0px; 306 | border: 0px; 307 | } 308 | 309 | legend { 310 | display: block; 311 | width: 100%; 312 | max-width: 100%; 313 | padding: 0px; 314 | margin-bottom: 0.5rem; 315 | font-size: 1.5rem; 316 | line-height: inherit; 317 | color: inherit; 318 | white-space: normal; 319 | } 320 | 321 | progress { 322 | vertical-align: baseline; 323 | } 324 | 325 | [type="number"]::-webkit-inner-spin-button, 326 | [type="number"]::-webkit-outer-spin-button { 327 | height: auto; 328 | } 329 | 330 | [type="search"] { 331 | outline-offset: -2px; 332 | -webkit-appearance: none; 333 | } 334 | 335 | [type="search"]::-webkit-search-cancel-button, 336 | [type="search"]::-webkit-search-decoration { 337 | -webkit-appearance: none; 338 | } 339 | 340 | ::-webkit-file-upload-button { 341 | font: inherit; 342 | -webkit-appearance: button; 343 | } 344 | 345 | output { 346 | display: inline-block; 347 | } 348 | 349 | summary { 350 | display: list-item; 351 | cursor: pointer; 352 | } 353 | 354 | template { 355 | display: none; 356 | } 357 | 358 | [hidden] { 359 | display: none !important; 360 | } 361 | 362 | hr { 363 | margin-top: 1rem; 364 | margin-bottom: 1rem; 365 | border-width: 1px 0px 0px; 366 | border-right-style: initial; 367 | border-bottom-style: initial; 368 | border-left-style: initial; 369 | border-right-color: initial; 370 | border-bottom-color: initial; 371 | border-left-color: initial; 372 | border-image: initial; 373 | border-top-style: solid; 374 | border-top-color: rgb(67, 72, 87); 375 | } 376 | 377 | code { 378 | font-size: 87.5%; 379 | color: rgb(232, 62, 140); 380 | word-break: break-word; 381 | } 382 | 383 | a>code { 384 | color: inherit; 385 | } 386 | 387 | kbd { 388 | padding: 0.2rem 0.4rem; 389 | font-size: 87.5%; 390 | color: rgb(255, 255, 255); 391 | background-color: rgb(33, 37, 41); 392 | border-radius: 0.2rem; 393 | } 394 | 395 | kbd kbd { 396 | padding: 0px; 397 | font-size: 100%; 398 | font-weight: 700; 399 | } 400 | 401 | pre { 402 | display: block; 403 | font-size: 87.5%; 404 | color: rgb(33, 37, 41); 405 | } 406 | 407 | pre code { 408 | font-size: inherit; 409 | color: inherit; 410 | word-break: normal; 411 | } -------------------------------------------------------------------------------- /src/static/ZombiePreview.css: -------------------------------------------------------------------------------- 1 | 2 | .zombie-parts-bin-component { 3 | min-height: 100%; 4 | } 5 | 6 | .zombie-preview { 7 | height: 84vh; 8 | width: 47vh; 9 | background-size: cover; 10 | background-image: url(./tester-bg@2x.png) 11 | } 12 | 13 | .zombie-char { 14 | position: relative; 15 | } 16 | 17 | .zombie-parts { 18 | position: relative; 19 | margin-left: -2vh; 20 | margin-top: 31vh; 21 | } 22 | 23 | .zombie-loading { 24 | background: url(./zombierun.png) 0; 25 | background-size: cover; 26 | height: 287px; 27 | width: 192px; 28 | position: absolute; 29 | left: 16vh; 30 | animation: play-data-v-3614808b .7s steps(24) infinite; 31 | } 32 | 33 | @keyframes play-data-v-3614808b { 34 | to { 35 | background-position: -4608px 36 | } 37 | } 38 | 39 | .zombie-parts { 40 | position: relative; 41 | margin-left: -2vh; 42 | margin-top: 31vh 43 | } 44 | 45 | .zombie-parts .head { 46 | width: 28vh; 47 | position: absolute; 48 | left: 13vh; 49 | top: -4vh 50 | } 51 | 52 | .zombie-parts .eye { 53 | width: 13vh; 54 | position: absolute; 55 | left: 23vh; 56 | top: 8vh; 57 | } 58 | 59 | .zombie-parts .shirt { 60 | width: 13vh; 61 | position: absolute; 62 | left: 15.6vh; 63 | top: 13vh 64 | } 65 | 66 | .mouth { 67 | width: 6vh; 68 | position: absolute; 69 | left: 26.6vh; 70 | top: 15vh 71 | } 72 | 73 | .torso { 74 | width: 13vh; 75 | position: absolute; 76 | left: 15.6vh; 77 | top: 13vh 78 | } 79 | 80 | .left-thigh { 81 | width: 6vh; 82 | position: absolute; 83 | left: 17.3vh; 84 | top: 22vh 85 | } 86 | 87 | .right-thigh { 88 | width: 6vh; 89 | position: absolute; 90 | left: 20.4vh; 91 | top: 22vh 92 | } 93 | 94 | .cat-legs { 95 | width: 10vh; 96 | position: absolute; 97 | left: 15.4vh; 98 | top: 18vh 99 | } 100 | 101 | .left-hand { 102 | width: 4vh; 103 | position: absolute; 104 | left: 24.3vh; 105 | top: 19vh 106 | } 107 | 108 | .right-hand { 109 | width: 4vh; 110 | position: absolute; 111 | left: 28.4vh; 112 | top: 19vh 113 | } 114 | 115 | .left-forearm { 116 | width: 4vh; 117 | position: absolute; 118 | left: 22.3vh; 119 | top: 20vh 120 | } 121 | 122 | .right-forearm { 123 | width: 4vh; 124 | position: absolute; 125 | left: 26.4vh; 126 | top: 20vh 127 | } 128 | 129 | .left-upper-arm { 130 | width: 6vh; 131 | position: absolute; 132 | left: 19.3vh; 133 | top: 16vh 134 | } 135 | 136 | .right-upper-arm { 137 | width: 6vh; 138 | position: absolute; 139 | left: 23.4vh; 140 | top: 16vh 141 | } 142 | 143 | .left-leg { 144 | width: 4vh; 145 | position: absolute; 146 | left: 18.3vh; 147 | top: 27vh 148 | } 149 | 150 | .right-leg { 151 | width: 3.3vh; 152 | position: absolute; 153 | left: 22.3vh; 154 | top: 27.6vh 155 | } 156 | 157 | .left-feet { 158 | width: 4vh; 159 | position: absolute; 160 | left: 18.3vh; 161 | top: 30vh 162 | } 163 | 164 | .right-feet { 165 | width: 3.3vh; 166 | position: absolute; 167 | left: 22.3vh; 168 | top: 30.3vh 169 | } 170 | 171 | .zombie-name { 172 | text-transform: uppercase; 173 | text-shadow: 5px 5px 5px rgba(0, 0, 0, .2) 174 | } 175 | 176 | 177 | .zombie-name { 178 | font-weight: 700 179 | } 180 | 181 | .home-card .zombie-char { 182 | left: -6vh; 183 | top: -27vh; 184 | transform: scale(0.4); 185 | } 186 | 187 | 188 | .card[data-v-ab64d37c] .card-header { 189 | background: transparent; 190 | } 191 | 192 | input[type="range"] { 193 | width: 100%; 194 | } 195 | 196 | a[data-v-2133ad68] { 197 | text-decoration: none; 198 | } 199 | 200 | ul[data-v-4a0b5f72] { 201 | overflow-y: scroll; 202 | } 203 | 204 | ul[data-v-4a0b5f72]::-webkit-scrollbar { 205 | display: none; 206 | } 207 | 208 | .form-control.is-invalid, 209 | .form-control.is-valid, 210 | .was-validated .form-control:invalid, 211 | .was-validated .form-control:valid { 212 | background-position: right calc(0.375em + 0.1875rem) center; 213 | } 214 | 215 | input[type="color"].form-control { 216 | height: calc((1.5em + 0.75rem) + 2px); 217 | padding: 0.125rem 0.25rem; 218 | } 219 | 220 | .input-group-sm input[type="color"].form-control, 221 | input[type="color"].form-control.form-control-sm { 222 | height: calc((1.5em + 0.5rem) + 2px); 223 | padding: 0.125rem 0.25rem; 224 | } 225 | 226 | .input-group-lg input[type="color"].form-control, 227 | input[type="color"].form-control.form-control-lg { 228 | height: calc((1.5em + 1rem) + 2px); 229 | padding: 0.125rem 0.25rem; 230 | } 231 | 232 | input[type="color"].form-control:disabled { 233 | background-color: rgb(173, 181, 189); 234 | opacity: 0.65; 235 | } 236 | 237 | .input-group>.custom-range { 238 | position: relative; 239 | flex: 1 1 auto; 240 | width: 1%; 241 | margin-bottom: 0px; 242 | } 243 | 244 | .input-group>.custom-file+.custom-range, 245 | .input-group>.custom-range+.custom-file, 246 | .input-group>.custom-range+.custom-range, 247 | .input-group>.custom-range+.custom-select, 248 | .input-group>.custom-range+.form-control, 249 | .input-group>.custom-range+.form-control-plaintext, 250 | .input-group>.custom-select+.custom-range, 251 | .input-group>.form-control+.custom-range, 252 | .input-group>.form-control-plaintext+.custom-range { 253 | margin-left: -1px; 254 | } 255 | 256 | .input-group>.custom-range:focus { 257 | z-index: 3; 258 | } 259 | 260 | .input-group>.custom-range:not(:last-child) { 261 | border-top-right-radius: 0px; 262 | border-bottom-right-radius: 0px; 263 | } 264 | 265 | .input-group>.custom-range:not(:first-child) { 266 | border-top-left-radius: 0px; 267 | border-bottom-left-radius: 0px; 268 | } 269 | 270 | .input-group>.custom-range { 271 | padding: 0px 0.75rem; 272 | background-color: rgb(255, 255, 255); 273 | background-clip: padding-box; 274 | border: 1px solid rgb(206, 212, 218); 275 | height: calc((1.5em + 0.75rem) + 2px); 276 | border-radius: 0.25rem; 277 | transition: border-color 0.15s ease-in-out 0s, box-shadow 0.15s ease-in-out 0s; 278 | } 279 | 280 | .input-group>.custom-range:focus { 281 | color: rgb(73, 80, 87); 282 | background-color: rgb(255, 255, 255); 283 | border-color: rgb(128, 189, 255); 284 | outline: 0px; 285 | box-shadow: rgba(0, 123, 255, 0.25) 0px 0px 0px 0.2rem; 286 | } 287 | 288 | .input-group>.custom-range:disabled, 289 | .input-group>.custom-range[readonly] { 290 | background-color: rgb(233, 236, 239); 291 | } 292 | 293 | .input-group-lg>.custom-range { 294 | height: calc((1.5em + 1rem) + 2px); 295 | padding: 0px 1rem; 296 | border-radius: 0.3rem; 297 | } 298 | 299 | .input-group-sm>.custom-range { 300 | height: calc((1.5em + 0.5rem) + 2px); 301 | padding: 0px 0.5rem; 302 | border-radius: 0.2rem; 303 | } 304 | 305 | .input-group .custom-range.is-valid, 306 | .was-validated .input-group .custom-range:valid { 307 | border-color: rgb(40, 167, 69); 308 | } 309 | 310 | .input-group .custom-range.is-valid:focus, 311 | .was-validated .input-group .custom-range:valid:focus { 312 | border-color: rgb(40, 167, 69); 313 | box-shadow: rgba(40, 167, 69, 0.25) 0px 0px 0px 0.2rem; 314 | } 315 | 316 | .custom-range.is-valid:focus::-webkit-slider-thumb, 317 | .was-validated .custom-range:valid:focus::-webkit-slider-thumb { 318 | box-shadow: rgb(255, 255, 255) 0px 0px 0px 1px, rgb(155, 231, 172) 0px 0px 0px 0.2rem; 319 | } 320 | 321 | .custom-range.is-valid::-webkit-slider-thumb, 322 | .was-validated .custom-range:valid::-webkit-slider-thumb { 323 | background-color: rgb(40, 167, 69); 324 | background-image: none; 325 | } 326 | 327 | .custom-range.is-valid::-webkit-slider-thumb:active, 328 | .was-validated .custom-range:valid::-webkit-slider-thumb:active { 329 | background-color: rgb(155, 231, 172); 330 | background-image: none; 331 | } 332 | 333 | .custom-range.is-valid::-webkit-slider-runnable-track, 334 | .was-validated .custom-range:valid::-webkit-slider-runnable-track { 335 | background-color: rgba(40, 167, 69, 0.35); 336 | } 337 | 338 | .custom-range.is-valid~.valid-feedback, 339 | .custom-range.is-valid~.valid-tooltip, 340 | .was-validated .custom-range:valid~.valid-feedback, 341 | .was-validated .custom-range:valid~.valid-tooltip { 342 | display: block; 343 | } 344 | 345 | .input-group .custom-range.is-invalid, 346 | .was-validated .input-group .custom-range:invalid { 347 | border-color: rgb(220, 53, 69); 348 | } 349 | 350 | .input-group .custom-range.is-invalid:focus, 351 | .was-validated .input-group .custom-range:invalid:focus { 352 | border-color: rgb(220, 53, 69); 353 | box-shadow: rgba(220, 53, 69, 0.25) 0px 0px 0px 0.2rem; 354 | } 355 | 356 | .custom-range.is-invalid:focus::-webkit-slider-thumb, 357 | .was-validated .custom-range:invalid:focus::-webkit-slider-thumb { 358 | box-shadow: rgb(255, 255, 255) 0px 0px 0px 1px, rgb(246, 205, 209) 0px 0px 0px 0.2rem; 359 | } 360 | 361 | .custom-range.is-invalid::-webkit-slider-thumb, 362 | .was-validated .custom-range:invalid::-webkit-slider-thumb { 363 | background-color: rgb(220, 53, 69); 364 | background-image: none; 365 | } 366 | 367 | .custom-range.is-invalid::-webkit-slider-thumb:active, 368 | .was-validated .custom-range:invalid::-webkit-slider-thumb:active { 369 | background-color: rgb(246, 205, 209); 370 | background-image: none; 371 | } 372 | 373 | .custom-range.is-invalid::-webkit-slider-runnable-track, 374 | .was-validated .custom-range:invalid::-webkit-slider-runnable-track { 375 | background-color: rgba(220, 53, 69, 0.35); 376 | } 377 | 378 | .custom-range.is-invalid~.invalid-feedback, 379 | .custom-range.is-invalid~.invalid-tooltip, 380 | .was-validated .custom-range:invalid~.invalid-feedback, 381 | .was-validated .custom-range:invalid~.invalid-tooltip { 382 | display: block; 383 | } 384 | 385 | 386 | .row { 387 | display: flex; 388 | flex-wrap: wrap; 389 | padding: 10px 30px; 390 | } 391 | 392 | .col { 393 | position: relative; 394 | width: 100%; 395 | min-height: 1px; 396 | padding-right: 10px; 397 | padding-left: 10px; 398 | flex-basis: 0px; 399 | flex-grow: 1; 400 | max-width: 100%; 401 | } 402 | 403 | .form-control { 404 | display: block; 405 | width: 100%; 406 | height: calc(2.25rem + 2px); 407 | padding: 0.375rem 0.75rem; 408 | font-size: 1rem; 409 | line-height: 1.5; 410 | color: rgb(255, 255, 255); 411 | background-color: rgb(67, 72, 87); 412 | background-clip: padding-box; 413 | border: 1px solid rgb(67, 72, 87); 414 | border-radius: 0.25rem; 415 | transition: border-color 0.15s ease-in-out 0s, box-shadow 0.15s ease-in-out 0s; 416 | } 417 | 418 | .form-control:focus { 419 | color: rgb(73, 80, 87); 420 | background-color: rgb(255, 255, 255); 421 | border-color: rgb(128, 189, 255); 422 | outline: 0px; 423 | box-shadow: rgba(0, 123, 255, 0.25) 0px 0px 0px 0.2rem; 424 | } 425 | 426 | .form-control::-webkit-input-placeholder { 427 | color: rgb(108, 117, 125); 428 | opacity: 1; 429 | } 430 | 431 | .form-control::placeholder { 432 | color: rgb(108, 117, 125); 433 | opacity: 1; 434 | } 435 | 436 | .form-control:disabled, 437 | .form-control[readonly] { 438 | background-color: rgb(48, 52, 62); 439 | opacity: 1; 440 | } 441 | 442 | select.form-control[multiple], 443 | select.form-control[size], 444 | textarea.form-control { 445 | height: auto; 446 | } 447 | 448 | .form-group { 449 | margin-bottom: 1rem; 450 | } 451 | 452 | .form-row>.col, 453 | .form-row>[class*="col-"] { 454 | padding-right: 5px; 455 | padding-left: 5px; 456 | } 457 | 458 | .custom-select.is-valid, 459 | .form-control.is-valid, 460 | .was-validated .custom-select:valid, 461 | .was-validated .form-control:valid { 462 | border-color: rgb(40, 167, 69); 463 | } 464 | 465 | .custom-select.is-valid:focus, 466 | .form-control.is-valid:focus, 467 | .was-validated .custom-select:valid:focus, 468 | .was-validated .form-control:valid:focus { 469 | border-color: rgb(40, 167, 69); 470 | box-shadow: rgba(40, 167, 69, 0.25) 0px 0px 0px 0.2rem; 471 | } 472 | 473 | .custom-select.is-valid~.valid-feedback, 474 | .custom-select.is-valid~.valid-tooltip, 475 | .form-control-file.is-valid~.valid-feedback, 476 | .form-control-file.is-valid~.valid-tooltip, 477 | .form-control.is-valid~.valid-feedback, 478 | .form-control.is-valid~.valid-tooltip, 479 | .was-validated .custom-select:valid~.valid-feedback, 480 | .was-validated .custom-select:valid~.valid-tooltip, 481 | .was-validated .form-control-file:valid~.valid-feedback, 482 | .was-validated .form-control-file:valid~.valid-tooltip, 483 | .was-validated .form-control:valid~.valid-feedback, 484 | .was-validated .form-control:valid~.valid-tooltip { 485 | display: block; 486 | } 487 | 488 | .custom-select.is-invalid, 489 | .form-control.is-invalid, 490 | .was-validated .custom-select:invalid, 491 | .was-validated .form-control:invalid { 492 | border-color: rgb(220, 53, 69); 493 | } 494 | 495 | .custom-select.is-invalid:focus, 496 | .form-control.is-invalid:focus, 497 | .was-validated .custom-select:invalid:focus, 498 | .was-validated .form-control:invalid:focus { 499 | border-color: rgb(220, 53, 69); 500 | box-shadow: rgba(220, 53, 69, 0.25) 0px 0px 0px 0.2rem; 501 | } 502 | 503 | .custom-select.is-invalid~.invalid-feedback, 504 | .custom-select.is-invalid~.invalid-tooltip, 505 | .form-control-file.is-invalid~.invalid-feedback, 506 | .form-control-file.is-invalid~.invalid-tooltip, 507 | .form-control.is-invalid~.invalid-feedback, 508 | .form-control.is-invalid~.invalid-tooltip, 509 | .was-validated .custom-select:invalid~.invalid-feedback, 510 | .was-validated .custom-select:invalid~.invalid-tooltip, 511 | .was-validated .form-control-file:invalid~.invalid-feedback, 512 | .was-validated .form-control-file:invalid~.invalid-tooltip, 513 | .was-validated .form-control:invalid~.invalid-feedback, 514 | .was-validated .form-control:invalid~.invalid-tooltip { 515 | display: block; 516 | } 517 | 518 | .input-group>.custom-file, 519 | .input-group>.custom-select, 520 | .input-group>.form-control { 521 | position: relative; 522 | flex: 1 1 auto; 523 | width: 1%; 524 | margin-bottom: 0px; 525 | } 526 | 527 | .input-group>.custom-file+.custom-file, 528 | .input-group>.custom-file+.custom-select, 529 | .input-group>.custom-file+.form-control, 530 | .input-group>.custom-select+.custom-file, 531 | .input-group>.custom-select+.custom-select, 532 | .input-group>.custom-select+.form-control, 533 | .input-group>.form-control+.custom-file, 534 | .input-group>.form-control+.custom-select, 535 | .input-group>.form-control+.form-control { 536 | margin-left: -1px; 537 | } 538 | 539 | .input-group>.custom-file .custom-file-input:focus~.custom-file-label, 540 | .input-group>.custom-select:focus, 541 | .input-group>.form-control:focus { 542 | z-index: 3; 543 | } 544 | 545 | .input-group>.custom-select:not(:last-child), 546 | .input-group>.form-control:not(:last-child) { 547 | border-top-right-radius: 0px; 548 | border-bottom-right-radius: 0px; 549 | } 550 | 551 | .input-group>.custom-select:not(:first-child), 552 | .input-group>.form-control:not(:first-child) { 553 | border-top-left-radius: 0px; 554 | border-bottom-left-radius: 0px; 555 | } 556 | 557 | .input-group-lg>.form-control, 558 | .input-group-lg>.input-group-append>.btn, 559 | .input-group-lg>.input-group-append>.input-group-text, 560 | .input-group-lg>.input-group-prepend>.btn, 561 | .input-group-lg>.input-group-prepend>.input-group-text { 562 | height: calc(2.875rem + 2px); 563 | padding: 0.5rem 1rem; 564 | font-size: 1.25rem; 565 | line-height: 1.5; 566 | border-radius: 0.3rem; 567 | } 568 | 569 | .input-group-sm>.form-control, 570 | .input-group-sm>.input-group-append>.btn, 571 | .input-group-sm>.input-group-append>.input-group-text, 572 | .input-group-sm>.input-group-prepend>.btn, 573 | .input-group-sm>.input-group-prepend>.input-group-text { 574 | height: calc(1.8125rem + 2px); 575 | padding: 0.25rem 0.5rem; 576 | font-size: 0.875rem; 577 | line-height: 1.5; 578 | border-radius: 0.2rem; 579 | } 580 | 581 | .custom-range { 582 | width: 100%; 583 | padding-left: 0px; 584 | background-color: transparent; 585 | -webkit-appearance: none; 586 | } 587 | 588 | .custom-range:focus { 589 | outline: none; 590 | } 591 | 592 | .custom-range:focus::-webkit-slider-thumb { 593 | box-shadow: rgb(255, 255, 255) 0px 0px 0px 1px, rgba(0, 123, 255, 0.25) 0px 0px 0px 0.2rem; 594 | } 595 | 596 | .custom-range::-webkit-slider-thumb { 597 | width: 1rem; 598 | height: 1rem; 599 | margin-top: -0.25rem; 600 | background-color: rgb(0, 123, 255); 601 | border: 0px; 602 | border-radius: 1rem; 603 | transition: background-color 0.15s ease-in-out 0s, border-color 0.15s ease-in-out 0s, box-shadow 0.15s ease-in-out 0s; 604 | -webkit-appearance: none; 605 | } 606 | 607 | .custom-range::-webkit-slider-thumb:active { 608 | background-color: rgb(179, 215, 255); 609 | } 610 | 611 | .custom-range::-webkit-slider-runnable-track { 612 | width: 100%; 613 | height: 0.5rem; 614 | color: transparent; 615 | cursor: pointer; 616 | background-color: rgb(222, 226, 230); 617 | border-color: transparent; 618 | border-radius: 1rem; 619 | } 620 | 621 | .card-header { 622 | padding: 0.75rem 1.25rem; 623 | margin-bottom: 0px; 624 | background-color: rgba(0, 0, 0, 0.03); 625 | border-bottom: 1px solid rgba(0, 0, 0, 0.125); 626 | } 627 | 628 | .card-header:first-child { 629 | border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0px 0px; 630 | } 631 | 632 | .card-header+.list-group .list-group-item:first-child { 633 | border-top: 0px; 634 | } 635 | 636 | .accordion .card:not(:first-of-type) .card-header:first-child { 637 | border-radius: 0px; 638 | } 639 | 640 | .bg-dark { 641 | background-color: rgb(26, 28, 34) !important; 642 | } 643 | 644 | a.bg-dark:focus, 645 | a.bg-dark:hover, 646 | button.bg-dark:focus, 647 | button.bg-dark:hover { 648 | background-color: rgb(4, 4, 5) !important; 649 | } 650 | 651 | .mt-2, 652 | .my-2 { 653 | margin-top: 0.5rem !important; 654 | } 655 | 656 | .pt-4, 657 | .py-4 { 658 | padding-top: 1.5rem !important; 659 | } 660 | 661 | html { 662 | position: relative; 663 | min-height: 100%; 664 | } 665 | 666 | h1, 667 | h2 { 668 | line-height: 1.5; 669 | margin-top: 10px; 670 | margin-bottom: 10px; 671 | } 672 | 673 | h2, 674 | h3, 675 | h4, 676 | h5 { 677 | margin-top: 30px; 678 | } 679 | 680 | .hide { 681 | visibility: hidden; 682 | } 683 | 684 | .zombie-preview { 685 | position: relative; 686 | } 687 | 688 | .share-page .name-input { 689 | background-color: rgb(255, 255, 255); 690 | color: rgb(51, 51, 51); 691 | max-width: 500px; 692 | } 693 | 694 | .zombie-parts .eye, 695 | .zombie-parts .head, 696 | .zombie-parts .shirt { 697 | display: none; 698 | } 699 | 700 | .zombie-parts.eye-visible-1 .eye-part-1, 701 | .zombie-parts.eye-visible-2 .eye-part-2, 702 | .zombie-parts.eye-visible-3 .eye-part-3, 703 | .zombie-parts.eye-visible-4 .eye-part-4, 704 | .zombie-parts.eye-visible-5 .eye-part-5, 705 | .zombie-parts.eye-visible-6 .eye-part-6, 706 | .zombie-parts.eye-visible-7 .eye-part-7, 707 | .zombie-parts.eye-visible-8 .eye-part-8, 708 | .zombie-parts.eye-visible-9 .eye-part-9, 709 | .zombie-parts.eye-visible-10 .eye-part-10, 710 | .zombie-parts.eye-visible-11 .eye-part-11, 711 | .zombie-parts.head-visible-1 .head-part-1, 712 | .zombie-parts.head-visible-2 .head-part-2, 713 | .zombie-parts.head-visible-3 .head-part-3, 714 | .zombie-parts.head-visible-4 .head-part-4, 715 | .zombie-parts.head-visible-5 .head-part-5, 716 | .zombie-parts.head-visible-6 .head-part-6, 717 | .zombie-parts.head-visible-7 .head-part-7, 718 | .zombie-parts.head-visible-8 .head-part-8, 719 | .zombie-parts.shirt-visible-1 .shirt-part-1, 720 | .zombie-parts.shirt-visible-2 .shirt-part-2, 721 | .zombie-parts.shirt-visible-3 .shirt-part-3, 722 | .zombie-parts.shirt-visible-4 .shirt-part-4, 723 | .zombie-parts.shirt-visible-5 .shirt-part-5, 724 | .zombie-parts.shirt-visible-6 .shirt-part-6 { 725 | display: inherit; 726 | } 727 | 728 | .head-part-8 { 729 | z-index: 9; 730 | } 731 | 732 | .vb>.vb-dragger { 733 | z-index: 5; 734 | width: 12px; 735 | right: 0px; 736 | } 737 | 738 | .form-control { 739 | box-shadow: none; 740 | } 741 | 742 | .has-error .form-control, 743 | .has-error .form-control:focus, 744 | .has-success .form-control, 745 | .has-success .form-control:focus, 746 | .has-warning .form-control, 747 | .has-warning .form-control:focus { 748 | border-color: rgb(67, 72, 87); 749 | box-shadow: none; 750 | } 751 | 752 | @media (min-width: 992px) { 753 | .mt-lg-5, 754 | .my-lg-5 { 755 | margin-top: 3rem!important; 756 | } 757 | .pt-lg-5, 758 | .py-lg-5 { 759 | padding-top: 3rem!important; 760 | } 761 | } -------------------------------------------------------------------------------- /src/static/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/bg.png -------------------------------------------------------------------------------- /src/static/catlegs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/catlegs.png -------------------------------------------------------------------------------- /src/static/get_start_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/get_start_btn.png -------------------------------------------------------------------------------- /src/static/homecard@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/homecard@2x.png -------------------------------------------------------------------------------- /src/static/index_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/index_bg.png -------------------------------------------------------------------------------- /src/static/learn_more_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/learn_more_btn.png -------------------------------------------------------------------------------- /src/static/lesson-complete-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/lesson-complete-bg.png -------------------------------------------------------------------------------- /src/static/lesson_number_completed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/lesson_number_completed.png -------------------------------------------------------------------------------- /src/static/lesson_panel_complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/lesson_panel_complete.png -------------------------------------------------------------------------------- /src/static/start_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/start_btn.png -------------------------------------------------------------------------------- /src/static/tester-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/tester-bg@2x.png -------------------------------------------------------------------------------- /src/static/walls.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/walls.jpg -------------------------------------------------------------------------------- /src/static/zombierun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fankouzu/my-crypto-zombie/79ede64adf382ac861897f1631599f4242db932c/src/static/zombierun.png --------------------------------------------------------------------------------