时间获取收益 - 基于时间的经济系统

在本课中,我们将实现游戏的经济系统。

经济系统的介绍可以简化为:

treasury = last_settlement_treasury + economic_power * (current_time - last_settlement_time)

金库目前在游戏设计中只能用于招募士兵。

经济实力代表城堡在单位时间内积累的金库数量。经济实力有两种类型:城堡基础经济实力和额外经济实力。 基础经济实力取决于城堡的大小和等级,随着城堡升级而增加。而额外经济实力受士兵和战斗效果的影响。 士兵提供正面的额外经济实力,赢得战斗也会带来正面的力量加成。然而,失败的战斗会导致负面的力量加成。因此,额外经济实力也可以实现为“增益”。

链上没有触发器可以在特定时期自动收集每个城堡的金库,因此城堡的经济需要手动结算。 然而,在两次结算之间,由于城堡升级、士兵招募或参与战斗,城堡的“总”经济实力会发生变化。这使得新的结算计算变得复杂。

如前所述,士兵和战斗的额外经济实力实现为“增益”。一个增益包含其生效时间的信息。 在结算期间,基础经济实力贡献的金库和每个增益在其区间与结算区间的交集内的贡献都要计算在内。

处理基础经济实力变化的问题相对简单。可以在城堡升级时触发自动结算。由于升级是手动行为,这是结算基础经济实力的良好时机。

首先,计算一段时间内的经济收益是简单的,在 core.move 中:

use sui::math;

/// 根据力量和时间段(1 分钟)计算经济收益。
fun calculate_economic_benefits(start: u64, end: u64, power: u64): u64 {
    math::divide_and_round_up((end - start) * power, 60u64 * 1000u64)
}

代码中的输入 startend 是以毫秒为单位的时间戳。要获取当前时间戳,我们需要使用部署在 0x6Clock 对象。 在 core.move 中创建一个 settle_castle_economy_inner 函数:

/// 结算城堡的经济,内部方法
public(package) fun settle_castle_economy_inner(clock: &Clock, castle_data: &mut CastleData) {
    use sui::clock::{Self, Clock};

    let current_timestamp = clock::timestamp_ms(clock);
}

现在尝试计算基础经济实力的经济收益,并更新 CastleData

// 1. 计算基础力量收益
let base_benefits = calculate_economic_benefits(castle_data.economy.settle_time, current_timestamp, castle_data.economy.base_power);
castle_data.economy.treasury = castle_data.economy.treasury + base_benefits;
castle_data.economy.settle_time = current_timestamp;

接着计算士兵增益:

// 2. 计算士兵增益
let soldier_benefits = calculate_economic_benefits(castle_data.economy.soldier_buff.start, current_timestamp, castle_data.economy.soldier_buff.power);
castle_data.economy.treasury = castle_data.economy.treasury + soldier_benefits;
castle_data.economy.soldier_buff.start = current_timestamp;

士兵数量也可以在招募和战斗时发生变化,但我们不会维护多个士兵增益,因此在招募和战斗结算前也需要进行经济结算。

计算战斗增益是复杂的,有多个战斗增益,并且可以是正面或负面的:

// 3. 计算战斗增益
if (!vector::is_empty(&castle_data.economy.battle_buff)) {
    let length = vector::length(&castle_data.economy.battle_buff);
    let mut expired_buffs = vector::empty<u64>();
    let mut i = 0;
    while (i < length) {
        let buff = vector::borrow_mut(&mut castle_data.economy.battle_buff, i);
        let mut battle_benefit;
        if (buff.end <= current_timestamp) {
            vector::push_back(&mut expired_buffs, i);
            battle_benefit = calculate_economic_benefits(buff.start, buff.end, buff.power);
        } else {
            battle_benefit = calculate_economic_benefits(buff.start, current_timestamp, buff.power);
            buff.start = current_timestamp;
        };
        if (buff.debuff) {
            castle_data.economy.treasury = castle_data.economy.treasury - battle_benefit;
        } else {
            castle_data.economy.treasury = castle_data.economy.treasury + battle_benefit;
        };
        i = i + 1;
    };
    // 移除过期增益
    while(!vector::is_empty(&expired_buffs)) {
        let expired_buff_index = vector::remove(&mut expired_buffs, 0);
        vector::remove(&mut castle_data.economy.battle_buff, expired_buff_index);
    };
    vector::destroy_empty<u64>(expired_buffs);
}

完成了,完整的经济结算函数应为:

// core.move

/// 结算城堡的经济,内部方法
public(package) fun settle_castle_economy_inner(clock: &Clock, castle_data: &mut CastleData) {
    let current_timestamp = clock::timestamp_ms(clock);

    // 1. 计算基础力量收益
    let base_benefits = calculate_economic_benefits(castle_data.economy.settle_time, current_timestamp, castle_data.economy.base_power);
    castle_data.economy.treasury = castle_data.economy.treasury + base_benefits;
    castle_data.economy.settle_time = current_timestamp;

    // 2. 计算士兵增益
    let soldier_benefits = calculate_economic_benefits(castle_data.economy.soldier_buff.start, current_timestamp, castle_data.economy.soldier_buff.power);
    castle_data.economy.treasury = castle_data.economy.treasury + soldier_benefits;
    castle_data.economy.soldier_buff.start = current_timestamp;

    // 3. 计算战斗增益
    if (!vector::is_empty(&castle_data.economy.battle_buff)) {
        let length = vector::length(&castle_data.economy.battle_buff);
        let mut expired_buffs = vector::empty<u64>();
        let mut i = 0;
        while (i < length) {
            let buff = vector::borrow_mut(&mut castle_data.economy.battle_buff, i);
            let mut battle_benefit;
            if (buff.end <= current_timestamp) {
                vector::push_back(&mut expired_buffs, i);
                battle_benefit = calculate_economic_benefits(buff.start, buff.end, buff.power);
            } else {
                battle_benefit = calculate_economic_benefits(buff.start, current_timestamp, buff.power);
                buff.start = current_timestamp;
            };

            if (buff.debuff) {
                castle_data.economy.treasury = castle_data.economy.treasury - battle_benefit;
            } else {
                castle_data.economy.treasury = castle_data.economy.treasury + battle_benefit;
            };
            i = i + 1;
        };

        // 移除过期增益
        while(!vector::is_empty(&expired_buffs)) {
            let expired_buff_index = vector::remove(&mut expired_buffs, 0);
            vector::remove(&mut castle_data.economy.battle_buff, expired_buff_index);
        };
        vector::destroy_empty<u64>(expired_buffs);
    }
}    

最后,在当前模块中添加一个封装函数,并在 castle.move 中添加一个入口函数:

// core.move

/// 结算城堡的经济,包括胜利奖励和失败惩罚
public(package) fun settle_castle_economy(id: ID, clock: &Clock, game_store: &mut GameStore) {
    settle_castle_economy_inner(clock, dynamic_field::borrow_mut<ID, CastleData>(&mut game_store.id, id));
}

// castle.move

/// 结算城堡的经济
entry fun settle_castle_economy(castle: &Castle, clock: &Clock, game_store: &mut GameStore) {
    core::settle_castle_economy(object::id(castle), clock, game_store);
}