-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtown_defence.rs
More file actions
190 lines (174 loc) · 6.2 KB
/
town_defence.rs
File metadata and controls
190 lines (174 loc) · 6.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
//! This module deals with visitor groups (attacks) leaving towns again and calculating the outcome of the visit.
//!
//! A fight report is generated as soon as all visitors have left or have been satisfied.
//! Usually, the satisfaction of each visitor is only computed when time is up for an attack to be finished.
//! But there are two exceptions.
//! 1) When a player has an open browser window, the frontend can detect that a visitor is satisfied and then notify the server
//! 2) Units that wait in the town need to be checked regularly
//!
//! Effects that must be taken into consideration:
//! * Defensive towers (flowers etc) which are only available by computing proximity
//! * Direct effects on units, from abilities, which are explicitly stored on the db
//!
//! When a unit is defeated or leaves otherwise, it still has to stick around in the database until all units of the group are done.
//! This can be marked in the db using the status on each HoboToAttack.
use crate::db::DB;
use crate::town_view::TownView;
use chrono::NaiveDateTime;
use paddlers_shared_lib::game_mechanics::town::*;
use paddlers_shared_lib::prelude::*;
pub(crate) struct AttackingHobo<'a> {
hobo: &'a Hobo,
attack_to_hobo: &'a AttackToHobo,
effects: &'a [Effect],
attack: &'a Attack,
}
impl DB {
/// Checks if all visitors have already left (or been satisfied).
/// If so, the visit is evaluated and a report with rewards is generated.
pub fn maybe_evaluate_attack(&self, atk: &Attack, now: NaiveDateTime) {
let now: Timestamp = now.into();
let village = atk.destination();
let active_units = self.attack_hobos_active_with_attack_info(atk);
let town = TownView::load_village(&self, village);
for (hobo, info) in &active_units {
let effects = self.effects_on_hobo(hobo.key());
let unit = AttackingHobo {
hobo: hobo,
attack_to_hobo: info,
effects: &effects,
attack: atk,
};
if town.hp_left(&unit, now) == 0 {
self.set_satisfied(hobo.key(), atk.key(), true);
if !hobo.hurried && info.released.is_none() {
self.release_resting_visitor(hobo.key(), atk.key());
}
} else if town.hobo_left_town(&unit, now) {
self.set_satisfied(hobo.key(), atk.key(), false);
}
}
// Check if all are satisfied or have left otherwise, then finish visit
if self.attack_done(atk) {
self.generate_report(atk);
if atk.origin_village_id.is_none() {
self.delete_attack_hobos(atk.key());
}
self.delete_attack(atk);
}
}
fn generate_report(&self, atk: &Attack) {
let mut report = NewVisitReport {
village_id: atk.destination_village_id,
karma: 0,
};
let happy_hobos = self.attack_hobos_satisfied(atk);
report.karma = happy_hobos.len() as i64;
use std::ops::Add;
let feathers = happy_hobos.iter().map(reward_feathers).fold(0, i64::add);
let sticks = happy_hobos.iter().map(reward_sticks).fold(0, i64::add);
let logs = happy_hobos.iter().map(reward_logs).fold(0, i64::add);
if report.karma + feathers + sticks + logs == 0 {
return;
}
let vr = self.insert_visit_report(report);
let mut rewards = vec![];
if feathers > 0 {
rewards.push(NewReward {
visit_report_id: vr.id,
resource_type: ResourceType::Feathers,
amount: feathers,
});
}
if sticks > 0 {
rewards.push(NewReward {
visit_report_id: vr.id,
resource_type: ResourceType::Sticks,
amount: sticks,
});
}
if logs > 0 {
rewards.push(NewReward {
visit_report_id: vr.id,
resource_type: ResourceType::Logs,
amount: logs,
});
}
self.insert_visit_report_rewards(rewards);
}
}
/// TODO [0.1.5]
fn reward_feathers(unit: &Hobo) -> i64 {
let f = if unit.hurried {
(1.0 + unit.hp as f32 * unit.speed / 4.0).log2().floor()
} else {
(1.0 + unit.hp as f32 / 16.0).log2().ceil()
};
// println!("Unit {:?} gives {} feathers", unit, f);
f as i64
}
/// TODO [0.1.5]
fn reward_sticks(unit: &Hobo) -> i64 {
if unit.id % 20 == 0 {
5
} else {
0
}
}
/// TODO [0.1.5]
fn reward_logs(unit: &Hobo) -> i64 {
if unit.id % 60 == 11 {
5
} else {
0
}
}
impl<'a> IAttackingHobo for AttackingHobo<'a> {
fn max_hp(&self) -> u32 {
self.hobo.hp as u32
}
fn speed(&self) -> f32 {
self.hobo.speed
}
fn hurried(&self) -> bool {
self.hobo.hurried
}
fn arrival(&self) -> Timestamp {
self.attack.arrival.into()
}
fn released(&self) -> Option<Timestamp> {
self.attack_to_hobo.released.map(|t| t.into())
}
fn effects_strength(&self) -> i32 {
self.effects
.iter()
.filter(|e| e.attribute == HoboAttributeType::Health)
.filter(|e| e.strength.is_some())
.fold(0, |acc, e| acc + e.strength.unwrap() as i64) as i32
}
}
impl ITownLayoutMarker for TownView {
const LAYOUT: TownLayout = TownLayout::Basic;
}
impl IDefendingTown for TownView {
type AuraId = i64;
fn auras_in_range(&self, index: &Self::Index, time: Timestamp) -> Vec<(Self::AuraId, i32)> {
let mut auras = vec![];
for b in &self.buildings_with_aura {
if time < b.creation.into() {
continue;
}
if b.attacks_per_cycle.is_none() {
if let (Some(range), Some(ap)) = (b.building_range, b.attack_power) {
let dx = (b.x - index.0 as i32).abs();
let dy = (b.y - index.1 as i32).abs();
let in_range = (dx * dx + dy * dy) as f32 <= range * range;
if in_range {
auras.push((b.id, ap));
}
}
}
}
auras
}
}