diff --git a/packages/contracts/src/systems/BattleSystem.sol b/packages/contracts/src/systems/BattleSystem.sol index 6a3fba1f..facdecbe 100644 --- a/packages/contracts/src/systems/BattleSystem.sol +++ b/packages/contracts/src/systems/BattleSystem.sol @@ -10,20 +10,7 @@ import { GAME_CONFIG_KEY, BATTLE_CONFIG_KEY } from "../Constants.sol"; contract BattleSystem is System { - function joinBattlefield(address _user) public { - // 加入战区,用户实际上是送到原点,状态改为探索中 - // User storage player = Player[_user]; - PlayerState playerState = Player.getState(_user); - require( - playerState == PlayerState.Preparing || playerState == PlayerState.Idle, - "You should in preparing state" - ); - //实际上是送到原点//TODO通过常数设置原点参数 - Player.setX(_user, GameConfig.getOriginX(GAME_CONFIG_KEY)); - Player.setY(_user, GameConfig.getOriginY(GAME_CONFIG_KEY)); - BattleConfig.pushBattlefieldPlayers(BATTLE_CONFIG_KEY, _user); - Player.setState(_user, PlayerState.Exploring); - } + function checkBattlePlayer(BattleListData memory battle, BattleState _battleState) internal view { // BattleListData memory battle = BattleList.get(_battleId); @@ -283,9 +270,27 @@ contract BattleSystem is System { } } + function getAttackResult( + uint256 _hp, + uint256 _attackPower + ) internal pure returns (uint256) { + // TODO 后期添加防御力抵消对方的攻击力 + if (_attackPower > _hp) { + return 0; + } + return _hp - _attackPower; + } + function initUserHP(address _user) public pure returns (uint256) { return 400; } + function raiseUserHP( + uint256 _targetHP, + uint256 _percent, + address _user + ) public { + Player.setHP(_user, (_targetHP * _percent) / 100); + } function loseGame(address _looser, address _winner) internal { // 游戏失败,将用户脱离战区,血量回满 @@ -311,24 +316,6 @@ contract BattleSystem is System { } - function outBattlefield(address _user) internal { - // 脱离战区,则将用户血量回满,坐标不变,状态改为准备中 - require( - Player.getState(_user) == PlayerState.Exploring, - "You should in exploring state" - ); - - Player.setHP(_user, initUserHP(_user)); - - for (uint256 i; i < BattleConfig.lengthBattlefieldPlayers(BATTLE_CONFIG_KEY); i++) { - if (BattleConfig.getItemBattlefieldPlayers(BATTLE_CONFIG_KEY, i) == _user) { - BattleConfig.updateBattlefieldPlayers(BATTLE_CONFIG_KEY, i, BattleConfig.getItemBattlefieldPlayers(BATTLE_CONFIG_KEY, BattleConfig.lengthBattlefieldPlayers(BATTLE_CONFIG_KEY) - 1)); - BattleConfig.popBattlefieldPlayers(BATTLE_CONFIG_KEY); - break; - } - } - Player.setState(_user, PlayerState.Preparing); - } function goHome() external { @@ -346,5 +333,45 @@ contract BattleSystem is System { outBattlefield(_msgSender()); } + + + function getAttackPower( + Buff _myBuff, + Buff _targetBuff, + uint256 _attackPower + ) internal pure returns (uint256) { + // TODO 后期添加防御力抵消对方的攻击力 + if (compareBuff(_myBuff, _targetBuff) == 0) { + return (_attackPower * 7) / 10; + } + if (compareBuff(_myBuff, _targetBuff) == 2) { + return (_attackPower * 13) / 10; + } + + return _attackPower; + } + + + function compareBuff( + Buff _myBuff, + Buff _targetBuff + ) internal pure returns (uint256) { + // 0表示失败,1表示相当,2表示胜利 + if ( + (_myBuff == Buff.Water && _targetBuff == Buff.Fire) || + (_myBuff == Buff.Wind && _targetBuff == Buff.Water) || + (_myBuff == Buff.Fire && _targetBuff == Buff.Wind) + ) { + return 2; + } + if ( + (_myBuff == Buff.Fire && _targetBuff == Buff.Water) || + (_myBuff == Buff.Water && _targetBuff == Buff.Wind) || + (_myBuff == Buff.Wind && _targetBuff == Buff.Fire) + ) { + return 0; + } + return 1; + } } \ No newline at end of file diff --git a/packages/contracts/src/systems/GameSystem.sol b/packages/contracts/src/systems/GameSystem.sol index 6a1e024c..a770ac3c 100644 --- a/packages/contracts/src/systems/GameSystem.sol +++ b/packages/contracts/src/systems/GameSystem.sol @@ -2,127 +2,42 @@ pragma solidity >=0.8.0; import { System } from "@latticexyz/world/src/System.sol"; -import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; +import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; +import { Game } from "../codegen/Tables.sol"; import { BattleState, Buff, PlayerState } from "../codegen/Types.sol"; -import { GameConfig, BattleConfig, BattleListData, BattleList, Player, PlayerData, - PlayerLocationLock, BoxList, BoxListData} from "../codegen/Tables.sol"; +import { BattleListData, BattleList, Player, PlayerData, PlayerLocationLock, BoxListData, BoxList, RandomList, RandomListData } from "../codegen/Tables.sol"; import { Move } from "./Common.sol"; -import { GAME_CONFIG_KEY, BATTLE_CONFIG_KEY } from "../Constants.sol"; -import { CommonUtils } from "./library/CommonUtils.sol"; - contract GameSystem is System { - event MoveEvent(address indexed player, uint16 x, uint16 y); - event AttackStart(address player, address target); + bytes32 constant GAME_KEY = keccak256("Game-Key"); + + event NewRandom(uint256 randomId, address author); + event MoveEvent(address indexed player, uint16 x, uint16 y); + event AttackStart(address player, address target); + + + + + - modifier CheckContinuity(Move[] memory moveList) { - // 验证行走轨迹合法且连续 - uint8 prefer = 1; - for (uint256 i; i < moveList.length; i++) { - - uint16 x2 = i > 0 ? moveList[i - 1].x : Player.getX(_msgSender()); - uint16 y2 = i > 0 ? moveList[i - 1].y : Player.getY(_msgSender()); - require( - CommonUtils.isNear(moveList[i].x, x2, moveList[i].y, y2), - "invalied move" - ); - // 判断用户每一个移动连续性以及合法性,不能超出1格, 不能斜着走,不能原地踏步 - Move memory info = moveList[i]; - //prefer的意思是可以通行 - bytes32 leaf = keccak256( - abi.encodePacked(info.x, ",", info.y, ",", prefer) - ); - bool isValidLeaf = MerkleProof.verify(info.proof, GameConfig.getMerkleRoot(GAME_CONFIG_KEY), leaf); - require(isValidLeaf, "bad position"); - } - _; - } - - function move(Move[] memory moveList) external CheckContinuity(moveList) { - // 限制移动速度,每次移动需要间隔一定时间 - require(PlayerLocationLock.get(_msgSender()) == 0, "You are locked"); - require( - moveList.length > 0 && moveList.length <= BattleConfig.getMaxMoveDistance(BATTLE_CONFIG_KEY), - "invalid move distance" - ); - - Player.setX(_msgSender(), moveList[moveList.length - 1].x); - Player.setY(_msgSender(), moveList[moveList.length - 1].y); - - emit MoveEvent( - _msgSender(), - moveList[moveList.length - 1].x, - moveList[moveList.length - 1].y - ); - } - - - function battleInvitation( - address _targetAddress, - Move[] memory moveList - ) external CheckContinuity(moveList) { - // 攻击,首先确定地图x,y上有具体用户,其次确定用户之间最短距离proof为10 - // 需要考虑一个格子上有多个用户的情况//一个格子只能有一个人 - // 判断对战双方的状态是否是Exploring - - require( - moveList.length > 0 && moveList.length <= BattleConfig.getMaxAttackzDistance(BATTLE_CONFIG_KEY), - "invalid attack distance" - ); - - require( - Player.getState(_msgSender()) == PlayerState.Exploring && - Player.getState(_targetAddress) == PlayerState.Exploring, - "Each player must be in exploring state" - ); - require( - Player.getX(_targetAddress) == moveList[moveList.length - 1].x && - Player.getY(_targetAddress) == moveList[moveList.length - 1].y, - "Target must be in the end of continuity" - ); - - Player.setState(_msgSender(), PlayerState.Attacking); - Player.setState(_targetAddress, PlayerState.Attacking); - - uint256 battleId = GameConfig.getBattleId(GAME_CONFIG_KEY); - BattleList.setAttacker(battleId, _msgSender()); - BattleList.setDefender(battleId, _targetAddress); - BattleList.setTimestamp(battleId, block.timestamp); - BattleList.setAttackerHP(battleId, Player.getHP(_msgSender())); - BattleList.setDefenderHP(battleId, Player.getHP(_targetAddress)); - - // battleId++; - GameConfig.setBattleId(GAME_CONFIG_KEY, battleId + 1); - - emit AttackStart(_msgSender(), _targetAddress); - } - - - - - // function settleBattle() external {} - - // function createLootBox() external {} - - // function takeLoot() external {} - - // function dropLoot() external {} - - - //------非战区空间部分 -} - + function settleBattle() external {} + function createLootBox() external {} + function takeLoot() external {} + function dropLoot() external {} + //------非战区空间部分 + - + +} diff --git a/packages/contracts/src/systems/MoveSystem.sol b/packages/contracts/src/systems/MoveSystem.sol index aee7c620..c9b2392c 100644 --- a/packages/contracts/src/systems/MoveSystem.sol +++ b/packages/contracts/src/systems/MoveSystem.sol @@ -2,13 +2,18 @@ pragma solidity >=0.8.0; import { System } from "@latticexyz/world/src/System.sol"; +import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; + import { BattleState, Buff, PlayerState } from "../codegen/Types.sol"; -import { GameConfig, BattleConfig, RandomList, RandomListData, BoxList, BoxListData, Player, PlayerData, PlayerLocationLock} from "../codegen/Tables.sol"; +import { GameConfig, BattleConfig, RandomList, RandomListData,BattleList, BoxList, BoxListData, Player, PlayerData, PlayerLocationLock} from "../codegen/Tables.sol"; import { GAME_CONFIG_KEY, BATTLE_CONFIG_KEY } from "../Constants.sol"; import { CommonUtils } from "./library/CommonUtils.sol"; - +import {Move} from "./Common.sol"; contract MoveSystem is System { + event MoveEvent(address indexed player, uint16 x, uint16 y); + event AttackStart(address player, address target); + function unlockUserLocation() external { // 用户自行解锁 require(PlayerLocationLock.get(_msgSender()) != 0, "You are not locked"); @@ -28,5 +33,106 @@ contract MoveSystem is System { Player.setY(_msgSender(), y); } + modifier CheckContinuity(Move[] memory moveList) { + // 验证行走轨迹合法且连续 + uint8 prefer = 1; + for (uint256 i; i < moveList.length; i++) { + + uint16 x2 = i > 0 ? moveList[i - 1].x : Player.getX(_msgSender()); + uint16 y2 = i > 0 ? moveList[i - 1].y : Player.getY(_msgSender()); + require( + CommonUtils.isNear(moveList[i].x, x2, moveList[i].y, y2), + "invalied move" + ); + // 判断用户每一个移动连续性以及合法性,不能超出1格, 不能斜着走,不能原地踏步 + Move memory info = moveList[i]; + //prefer的意思是可以通行 + bytes32 leaf = keccak256( + abi.encodePacked(info.x, ",", info.y, ",", prefer) + ); + bool isValidLeaf = MerkleProof.verify(info.proof, GameConfig.getMerkleRoot(GAME_CONFIG_KEY), leaf); + require(isValidLeaf, "bad position"); + } + _; + } + + function move(Move[] memory moveList) external CheckContinuity(moveList) { + // 限制移动速度,每次移动需要间隔一定时间 + require(PlayerLocationLock.get(_msgSender()) == 0, "You are locked"); + require( + moveList.length > 0 && moveList.length <= BattleConfig.getMaxMoveDistance(BATTLE_CONFIG_KEY), + "invalid move distance" + ); + + Player.setX(_msgSender(), moveList[moveList.length - 1].x); + Player.setY(_msgSender(), moveList[moveList.length - 1].y); + + emit MoveEvent( + _msgSender(), + moveList[moveList.length - 1].x, + moveList[moveList.length - 1].y + ); + } + + + function battleInvitation( + address _targetAddress, + Move[] memory moveList + ) external CheckContinuity(moveList) { + // 攻击,首先确定地图x,y上有具体用户,其次确定用户之间最短距离proof为10 + // 需要考虑一个格子上有多个用户的情况//一个格子只能有一个人 + // 判断对战双方的状态是否是Exploring + + require( + moveList.length > 0 && moveList.length <= BattleConfig.getMaxAttackzDistance(BATTLE_CONFIG_KEY), + "invalid attack distance" + ); + + require( + Player.getState(_msgSender()) == PlayerState.Exploring && + Player.getState(_targetAddress) == PlayerState.Exploring, + "Each player must be in exploring state" + ); + require( + Player.getX(_targetAddress) == moveList[moveList.length - 1].x && + Player.getY(_targetAddress) == moveList[moveList.length - 1].y, + "Target must be in the end of continuity" + ); + + Player.setState(_msgSender(), PlayerState.Attacking); + Player.setState(_targetAddress, PlayerState.Attacking); + + uint256 battleId = GameConfig.getBattleId(GAME_CONFIG_KEY); + BattleList.setAttacker(battleId, _msgSender()); + BattleList.setDefender(battleId, _targetAddress); + BattleList.setTimestamp(battleId, block.timestamp); + BattleList.setAttackerHP(battleId, Player.getHP(_msgSender())); + BattleList.setDefenderHP(battleId, Player.getHP(_targetAddress)); + + // battleId++; + GameConfig.setBattleId(GAME_CONFIG_KEY, battleId + 1); + + emit AttackStart(_msgSender(), _targetAddress); + } + + function outBattlefield(address _user) internal { + // 脱离战区,则将用户血量回满,坐标不变,状态改为准备中 + require( + Player.getState(_user) == PlayerState.Exploring, + "You should in exploring state" + ); + + Player.setHP(_user, initUserHP(_user)); + + for (uint256 i; i < BattleConfig.lengthBattlefieldPlayers(BATTLE_CONFIG_KEY); i++) { + if (BattleConfig.getItemBattlefieldPlayers(BATTLE_CONFIG_KEY, i) == _user) { + BattleConfig.updateBattlefieldPlayers(BATTLE_CONFIG_KEY, i, BattleConfig.getItemBattlefieldPlayers(BATTLE_CONFIG_KEY, BattleConfig.lengthBattlefieldPlayers(BATTLE_CONFIG_KEY) - 1)); + BattleConfig.popBattlefieldPlayers(BATTLE_CONFIG_KEY); + break; + } + } + Player.setState(_user, PlayerState.Preparing); + } + } \ No newline at end of file diff --git a/packages/contracts/src/systems/PlayerSystem.sol b/packages/contracts/src/systems/PlayerSystem.sol index 1c1578e9..2ba0b82a 100644 --- a/packages/contracts/src/systems/PlayerSystem.sol +++ b/packages/contracts/src/systems/PlayerSystem.sol @@ -7,9 +7,7 @@ import { Player } from "../codegen/Tables.sol"; contract PlayerSystem is System { bytes32 constant PLAYER_KEY = keccak256("Player-Key"); - constructor(){ - - } + function getInfo(address addr) public view returns (string memory, string memory) { return (Player.getName(addr), Player.getUrl(addr)); @@ -21,10 +19,6 @@ contract PlayerSystem is System { Player.setUrl(addr, url); } - function transfer(address addr, uint16 x, uint16 y) public { - Player.setX(addr, x); - Player.setY(addr, y); - } function getPosition(address addr) public view returns (uint16, uint16) { uint16 x = Player.getX(addr);