Skip to main content

squadleader/
lib.rs

1#![allow(unused)]
2
3//! _squadleader_ is a crate containing a type system and a rule engine that is
4//! based on the rules of the classic boardgame with the same name.
5//! Please note, there's no affiliation between this makers of this crate
6//! and the publishers of the boardgame.
7//!
8//! Further, this crate is not supposed to be a game. However, it can probably
9//! be used as a starting point, create one.
10//! The purpose of this crate is to model types and rules of the original game.
11//! You can think of it as a type system and rule engine modeling the game
12//! of chess, without being an actual playable chess game.
13//!
14//! References to the original 4th Edition Squad Leader rules are made
15//! in the source, and expressed in the following manner:
16//! `SLX.Y` refers to paragraph X.Y in the original 4th ed. rule book.
17//! All rules, that do not reference the original game, are labled RX.Y.
18
19// "Courage, above all things, is the first quality of a warrior."
20// –Carl von Clausewitz
21
22
23pub fn run_squadleader() {
24    println!("Place your SL logic here");
25}
26
27////////////////////////////////////////////////////////////////////////////////
28// Place all imports below:
29use std::collections::HashMap;
30use std::marker;
31// use scenario::*;
32
33////////////////////////////////////////////////////////////////////////////////
34/// SL1. Addmendums, labeled AX.Y, made to the original SL rule set.
35///
36/// A1.1
37/// A generic struct representing any target (building, unit, armour, etc).
38/// Every target has a location (in terms of longitude, latitude), and an
39/// elevation. Every target also has an armor value - a value that has to be
40/// super seeded for any destructive force to have a chance to inflict damage
41/// on the target. Finally, every target has a health value - how much damage it
42/// can withstand before it is destroyed.
43struct Target {
44    location: Hex,
45    elevation: u8,
46    armor: u8,
47    health: u8,
48}
49
50/// A struct reponsible for tracking relevant events on the battlefield.
51struct BattleManager {}
52
53/// The ScenarioManager is reponsible for tracking scenrio relevant events on
54/// the battlefield. TODO: give examples of scenerio events.
55struct ScenarioManager {}
56
57/// Marker States
58// A1.2
59// The state of a non-broken combat unit at the start of a new Game Turn.
60struct Unphased;
61// SL5.1
62// Any combat unit that moved during Phase::PrepFire of the current GameTurn.
63// TODO: Decide on naming of states: struct PrepFireMoved;
64struct PrepFired;
65// Asserts SL5.75
66// TODO: consider other solutions here.
67// TODO: what types of support weapons are there?
68struct FiredSupportWeapon;
69
70////////////////////////////////////////////////////////////////////////////////
71/// SL2. Combat Units
72///
73/// SL2.1 Unit: a unit represents any infantry squad, officer, or NCO
74/// (Non-Commissioned Officer), present on the battlefield.
75//  TODO: consider weather to treat Armour crews and AT Gun crews as units.
76/// Unit is a Generic Type representing any unit (leader, squad, armour crew,
77/// Anti Tank Gun crew) on the battlefield.
78#[derive(Debug)]
79enum Unit {
80    // TODO: _consider: Are Armour and ATGun treated differently in terms of
81    // PrepFire and limitations to Movement?
82    Armour,
83    ATGun,
84    Leader,
85    Squad,
86}
87
88impl Unit {
89    fn has_moved(&self) -> bool {
90        todo!()
91    }
92
93    fn has_prepfired(&self) -> bool {
94        todo!()
95    }
96
97    fn broken(&self) -> bool {
98        todo!()
99    }
100
101    fn move_to_dest(&self) -> bool {
102        todo!()
103    }
104}
105
106/// SL2.2 Firepower: the basic strength that a unit can attack with in combat.
107/// SL2.3 Range: the distance, calculated as the least number of game hexes,
108/// from the firing unit to the target, that a unit can reach with its normal
109/// firepower factor.
110/// SL2.4 Morale: a rating of a unit's ability to withstand combat stress before
111/// 'breaking down psychologically and fleeing'. A broken unit will remain
112/// broken until it has received successful treatment by non-broken personel.
113use std::net::Ipv4Addr;
114#[derive(Debug)]
115struct Squad<State> {
116    // The `ars` is an identifying value, based on the United States Army
117    // Regimental System (USARS) - an organizational and classification system
118    // used by the United States Army. The `ars` provides each squad and leader
119    // on the battlefield with a unique identification, within a single
120    // regiment. A squad consists of 6-10 soldiers. To simplify
121    // identification on the battlefield, we have decided to treat the whole
122    // squad as one single entity (solider), with its unique ars.
123    //
124    // Let's say that we want to model the following army:
125    //
126    // + 1 Battalion: 100-1_000 soldiers, comprised of 3-5 companies
127    // + 3 Companies: each with 60-200 soldiers, comprised of 3-4 platoons
128    // + Platoon: 18-50 soldiers, comprised of 3-4 squads
129    // + Squad: comprised of 6-10 soldiers
130    //
131    // Example
132    // ```
133    // use std::net::Ipv4Addr;
134    //
135    // let squad_id = Ipv4Addr::new(Battalion, Company, Platoon, Squad);
136    // //1st Battalion, 3rd Company, 4th Platoon, 4th Squad becomes:
137    // let squad_id = Ipv4Addr::new(1, 3, 4, 4);
138    // ```
139    //
140    pub ars: Ipv4Addr,
141    firepower: u8,
142    range: u8,
143    morale: u8,
144    // Marker Type used to model illegal actions based on state.
145    // See for example SL5.1.
146    _state: marker::PhantomData<State>, // TODO: A way to model that a unit is either broken or non-broken.
147                                        // condition: Condition,
148}
149
150impl Squad<Unphased> {
151    fn has_moved(&self) -> bool {
152        todo!()
153    }
154    fn stacked_with_leader(&self) -> bool {
155        todo!()
156    }
157}
158
159// SL2.6 Leadership affects unit performance
160impl Squad<Unphased> {
161    // TODO: modifier when calculating `unit To Hit` or `unit save morale`.
162    fn get_leadership_modifier(&self) -> u8 {
163        todo!()
164        /*
165        match self.led_by()? {
166            None => 0,
167            Some => self.led_by().leadership(),
168        }
169        */
170    }
171}
172
173/// SL2.5 Identity: the name and rank of the leader unit.
174/// SL2.6 Leadership: a rating of a leader's ability to get the best performance
175/// out of the combat units under his command.
176/// The leadership number, usually negative, is added as a modifier to any
177/// morale or firepower test performed by a unit under the leader's command.
178///
179struct Leader {
180    identity: String,
181    pub leadership: u8,
182    // Morale: a rating of the leader's ability to withstand combat stress
183    // before breaking down psychologically, and fleeing'.
184    // A broken leader is non-operational until receiving treatment by
185    // non-broken personel.
186    //
187    // TODO: Using illegal states, make sure that leadership modifier to units
188    // is only applicable if the leader himself is non-broken.
189    morale: u8,
190    // TODO: _WIP Ideas for fields to add:
191    // Competence: a rating of a leader's tactical competence.
192    //
193    // TODO: A way to model that a leader is either broken or non-broken.
194    // condition: Condition,
195}
196
197// TODO: _code-structure: move to morale
198// TODO: perhaps improve naming of enum?
199// TODO: perhaps add Surrendered?
200/// A non-broken unit is fully able to operate under combat conditions.
201/// A broken unit is psychologically broken down and unable to follow orders.
202/// The unit's survivial instincts will trumph any army discipline until the
203/// unit is rallied into a non-broken state.
204///
205enum Condition {
206    Broken,
207    Composed,
208}
209
210/// SL2.7 Weapon Type: A short description of the weapon type.
211/// All suppport weapons must be operated by combat units.
212/// Enemy support weapons can be captured and used by either side.
213/// SL2.8 Penetration: a support weapon's ability to affect targets that are in
214/// line with the target's `Hex`. A machine gun with a penetration value of 3
215/// will have equal effect on the target hex, but also on the additional 2 hexes
216/// that are in a straight line, along the line of sight, behind the target hex.
217/// A penetration value of 1 will only affect the targeted hex.
218/// SL2.9 _Breakdown_: combat environments are harsh.
219/// To express this, support weapons have a number which determines if the
220/// weapon temporarily breaks down and malfunctions during operation.
221///
222enum WeaponType {
223    Rifle,
224    LMG,
225    MMG,
226    HMG,
227    Mortar,
228    Demolition,
229    Flamethrower,
230    APMine,
231    ATMine,
232    ATRifle,
233    ATGun,
234    TankGun,
235    Artillery,
236}
237
238struct SupportWeapon {
239    weapon: WeaponType,
240    firepower: u8,
241    penetration: u8,
242    range: u8,
243    breakdown: u8,
244}
245
246/// SL18.1
247/// All vehicles must be operated by crews.
248/// Enemy vechicles cannot be captured and used.
249struct Vechicle {
250    mf: u8,
251}
252
253////////////////////////////////////////////////////////////////////////////////
254/// SL3. Maps
255///
256/// Each hex represents a distance of meters on the battlefield.
257/// The default hexdistance is 40 meters, but that value can be changed.
258/// The distance must be chosen with care and with intetion to capture the flow
259/// of tactical small unit infantry combat.
260///
261// TODO: add functionality to change the HEXDISTANCE.
262static HEXDISTANCE: AtomicU8 = AtomicU8::new(40);
263
264/// SL3.2 A hexagon shaped grid is superimposed on the battlefield map.
265/// The default is for the hexagon grid to be six-sided and visible.
266///
267// TODO: Add functionality to toogle the visibility of the hexagon.
268// const mut CONST DRAWHEXAGONS:bool = true;
269///
270/// Every hex has a unique id which can be referenced against a map database.
271/// The map database contains information about what's in the hex, including
272/// elevation data, and the category of terrain that the hex belongs to.
273// TODO: Add map database connectivity functionlality.
274///
275/// SL3.6 Map designers can include aesthetic elements which have no terrain
276/// effect on movement. It's important to note that each hex on a map __can__
277/// belong to more than one terrain category. The effect of this on movement
278/// and combat resolution is __cumulative__.
279/// For example, a hex can contain both a TERRAIN::HEDGES and TERRAIN::WOODS.
280/// The cost of moving through that hex would be calculated by adding the cost
281/// of both terrain types.
282///
283//  TODO: Implement terrain category in database.
284struct Hex {
285    id: u32,
286    elevation: u8,
287    // TODO: not super happy of type-setting terrain as a Vec<Terrain>, but how
288    // else can we handle the fact that a single hex could contain
289    // a house on a hill surrounded by a hedge?
290    terrain: Vec<Terrain>,
291}
292
293impl Hex {
294    fn broken(&self) -> bool {
295        todo!()
296    }
297}
298
299/// Returns the distance from hex A, to hex B in the number of inclusive hexes.
300fn get_distance(origin: Hex, target: Hex) -> u8 {
301    todo!()
302}
303
304/// Returns a boolean indicating if the unit located in origin hex can see
305/// the enemy located in the target hex.
306/// The requires a database hit due to the fact that all line of sight from
307/// to any other hex on the map has been precalculated in to a lookup table.
308/// Each hex has a center which is used to calculate Line of Sight (LOS) to other
309/// hexes, for targeting purposes. A unit (except for mortar units) can only fire
310/// on targets that they can see (that is in their LOS).
311fn get_los(origin: Hex, target: Hex) -> bool {
312    todo!()
313}
314
315/// SL3.1 Terrain will affect how fast units can move through a hex on the map.
316/// The Terrain Effects Chart (or TEC) is a database containing a description of
317/// all terrain types.
318//  TODO: add TEC database and connectivity.
319///
320/// The Terrain Effect Function (TEF) calculates how a particular type of terrain
321/// affects movement. A hex can contain multiple terrain types.
322/// The cost of moving through such a hex is cumulative.
323fn get_terrain_effect_on_move(hex: Hex) -> u8 {
324    // types of terrain in the hex
325    // SL3.8 accumulate terrain types to calculate effect
326    todo!();
327}
328/// SL3.3 Each hex on the map has a center point. This point is used to
329/// calculating line of sight (LOS) between two different hexes on a map.
330/// These points are important because combat units are assumed to be firing
331/// from these center points towards center points in other hexes.
332
333/// The LOS calculations are persisted in the each map database and then
334/// used during simulation to determine if there's a clear LOS between two hexes.
335//  TODO: write a function that can, if provided with a map data file, generate
336//  LOS between two hexes on that map and persist it.
337
338/// SL3.4 Map designers should aim for an `isomorphic` quality in their maps
339/// making it possible to combine the edge of each map to any other map.
340//  TODO: write functionality to determine if two maps can be combined, edge to
341//  edge, and flag if the maps are in fact incompatible.
342/// SL3.7 The half hexes along the edge of the a map are all treated as full
343/// hexes in terms of terrain and line of sight.
344//  TODO: EDGE CASE: edge of maps contain half hexes to enable isomorphic
345//  combination of maps. Write functionality to handle this edge case.
346
347/// SL3.5 Each hex has a unique identifier which commanders can use to communicate
348/// unit movement and targeting.
349//  TODO: consider how Hex of edge hexes is determined for combined maps.
350//  The likely solution is to have the halves of each edge hex to create a new
351//  hex, however the id of that hex is something that needs to be considered.
352/// The classic Hex format is:
353/// 1AB3 where <MAPID(1A)-ROWLETTER(B)-ROWHEXNUMBER(3)>
354fn get_grid_coordinate() -> Hex {
355    // Normally used when clicking on a map and reading the Hex of the hex.
356    todo!()
357}
358
359////////////////////////////////////////////////////////////////////////////////
360/// SL4. Sequence of Play
361///
362/// A game turn consists of two complete eight phase player turns. Meaning, each
363/// player will perform the 8 phases, for a combined total of 16 phases.
364/// The 16 phases represent one complete game turn.
365/// Each game turn represents 2 minutes of actual time.
366use std::sync::atomic::{AtomicU8, Ordering};
367// TODO: add functionality to change the length of a GAMETURN.
368static GAMETURN: AtomicU8 = AtomicU8::new(2);
369
370/// Reaction is slower than action, therefore it is important to distinguish
371/// between the first moving player and the second moving player.
372/// A player can either attack or defend, and just like two boxers, a first
373/// mover may want to preempt an attack by moving into a defensive position -
374/// an action that would improve his defensive posture.
375/// In each game turn, one player will be the defender, while the othe will be
376/// the attacker, they then switch and the defender becomes the attacker.
377/// It should be noted that it's the attacker who goes through the phases.
378enum Player {
379    Attacker,
380    Defender,
381}
382
383/// The player who moves first in a game turn is refered to as the
384/// Player::Attacker, while the other player is refered to as Player::Defender.
385enum Phase {
386    // SL4.1
387    // Both players can attempt to repair malfunctioning support weapons and
388    // attempt to rally broken units.
389    // TODO: add fn repair_malfunctioning_support_weapon()
390    // TODO: add fn rally_broken_unit().
391    Rally,
392    // SL4.2
393    // The Player::Attacker may order any of his units to fire on any
394    // enemy units that are within the unit's line of sight.
395    // TODO: Any unit that fires under this phase is indicated graphically by a
396    // Marker::PrepFire.
397    PrepFire,
398    // SL4.3
399    // Player::Attacker may order unbroken units that did not Phase::PrepFire
400    // to execute a movement order.
401    Movement,
402    // SL4.4
403    // Player::Defender may order any unbroken units to fire at any enemy units
404    // that are either: 1) in their current LOS, or, 2) moved through their LOS
405    // during Phase::Movement.
406    DefensiveFire,
407    // SL4.5
408    // Player::Attacker may now order any of his units who moved during
409    // Phase::Movement to fire at enemy units. The penalty for firing after
410    // movement is that firepower is halfed, rounded down.
411    // TODO: assert that firepower is halved (rounded down) for units that moved.
412    // Units that did neither Phase::PrepFire nor Phase::Movement can fire at their
413    // full firepower.
414    // At the end of the AdvancingFire phase all Marker::PreFire are removed.
415    // TODO: function for removing Marker::Prepfire.
416    AdvancingFire,
417    // SL4.6
418    // Both players, first Player::Attacker, then Player::Defender, __must __
419    // move their broken units into cover of terrain type either
420    // Terrain::Wood or Terrain::Building. A unit that is already in one of
421    // of these two terrain types, need not move, __unless__, it is adjacent
422    // to an enemy unit.
423    // TODO: fn to check if a broken unit has to move despite being located in a
424    // Wood hex or Building hex, due to being adjacent to an enemy unit.
425    Rout,
426    // SL4.7
427    // As a final push, Player::Attacker may now move any of his non-broken units
428    // one hex forward. The hex moved into is allowed to contain enemy units.
429    // This is the only phase in which combat troops are allowed to be moved into
430    // a hex occupied by enemy units.
431    // TODO: add fn to prohibit all movement of opposing units into the
432    // same hex, unless it's Player::Attacker doing so in Phase::Advance.
433    // TODO: consider exception 27, 53.4, 56, 57.
434    Advance,
435    // SL4.8
436    // All units, on both sides, who find themselves occupying the same hex must
437    // attack each other. Results are calculated using the Close Combat Table (CCT).
438    CloseCombat,
439}
440
441// SL4.9 Game Turn
442// After the end of SL4.8 Phase:CloseCombat the Player::Attacker and
443// Player::Defender switches.
444// Once both players have each reached Phase::CloseCombat, then the current
445// game turn is over, and the next one starts, or the game is over.
446//
447// A Game Turn is considered complete when both the attacking entity and the
448// defending entity have gone through steps SL4.1 to SL4.8.
449// TODO: The Battlemanger can now increment the gameturn_counter by 1.
450// TODO: If  a scenario is being modeled then increment the Scenario Turn Record
451// Counter by 1.
452fn update_scenario_turn() {
453    // Phase.counter() mod 16 represents the number of complete Game Turns.
454    // Everytime the result is 0, increment the scenario game turn by one.
455    // Check if scenario game turn has reached limit and the game is over.
456    // TODO: implement Phase.counter() mod 16
457    // TODO: implement check if Phase.counter() > Scenario Turn Record Chart
458    // which would mean that the simulation/game is over.
459    todo!()
460}
461
462////////////////////////////////////////////////////////////////////////////////
463// SL5. Movement
464//
465// A trait describing anything on the battlefield that can be given an order.
466// An `Orderable` type can refuse to execute the order - usually due to being
467// in a morally broken condition, or having surrendered to enemy units.
468
469// For any type to have the Orderable trait, must implement `fn communicate.`
470// TODO: create a trait that can be used to identify units who can communicate.
471pub trait Orderable {
472    fn copythat() -> String;
473}
474
475/// A trait denotating any type that can carry equipment and portable weapons.
476pub trait Carrier {
477    fn carry() -> bool;
478    fn abandon() -> bool;
479}
480
481// Handles: Phase::Advance
482// Any unit that the player selects to move must pass the checks of not being
483fn advance_unit<T: Orderable>(unit: &T, dest: Hex) -> bool {
484    // if unit.condition != Condition::Broken {}
485    todo!()
486}
487
488// A broken unit in cover has to move if it finds itself adjacent to an enemy unit.
489// Handles: Phase::Rout
490fn broken_in_cover() {
491    // For every broken unit, check that no enemy unit is in an adjacent hex.
492    // If an enemy is adjacent then the unit has to be routed into another cover.
493    todo!("check unit is in a hex that is considered: TerrainType::Cover");
494    // TODO: todo!("check that the unit is not adjacent to enemy unit.")
495}
496
497// Fire Orders
498fn order_prep_fire() {
499    todo!("place graphical Marker::PrepFire on units which fired during Phase::PrepFire")
500}
501fn order_defensive_fire() {
502    todo!("Determine which units that are viable targets based on LOS")
503}
504
505fn order_moved_fire(unit: Unit, target: Unit) {
506    // The unit's firepower is halfed because it moved during Phase::Movement
507    // and thus did not have time to properly find targets and aim at them.
508    // unit.firepower / 2
509    todo!("Calculate units halved firepower, rounded up.")
510}
511
512fn order_aimed_fire(unit: Unit, target: Unit) {
513    // The unit neither prep fired nor moved. This enable the unit to fire at
514    // its full firepower.
515    // Unit's which Phase::PrepFire may not fire during the advancing fire phase.
516    if unit.has_moved() {
517        String::from("The unit moved and thus cannot fire during the advanced fire phase");
518    } else {
519        determine_fire_effect(unit, target);
520    }
521    todo!("Fire with full firepower at enemy unit.")
522}
523
524fn determine_fire_effect(attacker: Unit, defender: Unit) {
525    todo!()
526}
527
528// Player::Defender may order any unbroken units to fire at any enemy units
529// that are either: 1) in their current LOS, or, 2) moved through their LOS
530// during Phase::Movement. All targets that passed through the LOS are marked
531// as Marker::EnPassant, after the chess rule.
532fn get_enpassant_targets() -> Vec<Hex> {
533    // For each unit that moved during the movement phase
534    // calculate if it passed through any enemy unit's LOS.
535    // If a unit passed through the LOS of an enemy unit, a Marker::EnPassant
536    // is added to the enemy unit in question.
537    todo!(
538        "Calculate En passant targets and add them to list of viable targets."
539    )
540}
541
542/// OrderResponse
543/// Not every order by the commander can be executed, there can be many reasons why.
544enum OrderResponse {
545    CopyThat, // The unit has received the order and will attempt to execute it."
546    UnitBroken,
547    PrepFired,
548}
549
550fn response_to_order(response: OrderResponse) -> String {
551    match response {
552        OrderResponse::CopyThat => String::from("Copy that, Sir! Executing order."),
553        OrderResponse::UnitBroken => String::from("The unit is broken and unable to execute the order."),
554        OrderResponse::PrepFired => String::from("The unit fired during the prep fire phase and is therefore not eligable for this order."),
555    }
556}
557
558/// Orders
559fn order_move(unit: Unit) {
560    // Conditions to move
561    // 1. The unit is not broken.
562    // 2. The unit did not fire during Phase::PrepFire.
563
564    // is_broken(unit.id)
565    // has_prep_fired(unit.id)
566    if unit.broken() {
567        println!("{}", response_to_order(OrderResponse::UnitBroken));
568    } else if unit.has_prepfired() {
569        println!("{}", response_to_order(OrderResponse::PrepFired));
570    } else {
571        println!("{}", response_to_order(OrderResponse::CopyThat));
572        unit.move_to_dest();
573    }
574
575    // Instead of each game element keeping track of its own state, perhaps it's
576    // better to have an hashmap tracking all broken units
577    // and another hashmap tracking all units that prep fired.
578    todo!("Use hashmap solution instead of struct property.")
579}
580
581// Move a unit from its current location to the provided destination.
582// Terrain effects will affect if the unit can reach the destination.
583fn move_to_dest() {
584    todo!()
585}
586
587/// Markers are used to indicate state and help the player get a sense of what's
588/// going on in the battlefield.
589enum Marker {
590    PrepFire,       // The unit fired during the PrepFire phase.
591    EnPassant, // During its movement phase, the unit passed through one or more enemy unit's LOS .
592    ProximityPanic, // The marker indicates that a broken unit has to be moved because of enemy proximity.
593}
594
595// Fire Phase
596/// Firing into or through a hex with multiple terrain types has a cumulative
597/// effect.
598fn terrain_effect_movement(hex: Hex) -> u8 {
599    todo!()
600}
601
602// Game Loop
603fn game_loop() {
604    todo!("Implement a state machine which steps throug the game phases.");
605}
606
607// Fire
608/// The penetration value is always adjusted to 1 when firing from one elevation
609/// to a different elevation.
610/// Returns the adjusted penetration value of the unit.
611fn sw_calc_penetration(origin: &Hex, target: &Hex) -> Option<u8> {
612    if origin.elevation == target.elevation {
613        Some(1)
614    } else {
615        None
616    }
617    // TODO: move this into the fire sequence of the support weapon?
618}
619
620/// Support weapons that can penetrate more than one hexes can select which hexes
621/// to target, and these hexes need not be adjacent to each other.
622/// For example: a light machine gun with a penetration of 3 can affect three
623/// hexes in total, the target hex and two more hexes along it's line of sight.
624/// In the following array, T is the target hex. The first and second P represent
625/// hexes that are not adjacent to eacher, but they can still be penetrated.
626/// However, there cannot be any obstacles (like buildings and woods) between
627/// the penetration hexes.
628/// -> [_,T,_,P,_,P,_,_]
629fn can_penetrate() -> bool {
630    todo!()
631}
632
633fn terrain_effect_combat(hex: Hex) -> u8 {
634    // types of terrain in the hex
635    // accumulate terrain types to calculate effect
636    todo!()
637}
638
639// Close Combat
640// Handles: Phase::CloseCombat
641fn close_combat() {
642    // For all opposing units in the same hex
643    // use the Close Combat Table to calculate results
644    // Update state (remove casulties).
645}
646
647////////////////////////////////////////////////////////////////////////////////
648// SL5. Movement
649//
650// TODO: look to create a global const that can be set.
651// TODO: check if this information is redundant.
652// Each hex in the game represents 40 meters (from hex border to hex border).
653//
654// SL5.1 Only units that _did not fire_ during the Phase::PrepFire may move
655// during the ensuing Phase::Movement.
656// TODO: mechanism for limiting condition to _ensuing_.
657// TODO: _consider using TypeState to assert rules.
658impl BattleManager {
659    fn unit_can_move(&self, u: &Unit, p: &Phase) {
660        todo!()
661        /*
662        // Check the HashMap, did the unit move during the prep fire phase?
663        match !self.Moved.contains_key(p, u) {
664            Ok() => return true,
665            Err(e) => return RuleBreak::E5100, //UNIT_TRYING_TO_MOVE_POST_PREP_FIRED
666        }
667        */
668    }
669}
670
671// SL5.2 Units can move inside the limits of their Movement Factors (MF).
672// The number of hexes that the unit can move is a function of its MF and
673// various factors, such as leaders, terrain, presence of enemy units, items
674// carried, enemy fire, etc.
675
676// SL5.3.1 Unites can move over a hex containing other units.
677// SL5.3.2 One hex can contain more than one unit but not more than ???
678// TODO: decide how many units that can be stacked in one hex.
679
680// SL5.4 All counters, except vehicles, have movement factors (MF) alloted to them:
681// Support Weapons must be carried by Squad, Leader, or Vehicle
682// TODO: rules to handle movement of support weapons by leader, squad, or vehicle.
683enum MF {
684    Squad = 4, // SL5.41
685    Leader = 6, // SL5.42
686               // Vehicles MF is a field of vechicle structs.
687               // TODO: implement Vehicle struct with MF.
688}
689
690// SL5.44 If a squad spends the entire Phase::Movement in the company with
691// a leader, then it will recive a MF bonus of 2.
692// TODO: handle led by Leader and stacked with Leader for entire movement phase.
693fn calcuate_mf_bonus(s: Squad<Unphased>) {
694    todo!()
695    /*
696    if s.stacked_with_leader() {
697        2
698    } else {
699        0
700    }
701    */
702}
703
704// SL5.5 Moving into a hex has a MF cost, depending on the type of terrain.
705// SL5.51 Crossing walls and hedges places a 1 MF penalty on the units MF.
706// SL5.52 A unit moving from one road hex to another will only pay 1 MF for every second
707// road hex it traverses.
708// TODO: read up on enums and data types.
709enum Terrain {
710    OpenGround,
711    Shellhole,
712    Wheatfield,
713    OnRoad,
714    OntoRoad,
715    Woods,
716    EnterBuilding,
717    WithinBuilding,
718    OverWall,
719}
720
721impl Terrain {
722    //TODO: based on unit type or armour.
723    //TODO: _consider: should these be pulled from db?
724    fn movement_cost(&self) -> u32 {
725        match self {
726            Terrain::OpenGround => 1,
727            Terrain::Shellhole => 1,
728            Terrain::Wheatfield => 1,
729            Terrain::OnRoad => 1,
730            Terrain::OntoRoad => 1,
731            Terrain::Woods => 2,
732            Terrain::EnterBuilding => 2,
733            Terrain::WithinBuilding => 2,
734            Terrain::OverWall => 1,
735        }
736    }
737}
738// SL5.53 A Squad moving upwards, for example from TerrainCost::OpenGround;
739// OnRoad, Building, and Woods, from one terrain level to a higher one, will
740// double its MF cost.
741//
742// Moving along same level carries no bonus or additional cost.
743// Nor is there a bonus for moving from one level to a lower level.
744// TODO: add functionality to double terrain cost due to higher ground.
745// TODO: verify the use of Type State below.
746impl Squad<Unphased> {
747    fn tfx_higher_ground(&self, orig: Hex, dest: Hex) -> bool {
748        dest.elevation > orig.elevation
749    }
750}
751
752// SL5.54 Terrain effects are cummulative: hexes containig more than one terrain
753// type have the MF of each type added together.
754// TODO: add fn to cummulate terrain fx.
755
756// SL5.6 Infantry units can move up to and around a hex containing enemy units
757// but may only move into a hex containing an enemy unit during Phase:Advance.
758// TODO: add fn which prohibts movements into enemy controlled hex.
759
760// SL5.70-Carrying support weapons and portage costs.
761//
762
763// SL5.73 Regardless of terrain and/or weapons portage, a squad or crew may
764// always carry up to 5 (TODO: turn into global CONST?) portage points, and
765// a leader carry 3 portage points, up to 1 hex during the `Phase::Advance`.
766fn carry_during_advance_phase(c: impl Carrier, p: Phase) {
767    todo!()
768    /*
769    if c::Squad && u.portage < 6 && p == Phase::Advance {
770        // TODO: instruct BattleManager / SceneManger to move unit one hex.
771    } else if u::Leader && u.portage < 4 && p::Advance {
772        // TODO: instruct BattleManager / SceneManger to move unit one hex.
773    } else {
774        // TODO: Improve mechanism for Informing General.
775        println!("Unit is too encoumbered to move.")
776    }
777    */
778}
779
780// SL5.74 A `Squad` carrying 4 or more portage points, or a `Leader` carrying 2
781// or more portage points (TODO: turn into global CONST), during the
782// `Phase::Movement` may _not_ fire a `SupportWeapon` during the _ensuing_
783// `Phase::AdvancedFire`.
784fn may_fire_support_weapon(u: Unit, s: SupportWeapon, p: Phase) {
785    todo!()
786    /*
787    if p::AdvanceFire && (u::Leader.portage < 2 || u::Squad.portage < 4) {
788        // May fire support weapon.
789        // TODO: issue to handler responsible for the firing of weapons.
790    } else {
791        // TODO: add "Inform General" functionality to inform when commands are
792        // break a rule. Also log to BattleManager?
793        //
794        println!("Unit {:?} may not fire the support weapon {:?} because the unit
795        exceeded the allowed portage points during the movement phase {:?}.", u, s, p);
796    }
797    */
798}
799
800// Handled by A1.2.
801// TODO: move this to fire section.
802// SL5.75 An infantry unit may only fire one _type_ of `SupportWeapon` in the
803// same Phase::FirePhase, Phase::PrepFire, or Phase::AdvanceFire.
804// WeaponType::{LMG, MMG, HMG} are all considred weapons of the same _type_.
805// TODO: enforce this how? Perhaps by each unit tracking which weapon type
806// it has fired in the same fire phase:
807//
808// ```
809// Unit.fire(w: WeaponType) {
810//  if !Unit.weapon_types_fired.contains(w) {
811//      w.fire();
812//      Unit.weapon_types_fired.insert(w)
813//  }
814// }
815// ```
816// ...or let a battlemanager track and decide if a unit is allowed to fire
817// a particular type of support weapon.
818
819// SL5.76 Portage costs are assumed to refer to `Squad` usage costs.
820// TODO: Consider how `Leader` should be handled with relation to portage cost.
821
822////////////////////////////////////////////////////////////////////////////////
823/// SL6. Stacking
824
825////////////////////////////////////////////////////////////////////////////////
826/// SL7. Line of Sight (LOS)
827// SL7.1 Line of sight - what a unit can "see" and fire upon, is measured from
828// the center dot of the hex (of the unit) and a straight line to the center dot
829// of the target hex.
830// If an obstacle on the map can be observed on both sides of this line then the
831// LOS is obstructed.
832fn los_obstructed(orig: Hex, dest: Hex) -> bool {
833    // TODO: setup a database to hold all hex data, including LOS
834    todo!("Look-table or database hit to get LOS for passed hexes.")
835}
836
837// SL7.2 LOS is only blocked if something in the hex is actually blocking it.
838// It's not enough that there's woods or a build in the hex, the LOS actually
839// has to be blocked.
840// TODO: fn to verify LOS obstructed by gfx in hex.
841// Weapon's fire may be traced through a hex containg units without affecting
842// them, if the firer prefers this.
843// TODO: option to fire through without affecting unit blocking LOs.
844// TODO: Exception: 17.6
845
846// SL7.3 LOS extends into woods and buildings but not through them.
847// TODO: verify that LOS is obstructed by second woods/building hex so that
848// fire is not able to pass through woods/buildings.
849
850// SL7.4
851
852////////////////////////////////////////////////////////////////////////////////
853// SL8
854// SL15 Leadership
855// SL16 Defensive Fire Principles
856// SL17 Machine Guns
857// SL18 Fate
858// SL19 Movement and Fire
859// SL20 Close Combat
860
861////////////////////////////////////////////////////////////////////////////////
862/// Tests
863#[cfg(test)]
864mod tests {
865    #[test]
866    fn this_is_a_test() {
867        let res = 2 + 2;
868        assert_eq!(res, 4);
869    }
870}