From 7a102fdedcaed6bea86b868370c3094cfbb4e557 Mon Sep 17 00:00:00 2001 From: Alex Metelli Date: Mon, 30 Oct 2023 07:10:31 +0800 Subject: [PATCH] added celestia to debris + battle report event --- src/game/main.cairo | 96 ++++++++++++++++++++++++++-- src/libraries/fleet.cairo | 5 +- src/libraries/types.cairo | 1 + src/mocks/mock_upgradable.cairo | 6 +- tests/internal_functions/fleet.cairo | 2 +- tests/write_tests/fleet_write.cairo | 12 +++- 6 files changed, 110 insertions(+), 12 deletions(-) diff --git a/src/game/main.cairo b/src/game/main.cairo index 03150b4..175037d 100644 --- a/src/game/main.cairo +++ b/src/game/main.cairo @@ -1,6 +1,7 @@ // TODOS: -// - Make contract upgradable // - Add events for battle reports +// - Add celestia to battle +// - Adjust players points after battle #[starknet::contract] mod NoGame { @@ -101,6 +102,7 @@ mod NoGame { TechSpent: TechSpent, FleetSpent: FleetSpent, Upgraded: Upgraded, + BattleReport: BattleReport, } #[derive(Drop, starknet::Event)] @@ -126,6 +128,17 @@ mod NoGame { implementation: ClassHash } + #[derive(Drop, starknet::Event)] + struct BattleReport { + time: u64, + attacker: u16, + attacker_fleet_loss: Fleet, + defender: u16, + defender_fleet_loss: Fleet, + defences_loss: DefencesLevels, + debris: Debris, + } + #[constructor] fn constructor(ref self: ContractState) { self.owner.write(get_caller_address()); @@ -669,19 +682,23 @@ mod NoGame { self.check_enough_ships(planet_id, f); // Calculate distance let distance = fleet::get_distance(self.planet_position.read(planet_id), destination); + // Calculate time let techs = self.get_tech_levels(planet_id); let speed = fleet::get_fleet_speed(f, techs); let travel_time = fleet::get_flight_time(speed, distance); + // Check numeber of mission let active_missions = self.active_missions_len.read(planet_id); assert(active_missions < techs.digital.into() + 1, 'max active missions'); + // Pay for fuel let consumption = fleet::get_fuel_consumption(f, distance); let mut cost: ERC20s = Default::default(); cost.tritium = consumption; self.check_enough_resources(caller, cost); self.pay_resources_erc20(caller, cost); + // Write mission let time_now = get_block_timestamp(); let mut mission: Mission = Default::default(); @@ -689,6 +706,7 @@ mod NoGame { mission.destination = self.get_position_slot_occupant(destination); mission.time_arrival = time_now + travel_time; mission.fleet = f; + if is_debris_collection { mission.is_debris = true; self.add_active_mission(planet_id, mission); @@ -704,13 +722,15 @@ mod NoGame { self.add_hostile_mission(destination_id, hostile_mission); } + // Write new fleet levels self.fleet_leave_planet(planet_id, f); self.last_active.write(planet_id, get_block_timestamp()); } fn attack_planet(ref self: ContractState, mission_id: usize) { - let origin = self.get_owned_planet(get_caller_address()); + let caller = get_caller_address(); + let origin = self.get_owned_planet(caller); let mut mission = self.active_missions.read((origin, mission_id)); assert(!mission.is_zero(), 'the mission is empty'); let time_now = get_block_timestamp(); @@ -719,17 +739,22 @@ mod NoGame { let defences = self.get_defences_levels(mission.destination); let t1 = self.get_tech_levels(origin); let t2 = self.get_tech_levels(mission.destination); + let celestia_before = self.get_celestia_available(mission.destination); + let (f1, f2, d) = fleet::war(mission.fleet, t1, defender_fleet, defences, t2); + // calculate debris and update field - let debris1 = fleet::get_debris(mission.fleet, f1); - let debris2 = fleet::get_debris(defender_fleet, f2); + let debris1 = fleet::get_debris(mission.fleet, f1, 0); + let debris2 = fleet::get_debris(defender_fleet, f2, celestia_before - d.celestia); let total_debris = debris1 + debris2; let current_debries_field = self.planet_debris_field.read(mission.destination); self .planet_debris_field .write(mission.destination, current_debries_field + total_debris); + self.update_fleet_levels_after_attack(mission.destination, f2); self.update_defences_after_attack(mission.destination, d); + if f1.is_zero() { self.active_missions.write((origin, mission_id), Zeroable::zero()); } else { @@ -746,7 +771,22 @@ mod NoGame { mission.fleet = f1; self.active_missions.write((origin, mission_id), Zeroable::zero()); } + self.remove_hostile_mission(mission.destination, mission_id); + + let attacker_loss = self.calculate_fleet_loss(mission.fleet, f1); + let defender_loss = self.calculate_fleet_loss(defender_fleet, f2); + let defences_loss = self.calculate_defences_loss(defences, d); + self + .emit_battle_report( + time_now, + origin, + attacker_loss, + mission.destination, + defender_loss, + defences_loss, + total_debris + ); } fn recall_fleet(ref self: ContractState, mission_id: usize) { @@ -1601,6 +1641,54 @@ mod NoGame { return 10; } } + + fn calculate_fleet_loss(self: @ContractState, a: Fleet, b: Fleet) -> Fleet { + Fleet { + carrier: a.carrier - b.carrier, + scraper: a.scraper - b.scraper, + sparrow: a.sparrow - b.sparrow, + frigate: a.frigate - b.frigate, + armade: a.armade - b.armade, + } + } + + fn calculate_defences_loss( + self: @ContractState, a: DefencesLevels, b: DefencesLevels + ) -> DefencesLevels { + DefencesLevels { + celestia: a.celestia - b.celestia, + blaster: a.blaster - b.blaster, + beam: a.beam - b.beam, + astral: a.astral - b.astral, + plasma: a.plasma - b.plasma, + } + } + + fn emit_battle_report( + ref self: ContractState, + time: u64, + attacker: u16, + attacker_fleet_loss: Fleet, + defender: u16, + defender_fleet_loss: Fleet, + defences_loss: DefencesLevels, + debris: Debris + ) { + self + .emit( + Event::BattleReport( + BattleReport { + time, + attacker, + attacker_fleet_loss, + defender, + defender_fleet_loss, + defences_loss, + debris + } + ) + ) + } } } diff --git a/src/libraries/fleet.cairo b/src/libraries/fleet.cairo index f33c146..fe75c85 100644 --- a/src/libraries/fleet.cairo +++ b/src/libraries/fleet.cairo @@ -430,7 +430,7 @@ fn get_distance(start: PlanetPosition, end: PlanetPosition) -> u32 { } #[inline(always)] -fn get_debris(f_before: Fleet, f_after: Fleet) -> Debris { +fn get_debris(f_before: Fleet, f_after: Fleet, celestia: u32) -> Debris { let mut debris: Debris = Default::default(); let costs = Dockyard::get_ships_unit_cost(); let steel = ((f_before.carrier - f_after.carrier).into() * costs.carrier.steel) @@ -443,7 +443,8 @@ fn get_debris(f_before: Fleet, f_after: Fleet) -> Debris { + ((f_before.scraper - f_after.scraper).into() * costs.scraper.quartz) + ((f_before.sparrow - f_after.sparrow).into() * costs.sparrow.quartz) + ((f_before.frigate - f_after.frigate).into() * costs.sparrow.quartz) - + ((f_before.armade - f_after.armade).into() * costs.sparrow.quartz); + + ((f_before.armade - f_after.armade).into() * costs.sparrow.quartz) + + (celestia * 2000).into(); debris.steel = steel / 3; debris.quartz = quartz / 3; diff --git a/src/libraries/types.cairo b/src/libraries/types.cairo index ba9a2d0..bba1b34 100644 --- a/src/libraries/types.cairo +++ b/src/libraries/types.cairo @@ -408,3 +408,4 @@ impl MissionPrint of PrintTrait { self.fleet.print(); } } + diff --git a/src/mocks/mock_upgradable.cairo b/src/mocks/mock_upgradable.cairo index c0ef13f..b68f0d7 100644 --- a/src/mocks/mock_upgradable.cairo +++ b/src/mocks/mock_upgradable.cairo @@ -829,10 +829,12 @@ mod NoGameUpgraded { let defences = self.get_defences_levels(mission.destination); let t1 = self.get_tech_levels(origin); let t2 = self.get_tech_levels(mission.destination); + let celestia_before = self.get_celestia_available(mission.destination); + let (f1, f2, d) = fleet::war(mission.fleet, t1, defender_fleet, defences, t2); // calculate debris and update field - let debris1 = fleet::get_debris(mission.fleet, f1); - let debris2 = fleet::get_debris(defender_fleet, f2); + let debris1 = fleet::get_debris(mission.fleet, f1, 0); + let debris2 = fleet::get_debris(defender_fleet, f2, celestia_before - d.celestia); let total_debris = debris1 + debris2; let current_debries_field = self.planet_debris_field.read(mission.destination); self diff --git a/tests/internal_functions/fleet.cairo b/tests/internal_functions/fleet.cairo index 3f7406c..e186709 100644 --- a/tests/internal_functions/fleet.cairo +++ b/tests/internal_functions/fleet.cairo @@ -163,6 +163,6 @@ fn test_get_debris() { let mut after: Fleet = Default::default(); before.carrier = 10; after.carrier = 0; - let res = fleet::get_debris(before, after); + let res = fleet::get_debris(before, after, 10); } diff --git a/tests/write_tests/fleet_write.cairo b/tests/write_tests/fleet_write.cairo index cef1608..afd763f 100644 --- a/tests/write_tests/fleet_write.cairo +++ b/tests/write_tests/fleet_write.cairo @@ -1,6 +1,11 @@ use starknet::info::{get_block_timestamp, get_contract_address}; -use snforge_std::{declare, ContractClassTrait, PrintTrait, start_prank, start_warp}; +use snforge_std::{ + declare, ContractClassTrait, PrintTrait, start_prank, start_warp, spy_events, SpyOn, EventSpy, + EventAssertions, EventFetcher, event_name_hash, Event +}; + +use nogame::game::main::NoGame; use nogame::game::interface::{INoGameDispatcher, INoGameDispatcherTrait}; use nogame::libraries::types::{Fleet, Unit, TechLevels, PlanetPosition, ERC20s, DefencesLevels}; @@ -324,7 +329,8 @@ fn test_attack_planet() { dsp.game.generate_planet(); build_basic_mines(dsp.game); advance_game_state(dsp.game); - dsp.game.blaster_build(1); + dsp.game.celestia_build(5); + // dsp.game.blaster_build(1); let defences_before = dsp.game.get_defences_levels(2); start_prank(dsp.game.contract_address, ACCOUNT1()); build_basic_mines(dsp.game); @@ -347,7 +353,7 @@ fn test_attack_planet() { let (fleetA_after, fleetB_after, defences_after) = fleet::war( fleet_a, a_techs, fleet_b, defences_before, b_techs ); - let expected_debris = fleet::get_debris(fleetA_before, fleetA_after); + let expected_debris = fleet::get_debris(fleetA_before, fleetA_after, 5); let debris_field = dsp.game.get_debris_field(2); assert(debris_field == expected_debris, 'wrong after debris');