|
@@ -0,0 +1,153 @@
|
|
|
|
+;;; Combat systems implemented here
|
|
|
|
+
|
|
|
|
+(defvar *combat-menu-options-display* "
|
|
|
|
+Actions:
|
|
|
|
+1 | Attack | a
|
|
|
|
+2 | Regen. Shields | r
|
|
|
|
+3 | Overcharge Gun | o
|
|
|
|
+4 | Attempt to flee | f
|
|
|
|
+")
|
|
|
|
+
|
|
|
|
+;; Bizarre Core Assumptions TODO?
|
|
|
|
+;; * Whenever we think of engaging an enemy, the actual enemy object
|
|
|
|
+;; is always whatever the first element of the (enemy-ships *sector*)
|
|
|
|
+;; list. This is done because I'm lazy and it's fairly easy, will
|
|
|
|
+;; figure out a different way if/when it's needed, but in essence,
|
|
|
|
+;; the player typically does not make a choice about the characteristics
|
|
|
|
+;; of the enemy they fight, the game chooses for you. If this is true,
|
|
|
|
+;; this method is fine.
|
|
|
|
+
|
|
|
|
+;; Combat options:
|
|
|
|
+;; Attack (with specific weapon)
|
|
|
|
+;; Regen shields
|
|
|
|
+;; - (add value to rep-shield-val at cost of overcharing
|
|
|
|
+;; - (overcharing can cause warp field to go into low-power)
|
|
|
|
+;; - (overcharging can cause hull damage)
|
|
|
|
+;; Overcharge gun at cost of overcharing reactor
|
|
|
|
+;; Attempt to flee into the warp
|
|
|
|
+;; - (requires full power / overcharged reactor)
|
|
|
|
+;; - (can also cause reactor to go low power, which removes ability to regen/overcharge)
|
|
|
|
+
|
|
|
|
+;; TODO - Combat always pops off first enemy from the enemy-ships list
|
|
|
|
+(defun combat-loop (sector)
|
|
|
|
+ "The main loop responsbile for resolving combat situations"
|
|
|
|
+ (let ((combat-state T))
|
|
|
|
+ (loop
|
|
|
|
+ while combat-state
|
|
|
|
+ do (progn
|
|
|
|
+ (format T *combat-menu-options-display*)
|
|
|
|
+ (let ((top-level-selection (prompt-read "Enter a selection: ")))
|
|
|
|
+ (case (read-from-string top-level-selection)
|
|
|
|
+ (1 (progn ;; There is a way to collapse this progn into a single
|
|
|
|
+ ;; function call, but it's arguably a lot less readable
|
|
|
|
+ (let* ((weapon (weapon-select-menu (player-ship-obj sector)))
|
|
|
|
+ (attack-result (player-ship-attack
|
|
|
|
+ (player-ship-obj sector)
|
|
|
|
+ (first (enemy-ships sector))
|
|
|
|
+ weapon)))
|
|
|
|
+ (if attack-result
|
|
|
|
+ (progn
|
|
|
|
+ (setf (enemy-ships sector) (delete attack-result (enemy-ships sector)))
|
|
|
|
+ (format T "Your opponent has been vanquished!~%")
|
|
|
|
+ (top-level-game-menu))))
|
|
|
|
+ (enemy-turn sector)))))))))
|
|
|
|
+
|
|
|
|
+;; IN PROGRESS
|
|
|
|
+;; (defun enemy-turn (sector)
|
|
|
|
+;; "Perform AI actions based on AI-type associated with enemy ship"
|
|
|
|
+;; (flet ((enemy-attack (player-ship-obj enemy-obj)
|
|
|
|
+;; ;; For now, just use the first weapon
|
|
|
|
+;; (let ((weapon-obj ((first (weapons enemy-obj))))
|
|
|
|
+;; (ammo
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+;; (case (ai-type (first (enemy-ships sector)))
|
|
|
|
+;; (aggressive (progn
|
|
|
|
+;; ;; This AI will just stupidly attack until it's out of ammo
|
|
|
|
+;; ;; If it runs out of ammo, it will try and flee
|
|
|
|
+
|
|
|
|
+;; ))
|
|
|
|
+;; (fearful () ;; This AI will basically just try to escape, unless it fails
|
|
|
|
+;; ;; twice in a row, in which it will try to attack
|
|
|
|
+;; )
|
|
|
|
+;; (unpredicatable ())))
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+;; Select weapon
|
|
|
|
+;; Determine if hit (% chance?)
|
|
|
|
+;; If hit resolve shield
|
|
|
|
+;; -- IF Shields down (ie shield damage causes target shield value to be 0)
|
|
|
|
+;; -- Apply hull damage
|
|
|
|
+;; ELSE
|
|
|
|
+;; -- Decrement shield value per value of shield dmg
|
|
|
|
+
|
|
|
|
+(defun weapon-select-menu (player-ship-obj)
|
|
|
|
+ (let ((weapon-list (loop for weapon in (weapons player-ship-obj)
|
|
|
|
+ for i from 0
|
|
|
|
+ collect (list i
|
|
|
|
+ (name weapon)
|
|
|
|
+ (shield-dmg weapon)
|
|
|
|
+ (hull-dmg weapon)
|
|
|
|
+ (ammo-cost weapon)))))
|
|
|
|
+ (format T "~%Available Ammo: ~A~%~%" (ammo (inventory player-ship-obj)))
|
|
|
|
+ (format T "Available Weapons: ~%")
|
|
|
|
+ (format-table T weapon-list :column-label '("Number" "Name" "Shield Damage" "Hull Damage" "Ammo Cost"))
|
|
|
|
+ (let ((selection (prompt-read "Select a weapon to via the number column: ")))
|
|
|
|
+ (format t "Selection was number: ~A and weapon name: ~A~%~%"
|
|
|
|
+ selection
|
|
|
|
+ (second (nth (parse-integer selection) weapon-list)))
|
|
|
|
+ (return-from weapon-select-menu (nth (parse-integer selection) (weapons player-ship-obj))))))
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+(defun calculate-shield-damage (attacker-obj target-obj attacker-weapon)
|
|
|
|
+ "Given a target and a weapon, return a value that the
|
|
|
|
+ targets shield value should now be set as"
|
|
|
|
+ (let ((new-shield-value (- (rep-shield-val target-obj) (shield-dmg attacker-weapon))))
|
|
|
|
+ (setf (ammo (inventory attacker-obj)) (- (ammo (inventory attacker-obj)) (ammo-cost attacker-weapon)))
|
|
|
|
+ (if (>= 0 new-shield-value)
|
|
|
|
+ (return-from calculate-shield-damage 0)
|
|
|
|
+ (return-from calculate-shield-damage new-shield-value))))
|
|
|
|
+
|
|
|
|
+(defun calculate-hull-damage (attacker-obj target-obj attacker-weapon)
|
|
|
|
+ "Given a target and a weapon, return a value that the
|
|
|
|
+ targets hull value should now be set as"
|
|
|
|
+ (let ((new-hull-value (- (armor-val target-obj) (hull-dmg attacker-weapon))))
|
|
|
|
+ (setf (ammo (inventory attacker-obj)) (- (ammo (inventory attacker-obj)) (ammo-cost attacker-weapon)))
|
|
|
|
+ (if (>= 0 new-hull-value)
|
|
|
|
+ (return-from calculate-hull-damage 0)
|
|
|
|
+ (return-from calculate-hull-damage new-hull-value))))
|
|
|
|
+
|
|
|
|
+(defun player-ship-attack (player-ship-obj target-obj attacker-weapon)
|
|
|
|
+ "Given a target and the attackers chosen weapon resolve the combat by
|
|
|
|
+ calculating shield/hull damage and resolving resources. If the target
|
|
|
|
+ is reduced to 0 hull points, return the target-obj and end combat"
|
|
|
|
+ (if (> (ammo-cost attacker-weapon) (ammo (inventory player-ship-obj)))
|
|
|
|
+ (progn
|
|
|
|
+ (format T "You don't have enough ammo to fire your ~A gun!~%" (name attacker-weapon))
|
|
|
|
+ (return-from player-ship-attack NIL)))
|
|
|
|
+ (let ((calculated-shield-damage (calculate-shield-damage player-ship-obj target-obj attacker-weapon))
|
|
|
|
+ (calculated-hull-damage (calculate-hull-damage player-ship-obj target-obj attacker-weapon)))
|
|
|
|
+ (setf (rep-shield-val target-obj) calculated-shield-damage) ;; Resolve shield hit
|
|
|
|
+ (if (> (rep-shield-val target-obj) 0)
|
|
|
|
+ (format T "You hit their repulsor shields for ~A !~%" (shield-dmg attacker-weapon)))
|
|
|
|
+ (if (= 0 calculated-shield-damage)
|
|
|
|
+ (progn
|
|
|
|
+ (format T "Their shields are down!~%")
|
|
|
|
+ (setf (armor-val target-obj) calculated-hull-damage)
|
|
|
|
+ (format T "You hit their hull for ~A !~%" (hull-dmg attacker-weapon))))
|
|
|
|
+ (if (= 0 calculated-hull-damage)
|
|
|
|
+ (progn
|
|
|
|
+ (format T "Their hulls been breached! You've won the exchange!~%")
|
|
|
|
+ (return-from player-ship-attack target-obj))
|
|
|
|
+ NIL)))
|
|
|
|
+
|
|
|
|
+(defun display-weapons (player-ship-obj)
|
|
|
|
+ "Given player ship, display weapons and associated attributes"
|
|
|
|
+ (let* ((weapons (weapons player-ship-obj))
|
|
|
|
+ (weapon-list (loop for weapon in weapons
|
|
|
|
+ collect (list (name weapon)
|
|
|
|
+ (shield-dmg weapon)
|
|
|
|
+ (hull-dmg weapon)
|
|
|
|
+ (ammo-cost weapon)))))
|
|
|
|
+ (format T "~%WEAPON DETAILS~%")
|
|
|
|
+ (format-table T weapon-list :column-label '("Name" "Shield Damage" "Hull Damage" "Ammo Cost"))))
|