清理战场 - 战斗结算
在上一课中,我们终于确定了赢家,现在是清理战场的时候了,赢家得到奖励,输家受到惩罚。
战斗对双方城堡的影响包括:
- 战斗冷却
- 士兵伤亡
- 掠夺在战斗冷却期间由失败者的基础经济能力提供的利益
- 胜者获得经验值
让我们继续完成 battle
函数。我们将分别结算赢家和输家。
1. 赢家
我们需要首先结算城堡的经济:
// 5. 战斗结算
// 5.1 结算赢家
core::settle_castle_economy_inner(clock, &mut winner);
士兵剩余和士兵损失的计算方式如下:
use move_castle::utils;
let winner_solders_total_defense_power = core::get_castle_total_soldiers_defense_power(&winner);
let loser_solders_total_attack_power = core::get_castle_total_soldiers_attack_power(&loser);
let winner_soldiers_left;
if (winner_solders_total_defense_power > loser_solders_total_attack_power) {
let (_, winner_soldier_defense_power) = core::get_castle_soldier_attack_defense_power(core::get_castle_race(&winner));
winner_soldiers_left = math::divide_and_round_up(winner_solders_total_defense_power - loser_solders_total_attack_power, winner_soldier_defense_power);
} else {
winner_soldiers_left = 0;
};
let winner_soldiers_lost = core::get_castle_soldiers(&winner) - winner_soldiers_left;
上面的代码中使用的 get_castle_race
函数在 core
模块中:
public(package) fun get_castle_race(castle_data: &CastleData): u64 {
castle_data.race
}
在 utils.move
中的 abs_minus
函数是:
public(package) fun abs_minus(a: u64, b: u64): u64 {
let result;
if (a > b) {
result = a - b;
} else {
result = b - a;
};
result
}
在 core.move
中的 get_castle_soldiers
是:
public(package) fun get_castle_soldiers(castle_data: &CastleData): u64 {
castle_data.millitary.soldiers
}
胜利者获得的经验值取决于城堡的等级,根据设计,在 core.move
中添加这个常量向量和函数:
/// 根据胜利者等级在战斗中获得的经验值 1 - 10
const BATTLE_EXP_GAIN_LEVELS : vector<u64> = vector[25, 30, 40, 55, 75, 100, 130, 165, 205, 250];
public fun battle_winner_exp(castle_data: &CastleData): u64 {
let battle_exp_map = BATTLE_EXP_GAIN_LEVELS;
*vector::borrow<u64>(&battle_exp_map, castle_data.level)
}
回到 battle
函数,添加:
let winner_exp_gain = core::battle_winner_exp(&winner);
被掠夺的失败者基础经济能力应该是:
let reparation_economic_power = core::get_castle_economic_base_power(&loser);
更新赢家的城堡数据:
core::battle_settlement_save_castle_data(
game_store,
winner,
true,
current_timestamp + BATTLE_WINNER_COOLDOWN_MS,
reparation_economic_power,
current_timestamp,
current_timestamp + BATTLE_LOSER_ECONOMIC_PENALTY_TIME,
winner_soldiers_left,
winner_exp_gain
);
const BATTLE_WINNER_COOLDOWN_MS : u64 = 30 * 1000; // 30秒
const BATTLE_LOSER_ECONOMIC_PENALTY_TIME : u64 = 2 * 60 * 1000; // 2分钟
在 core.move
中:
public(package) fun get_castle_economic_base_power(castle_data: &CastleData): u64 {
castle_data.economy.base_power
}
// 计算士兵经济能力
public(package) fun calculate_soldiers_economic_power(count: u64): u64 {
SOLDIER_ECONOMIC_POWER * count
}
/// 结算战斗
public(package) fun battle_settlement_save_castle_data(game_store: &mut GameStore, mut castle_data: CastleData, win: bool, cooldown: u64, economic_base_power: u64, current_timestamp: u64, economy_buff_end: u64, soldiers_left: u64, exp_gain: u64) {
// 1. 战斗冷却
castle_data.millitary.battle_cooldown = cooldown;
// 2. 士兵剩余
castle_data.millitary.soldiers = soldiers_left;
castle_data.economy.soldier_buff.power = calculate_soldiers_economic_power(soldiers_left);
castle_data.economy.soldier_buff.start = current_timestamp;
// 3. 士兵导致的总攻击/防御力量
castle_data.millitary.total_attack_power = get_castle_total_attack_power(&castle_data);
castle_data.millitary.total_defense_power = get_castle_total_defense_power(&castle_data);
// 4. 获得经验值
castle_data.experience_pool = castle_data.experience_pool + exp_gain;
// 5. 经济增益
vector::push_back(&mut castle_data.economy.battle_buff, EconomicBuff {
debuff: !win,
power: economic_base_power,
start: current_timestamp,
end: economy_buff_end,
});
// 6. 放回表中
dynamic_field::add(&mut game_store.id, castle_data.id, castle_data);
}
这不是一个好的实践,这个函数有太多参数。
2. 输家
同样,在结算失败者的战斗结果之前,有必要结算其经济:
// 5.2 结算输家
core::settle_castle_economy_inner(clock, &mut loser);
输家的数字相当简单,士兵保持为0,获得的经验值为0。
let loser_soldiers_left = 0;
let loser_soldiers_lost = core::get_castle_soldiers(&loser) - loser_soldiers_left;
core::battle_settlement_save_castle_data(
game_store,
loser,
false,
current_timestamp + BATTLE_LOSER_COOLDOWN_MS,
reparation_economic_power,
current_timestamp,
current_timestamp + BATTLE_LOSER_ECONOMIC_PENALTY_TIME,
loser_soldiers_left,
0
);
const BATTLE_LOSER_COOLDOWN_MS : u64 = 2 * 60 * 1000; // 2分钟
最后,让我们重新审视完整的 battle
函数:
entry fun battle(castle: &Castle, clock: &Clock, game_store: &mut GameStore, ctx: &mut TxContext) {
// 1. 随机选择一个目标
let attacker_id = object::id(castle);
let target_id = core::random_battle_target(attacker_id, game_store, ctx);
// 2. 获取城堡数据
let (attacker, defender) = core::fetch_castle_data(attacker_id, target_id, game_store);
// 3. 检查战斗冷却时间
let current_timestamp = clock::timestamp_ms(clock);
assert!(core::get_castle_battle_cooldown(&attacker) < current_timestamp, EBattleCooldown);
assert!(core::get_castle_battle_cooldown(&defender) < current_timestamp, EBattleCooldown);
// 4. 战斗
// 4.1 计算总攻击力和防御力
let mut attack_power = core::get_castle_total_attack_power(&attacker);
let mut defense_power = core::get_castle_total_defense_power(&defender);
if (core::has_race_advantage(&attacker, &defender)) {
attack_power = math::divide_and_round_up(attack_power * 15, 10)
} else if (core::has_race_advantage(&defender, &attacker)) {
defense_power = math::divide_and_round_up(defense_power * 15, 10)
};
// 4.2 确定胜负
let (mut winner, mut loser);
if (attack_power > defense_power) {
winner = attacker;
loser = defender;
} else {
winner = defender;
loser = attacker;
};
let winner_id = core::get_castle_id(&winner);
let loser_id = core::get_castle_id(&loser);
// 5. 战斗结算
// 5.1 结算胜者
core::settle_castle_economy_inner(clock, &mut winner);
let winner_solders_total_defense_power = core::get_castle_total_soldiers_defense_power(&winner);
let loser_solders_total_attack_power = core::get_castle_total_soldiers_attack_power(&loser);
let winner_soldiers_left;
if (winner_solders_total_defense_power > loser_solders_total_attack_power) {
let (_, winner_soldier_defense_power) = core::get_castle_soldier_attack_defense_power(core::get_castle_race(&winner));
winner_soldiers_left = math::divide_and_round_up(winner_solders_total_defense_power - loser_solders_total_attack_power, winner_soldier_defense_power);
} else {
winner_soldiers_left = 0;
};
let winner_soldiers_lost = core::get_castle_soldiers(&winner) - winner_soldiers_left;
let winner_exp_gain = core::battle_winner_exp(&winner);
let reparation_economic_power = core::get_castle_economic_base_power(&loser);
core::battle_settlement_save_castle_data(
game_store,
winner,
true,
current_timestamp + BATTLE_WINNER_COOLDOWN_MS,
reparation_economic_power,
current_timestamp,
current_timestamp + BATTLE_LOSER_ECONOMIC_PENALTY_TIME,
winner_soldiers_left,
winner_exp_gain
);
// 5.2 结算败者
core::settle_castle_economy_inner(clock, &mut loser);
let loser_soldiers_left = 0;
let loser_soldiers_lost = core::get_castle_soldiers(&loser) - loser_soldiers_left;
core::battle_settlement_save_castle_data(
game_store,
loser,
false,
current_timestamp + BATTLE_LOSER_COOLDOWN_MS,
reparation_economic_power,
current_timestamp,
current_timestamp + BATTLE_LOSER_ECONOMIC_PENALTY_TIME,
loser_soldiers_left,
0
);
// 6. 触发事件
event::emit(CastleBattleLog {
attacker: attacker_id,
winner: winner_id,
loser: loser_id,
winner_soldiers_lost: winner_soldiers_lost,
loser_soldiers_lost: loser_soldiers_lost,
reparation_economic_power: reparation_economic_power,
battle_time: current_timestamp,
reparation_end_time: current_timestamp + BATTLE_LOSER_ECONOMIC_PENALTY_TIME
});
}
const BATTLE_WINNER_COOLDOWN_MS : u64 = 30 * 1000; // 30 秒
const BATTLE_LOSER_COOLDOWN_MS : u64 = 2 * 60 * 1000; // 2 分钟
const BATTLE_LOSER_ECONOMIC_PENALTY_TIME : u64 = 2 * 60 * 1000; // 2 分钟