From c9a00bc88f25599f5d7f1f8b50a553ababa49001 Mon Sep 17 00:00:00 2001 From: libolin <15866358268@163.com> Date: Sat, 4 Apr 2026 17:53:47 +0800 Subject: [PATCH] 4.4 --- 李柏霖学习笔记/4.4.md | 302 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 李柏霖学习笔记/4.4.md diff --git a/李柏霖学习笔记/4.4.md b/李柏霖学习笔记/4.4.md new file mode 100644 index 0000000..533c906 --- /dev/null +++ b/李柏霖学习笔记/4.4.md @@ -0,0 +1,302 @@ +练手项目:java地牢 + +dungeon-boot/ +├── src/ +│ ├── main/ +│ │ ├── java/ +│ │ │ └── com/dungeon/ +│ │ │ ├── DungeonApplication.java // 项目启动类 +│ │ │ ├── common/ // 公共配置与工具 +│ │ │ │ ├── config/ // MyBatis-Plus/Redis 配置 +│ │ │ │ ├── exception/ // 自定义异常(如:玩家死亡异常) +│ │ │ │ └── result/ // 统一接口返回对象 (Result) +│ │ │ ├── controller/ // API 控制层 (与前端交互) +│ │ │ │ ├── PlayerController.java +│ │ │ │ └── GameController.java +│ │ │ ├── entity/ // 数据库映射实体 (POJO) +│ │ │ │ ├── Player.java +│ │ │ │ ├── Room.java +│ │ │ │ └── Monster.java +│ │ │ ├── mapper/ // MyBatis-Plus Mapper 接口 +│ │ │ │ ├── PlayerMapper.java +│ │ │ │ └── RoomMapper.java +│ │ │ └── service/ // 核心业务逻辑层 +│ │ │ ├── PlayerService.java // 玩家属性、升级逻辑 +│ │ │ ├── MapService.java // 移动、房间探索逻辑 +│ │ │ └── BattleService.java // 战斗计算逻辑 +│ │ └── resources/ +│ │ ├── application.yml // 数据库连接与游戏全局配置 +│ └── test/ // 单元测试 +└── pom.xml // 项目依赖管理 + +controller + +```java +package com.example.demo01.controller; + +import com.example.demo01.common.result.Result; +import com.example.demo01.entity.Player; +import com.example.demo01.entity.Room; +import com.example.demo01.service.BattleService; +import com.example.demo01.service.MapService; +import com.example.demo01.service.PlayerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/game") +public class GameController { + + @Autowired + private PlayerService playerService; + + @Autowired + private MapService mapService; + + @Autowired + private BattleService battleService; + + @PostMapping("/move") + public Result move(@RequestBody MoveRequest request) { + // 1. 获取玩家信息 + Player player = playerService.getPlayerStatus(request.getPlayerId()); + if (player == null) return Result.error("玩家不存在"); + + // 2. 调用地图服务判断是否可以移动 + Room nextRoom = mapService.move(player.getCurrentRoomId(), request.getDirection()); + + if (nextRoom != null) { + // 3. 移动成功,更新玩家所在的房间ID + playerService.moveToRoom(player.getId(), nextRoom.getId()); + // 返回新房间的信息,让前端显示描述文字 + return Result.success(nextRoom); + } else { + return Result.error("哎呀!你撞到了墙上,那里没有路。"); + } + } + + @PostMapping("/attack") + public Result doAttack(@RequestParam Long playerId, @RequestParam Long monsterId) { + BattleResult result = battleService.attack(playerId, monsterId); + return Result.success(result); + } +} +``` + +``` +@Data +public class MoveRequest { + /** + * 正在操作的玩家 ID + */ + private Long playerId; + + /** + * 移动方向:north, south, east, west + */ + private String direction; +} +``` + +``` +@RestController +@RequestMapping("/api/player") +public class PlayerController { + + @Autowired + private PlayerService playerService; + + // 获取玩家当前面板状态:GET /api/player/status?id=1 + @GetMapping("/status") + public Result getStatus(@RequestParam Long id) { + Player player = playerService.getPlayerStatus(id); + if (player == null) { + return Result.error("未找到该角色"); + } + return Result.success(player); + } +} +``` + +service + +``` +package com.example.demo01.service.impl; + +import com.example.demo01.controller.BattleResult; +import com.example.demo01.entity.Monster; +import com.example.demo01.entity.Player; +import com.example.demo01.mapper.MonsterMapper; +import com.example.demo01.mapper.PlayerMapper; +import com.example.demo01.service.BattleService; +import com.example.demo01.service.PlayerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class BattleServiceImpl implements BattleService { + + @Autowired + private PlayerMapper playerMapper; + @Autowired + private MonsterMapper monsterMapper; + @Autowired + private PlayerService playerService; + + @Override + @Transactional + public BattleResult attack(Long playerId, Long monsterId) { + Player player = playerMapper.selectById(playerId); + Monster monster = monsterMapper.selectById(monsterId); + + StringBuilder log = new StringBuilder(); + + // 1. 玩家攻击怪物 + // 伤害公式:攻击力 - 防御力(最小为1) + int damageToMonster = Math.max(1, player.getAtk() - monster.getDef()); + monster.setHp(Math.max(0, monster.getHp() - damageToMonster)); + log.append(String.format("你攻击了 %s,造成了 %d 点伤害。 ", monster.getName(), damageToMonster)); + + // 2. 检查怪物是否死亡 + if (!monster.isAlive()) { + log.append(String.format("%s 倒下了!你获得了 %d 经验。", monster.getName(), monster.getExpReward())); + // 结算奖励 + playerService.addRewards(playerId, monster.getExpReward(), monster.getGoldReward()); + // 从房间移除怪物(可选逻辑:更新Room表或者标记Monster已死) + monsterMapper.deleteById(monsterId); + + return BattleResult.builder() + .log(log.toString()).monsterHp(0).playerHp(player.getHp()) + .monsterDead(true).rewardsExp(monster.getExpReward()).build(); + } + + // 3. 怪物反击(如果还没死) + int damageToPlayer = Math.max(1, monster.getAtk() - 5); // 假设玩家基础防御5 + player.setHp(Math.max(0, player.getHp() - damageToPlayer)); + playerMapper.updateById(player); // 更新玩家血量 + monsterMapper.updateById(monster); // 更新怪物血量 + + log.append(String.format("%s 发起反击,你受到了 %d 点伤害。", monster.getName(), damageToPlayer)); + + return BattleResult.builder() + .log(log.toString()) + .playerHp(player.getHp()) + .monsterHp(monster.getHp()) + .playerDead(player.getHp() <= 0) + .build(); + } +} + +``` + +``` +package com.example.demo01.service.impl; + +import com.example.demo01.entity.Room; +import com.example.demo01.mapper.RoomMapper; +import com.example.demo01.service.MapService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class MapServiceImpl implements MapService { + + @Autowired + private RoomMapper roomMapper; + + @Override + public Room move(Long currentRoomId, String direction) { + // 1. 先找到玩家所在的当前房间 + Room currentRoom = roomMapper.selectById(currentRoomId); + if (currentRoom == null) return null; + + // 2. 根据方向获取目标房间的 ID + Long nextRoomId = null; + switch (direction.toLowerCase()) { + case "north": nextRoomId = currentRoom.getNorthRoomId(); break; + case "south": nextRoomId = currentRoom.getSouthRoomId(); break; + case "east": nextRoomId = currentRoom.getEastRoomId(); break; + case "west": nextRoomId = currentRoom.getWestRoomId(); break; + } + + // 3. 如果 ID 为空,说明那边是墙 + if (nextRoomId == null) return null; + + // 4. 返回目标房间的完整信息 + return roomMapper.selectById(nextRoomId); + } + + @Override + public Room getRoomById(Long roomId) { + return roomMapper.selectById(roomId); + } +} +``` + +``` +package com.example.demo01.service.impl; + +import com.example.demo01.entity.Player; +import com.example.demo01.mapper.PlayerMapper; +import com.example.demo01.service.PlayerService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class PlayerServiceImpl implements PlayerService { + + @Autowired + private PlayerMapper playerMapper; + + @Override + public Player getPlayerStatus(Long playerId) { + return playerMapper.selectById(playerId); + } + + @Override + @Transactional // 涉及数据库修改,建议开启事务 + public boolean moveToRoom(Long playerId, Long newRoomId) { + Player player = playerMapper.selectById(playerId); + if (player == null) return false; + + player.setCurrentRoomId(newRoomId); + return playerMapper.updateById(player) > 0; + } + + @Override + public void takeDamage(Long playerId, Integer damage) { + Player player = playerMapper.selectById(playerId); + if (player != null) { + // 计算扣血后的 HP,确保不小于 0 + int newHp = Math.max(0, player.getHp() - damage); + player.setHp(newHp); + playerMapper.updateById(player); + + if (newHp == 0) { + // 这里可以触发死亡逻辑,比如清空存档或传回出生点 + System.out.println("玩家 " + player.getName() + " 已阵亡!"); + } + } + } + + @Override + public void addRewards(Long playerId, Integer exp, Integer gold) { + Player player = playerMapper.selectById(playerId); + if (player != null) { + player.setExp(player.getExp() + exp); + // 简单的升级逻辑:每 100 经验升一级 + if (player.getExp() >= player.getLevel() * 100) { + player.setLevel(player.getLevel() + 1); + player.setAtk(player.getAtk() + 5); // 升级加攻击 + player.setHp(player.getMaxHp()); // 升级回满血 + } + playerMapper.updateById(player); + } + } +} + +``` +