diff --git a/README.org b/README.org index 2feafaa..2b557dc 100644 --- a/README.org +++ b/README.org @@ -15,61 +15,31 @@ Once both of those dependencies are installed, run: ./bitter-duel #+end_src -* Design & Gameplay +* How To Play +First to 3 hits wins. -In Bitter Duel, you control a single character on a 5 by 5 grid. There is a single opponent facing off against you. +You are blue. -Each character has a stance and hand position, and can move, attack, or assume a stance on their turn. -Each character has 3 health. +Each unit on the board has a stance and a hand position. + +The stance can be high, mid, or low. You can change your stance each turn. + +Hand position can be high-right, high-left, mid-right, mid-left, low-right, low-left --- forming a circle. +Each turn you can choose to attack from your current hand position, or from an adjacent one on the circle. +For example, from high-right, you can attack from high-right, high-left, or mid-right. +After the attack, your hand moves to the opposite position. +For example, attacking from high-right, your hand becomes low-left. + +You can move 1 square on each turn. + +Build your order: move, change stance, and attack. + +Orders are submitted simultaneously and then resolved: movement first, then stance change, then attack if in range (neighboring squares). ** Stances -Stances are initial striking positions, and provide some intrinsic bonus offense and defense on the turn that the stance is assumed. +Your chance to hit is higher if your attack matches your stance. A mid-right attack has a lower chance of hidding from a high stance than a high-right attack. -After attacking or moving, your stance is broken until you next assume it. - -There are 4 stances: -- High (Up-Left/Up-Right) - - The High stance has a higher defense (70) against High and (60) Stab attacks, but low defense against Low attacks (30). - - The High stance adds additional offense to your High attacks (80). -- Low (Down-Left/Down-Right) - - The Low stance has higher defense (70) against Low and (60) Stab attacks, but low defense against High attacks (30). - - The low stance adds additional offense to your Low attacks (70). -- Side (Mid-Left/Mid-Right) - - The Side stance has higher defense (70) against swing attacks, but low defense (30) against High and Low attacks. - - The Side stance adds additional offense to your Swing attacks (70) - -** Hand Position -Your hand position adds defense against attacks coming from that direction (60) and dictates what next attacks you can perform. - -The hand positions are: -- Up-left: - - Defense against top and right attacks. -- Up-right - - Defense against top and left attacks. -- Mid-right - - Defense against mid and left attacks. -- Mid-left - - Defense against mid and right attacks. -- Down-left - - Defense against bottom and right attacks. -- Down-right - - Defense against bottom and left attacks. - -** Attacks -Each of the hand positions is also a type of attack. You may perform an attack from the 3 positions near the hand position. -For example, Up-Left can attack from Up-Left, Up-Right, or Mid-Left. -After an attack, your hand position changes to the opposite of the attack. So Up-Left becomes Down-Right. - -** Attacking and Turns -Both players lock in their actions, then the turn is resolved like so: -1. Handle any stance changes -2. Handle any movement. -3. Resolve attacks. - -To resolve an attack, get the offense of the attack and defense for that attack from the defender. -Subtract Defense from Offense, divide by 10, and add 5. Call this the "target number". -Roll a number between 1 and 10. If the roll is lower than the target number, the attack goes through and the defender takes a hit. -Characters always attack and defend from the hand position they start in at the beginning of the turn. - -* Credits -- https://sethbb.itch.io/32rogues +Your stance also determines your defense. +A high stance has bonus defense against high attacks, but weak defense against low attacks. +A low stance is the opposite. +A mid stance has bonus defense against mid attacks, but weak defense against high and low. diff --git a/modules/attack.scm b/modules/attack.scm new file mode 100644 index 0000000..1e03828 --- /dev/null +++ b/modules/attack.scm @@ -0,0 +1,143 @@ +(module (bd attack) () +(import scheme + (chicken base) + (chicken module) + (chicken string) + (bd random) + (srfi 99)) + +(export hand-direction hand-vert hand-horiz) +(define-record-type + (hand-direction vert horiz) + hand-direction? + (vert hand-vert) + (horiz hand-horiz)) + +(export direction-to-string) +(define (direction-to-string dir) + (conc (hand-vert dir) + " : " + (hand-horiz dir))) + +(define (opposite-horiz h) + (if (eqv? h 'right) + 'left + 'right)) + +(define (opposite-vert v) + (cond + ((eqv? v 'high) 'low) + ((eqv? v 'mid) 'mid) + ((eqv? v 'low) 'high))) + +(export opposite-pos) +(define (opposite-pos h) + (hand-direction + (opposite-vert (hand-vert h)) + (opposite-horiz (hand-horiz h)))) + +(define (pos-= h1 h2) + (and (eqv? (hand-vert h1) (hand-vert h2)) + (eqv? (hand-horiz h1) (hand-horiz h2)))) + +(define (pos-to-int h) + (cond + ((pos-= h (hand-direction 'high 'right)) 0) + ((pos-= h (hand-direction 'high 'left)) 1) + ((pos-= h (hand-direction 'mid 'left)) 2) + ((pos-= h (hand-direction 'low 'left)) 3) + ((pos-= h (hand-direction 'low 'right)) 4) + ((pos-= h (hand-direction 'mid 'right)) 5))) + +(define (int-to-pos i) + (case i + ((0) (hand-direction 'high 'right)) + ((1) (hand-direction 'high 'left)) + ((2) (hand-direction 'mid 'left)) + ((3) (hand-direction 'low 'left)) + ((4) (hand-direction 'low 'right)) + ((5) (hand-direction 'mid 'right)))) + +(export rotate-pos rotate-pos-cc) +(define (rotate-pos p) + (let ((pos (pos-to-int p))) + (if (= pos 5) + (int-to-pos 0) + (int-to-pos (+ 1 pos))))) + +(define (rotate-pos-cc p) + (let ((pos (pos-to-int p))) + (if (= pos 0) + (int-to-pos 5) + (int-to-pos (- pos 1))))) + +(export attack attack-direction attack-offense) +(define-record-type + (attack direction offense) + attack? + (direction attack-direction) + (offense attack-offense)) + +(define (high-stance-attack dir) + (if (eqv? 'high (hand-vert dir)) + 80 + 50)) + +(define (mid-stance-attack dir) + (if (eqv? 'mid (hand-vert dir)) + 80 + 50)) + +(define (low-stance-attack dir) + (if (eqv? 'low (hand-vert dir)) + 80 + 50)) + +(export attack-from-stance) +(define (attack-from-stance attack-direction stance) + (attack attack-direction + (cond + ((eqv? stance 'high) (high-stance-attack attack-direction)) + ((eqv? stance 'mid) (mid-stance-attack attack-direction)) + ((eqv? stance 'low) (mid-stance-attack attack-direction)) + (else 50)))) + +(define (high-stance-defense dir) + (cond + ((eqv? 'high (hand-vert dir)) 20) + ((eqv? 'low (hand-vert dir)) -30) + (else 0))) + +(define (mid-stance-defense dir) + (cond + ((eqv? 'mid (hand-vert dir)) 20) + (else -30))) + +(define (low-stance-defense dir) + (cond + ((eqv? 'low (hand-vert dir)) 20) + ((eqv? 'high (hand-vert dir)) -30) + (else 0))) + +(export defense-from-stance) +(define (defense-from-stance attack-dir hand-pos stance) + (+ 40 + (if (or (eqv? (hand-vert attack-dir) + (opposite-vert (hand-vert hand-pos))) + (eqv? (hand-horiz attack-dir) + (opposite-horiz (hand-horiz hand-pos)))) + 20 + 0) + (cond + ((eqv? stance 'high) (high-stance-defense attack-dir)) + ((eqv? stance 'mid) (mid-stance-defense attack-dir)) + ((eqv? stance 'low) (low-stance-defense attack-dir)) + (else 0)))) + +(export resolve-combat) +(define (resolve-combat attack defense) + (let ((tn (+ 5 (/ (- (attack-offense attack) defense) 10)))) + (if (> tn (+ 1 (rand-int 10))) + 'hit + 'miss))) +) diff --git a/modules/grid.scm b/modules/grid.scm new file mode 100644 index 0000000..dc0015d --- /dev/null +++ b/modules/grid.scm @@ -0,0 +1,74 @@ +(module (bd grid) () +(import scheme + (chicken base) + (chicken module) + raylib + (imugi core) + (imugi drawing) + (imugi math) + (srfi 1) + (srfi 99)) + +(export grid) +(define (grid len wid default) + (define (iter i acc) + (if (= len i) + acc + (iter + (+ 1 i) + (cons (make-list wid default) acc)))) + (iter 0 '())) + +(export gv) +(define (gv grd x y) + (list-ref (list-ref grd y) x)) + +(export gv!) +(define (gv! grd x y val) + (set! (list-ref (list-ref grd y) x) val)) + +(define (draw-grid-square offset width x y entity) + (let ((square-pos (v+ offset + (vec (* x width) + (* y width))))) + (push-render-object + 'screen + 0 + (lambda () + (draw-rectangle-2d + square-pos + width + width + (cond + ((eqv? entity 'player) (make-color 0 0 1 1)) + ((eqv? entity 'foe) (make-color 1 0 0 1)) + (else (make-color 0 0 0 1))) + (not (eqv? entity 'none)) + 2))))) + +(export draw-grid) +(define draw-grid + (make-system + 'draw-grid + 10 + 'entity + '() + (lambda (_ grid-view) + (let ((gd (grid-view-grid grid-view)) + (width (grid-view-width grid-view)) + (pos (grid-view-pos grid-view))) + (do ((i 0 (+ 1 i))) + ((= i (length gd)) gd) + (let ((row (list-ref gd i))) + (do ((j 0 (+ 1 j))) + ((= j (length row)) row) + (draw-grid-square pos width i j (list-ref row j))))))))) + +(export grid-view grid-view-grid) +(define-record-type + (grid-view start-pos gd width) + grid-view? + (start-pos grid-view-pos set-grid-view-pos!) + (gd grid-view-grid set-grid-view-grid!) + (width grid-view-width set-grid-view-width!)) +) diff --git a/modules/random.scm b/modules/random.scm new file mode 100644 index 0000000..60b74bc --- /dev/null +++ b/modules/random.scm @@ -0,0 +1,15 @@ +(module (bd random) () +(import scheme + (chicken base) + (chicken module) + (chicken random)) + +(export random) +(export rand-int) +(define random pseudo-random-real) +(define rand-int pseudo-random-integer) + +(export pick-random) +(define (pick-random lst) + (list-ref lst (rand-int (length lst)))) +) diff --git a/modules/ui.scm b/modules/ui.scm index d6f120b..e6e6676 100644 --- a/modules/ui.scm +++ b/modules/ui.scm @@ -29,6 +29,22 @@ (color label-color set-label-color!) (text label-text set-label-text!)) +(export process-dynamic-labels) +(define process-dynamic-labels + (make-system + 'process-dynamic-labels + 9 + 'entity + '(