Basic main menu and README

This commit is contained in:
Jakub 2026-05-17 20:52:13 +08:00
parent 693879746b
commit d450a0d155
8 changed files with 449 additions and 0 deletions

2
.dir-locals.el Normal file
View file

@ -0,0 +1,2 @@
((nil . ((geiser-default-implementation . chicken)
(geiser-active-implementations . (chicken)))))

75
README.org Normal file
View file

@ -0,0 +1,75 @@
#+title: Bitter Duel
This is a really simple tactics game for the [[https://itch.io/jam/spring-lisp-game-jam-2026][2026 Spring Lisp Game Jam]].
It is made in Chicken Scheme, using my own game framework [[https://git.cyan.sh/BirDt/imugi][Imugi]].
* Build
First install Chicken 5, then install Imugi using the instructions in its repo. You will also need to ~chicken-install csm~ for easier building.
Once both of those dependencies are installed, run:
#+begin_src shell
mkdir build && cd build
csm -program bitter-duel ..
./bitter-duel
#+end_src
* Design & Gameplay
In Bitter Duel, you control a single character on a 5 by 5 grid. There is a single opponent facing off against you.
Each character has a stance and hand position, and can move, attack, or assume a stance on their turn.
Each character has 3 health.
** Stances
Stances are initial striking positions, and provide some intrinsic bonus offense and defense on the turn that the stance is assumed.
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

1
all.options Normal file
View file

@ -0,0 +1 @@
-static -L /usr/local/lib/libraylib.a -L -lX11 -L -lXrandr -L -lXi -L -lXcursor -L -lXinerama -L -lXext -L -lGL -L -lm -L -lpthread -L -ldl -L -lrt

198
modules/ui.scm Normal file
View file

@ -0,0 +1,198 @@
(module (bd ui) ()
(import scheme
(chicken base)
(chicken module)
raylib
(imugi core)
(imugi math)
(imugi drawing)
(imugi resource)
(imugi input)
(srfi 1)
(srfi 4)
(srfi 99))
(define base-font (font "../res/fonts/Felipa-Regular.ttf"))
(define-record-type <label>
(int:make-label position centered? layer font font-size color text)
label?
(position label-pos set-label-pos!)
(centered? label-centered? set-label-centered!)
(layer label-layer set-label-layer!)
(font-size label-size set-label-size!)
(font label-font set-label-font!)
(color label-color set-label-color!)
(text label-text set-label-text!))
(export title)
(define (title position text
#!key
(centered (cons #f #f))
(layer 0)
(font base-font)
(size 128)
(color (make-color 0 0 0 1)))
(int:make-label position centered layer font size color text))
(export subtitle)
(define (subtitle position text
#!key
(centered (cons #f #f))
(layer 0)
(font base-font)
(size 36)
(color (make-color 0 0 0 1)))
(int:make-label position centered layer font size color text))
(export footer)
(define (footer position text
#!key
(centered (cons #f #f))
(layer 0)
(font base-font)
(size 24)
(color (make-color 0 0 0 1)))
(int:make-label position centered layer font size color text))
(export draw-labels)
(define draw-labels
(make-system
'draw-labels
10
'entity
'(<label>)
(lambda (_ label)
(push-render-object
'screen
(label-layer label)
(lambda ()
(let* (;; measure text length in pixels in case
;; we need it for horizontal centering
(text-length (f32vector->list
(measure-text-ex
(resource-contents (label-font label))
(label-text label)
(label-size label)
1)))
;; center the x/y of the position
;; based on label-centered?
(draw-pos (vec
(if (car (label-centered? label))
(- (/ (car (*window-size*))
2)
(/ (car text-length)
2))
(v-x (label-pos label)))
(if (cdr (label-centered? label))
(- (/ (cdr (*window-size*))
2)
(cadr text-length))
(v-y (label-pos label))))))
(draw-font-text-2d
draw-pos
(label-text label)
(label-size label)
(label-color label)
(label-font label))))))))
(define-record-type <button>
(int:make-button position size layer label state click-fn colors)
button?
(position button-pos set-button-pos!)
(size button-size set-button-size!)
(layer button-layer set-button-layer!)
(label button-label set-button-label!)
(state button-state set-button-state!)
(click-fn button-click-fn set-button-click-fn!)
(colors button-colors set-button-colors!))
(export button)
(define (button position label click-fn
#!key
(layer 0)
(size (vec 100 60))
(colors (list (make-color 0.48 0.48 0.48 1)
(make-color 0.63 0.63 0.63 1)
(make-color 0.39 0.39 0.39 1))))
(int:make-button position size layer label 'ready click-fn colors))
(define (check-button-state button)
(let ((x-range (cons (v-x (button-pos button))
(+ (v-x (button-pos button))
(v-x (button-size button)))))
(y-range (cons (v-y (button-pos button))
(+ (v-y (button-pos button))
(v-y (button-size button)))))
(mouse-pos (cons (get-mouse-x) (get-mouse-y))))
(if (and (< (car x-range) (car mouse-pos) (cdr x-range))
(< (car y-range) (cdr mouse-pos) (cdr y-range)))
(if (peek-event 'input 'click)
(set-button-state! button 'clicked)
(set-button-state! button 'hovered))
(set-button-state! button 'ready))))
(define (draw-button button)
(push-render-object
'screen
(button-layer button)
(lambda ()
(draw-rectangle-2d
(button-pos button)
(number->integer (v-x (button-size button)))
(number->integer (v-y (button-size button)))
(list-ref (button-colors button)
(case (button-state button)
((ready) 0)
((hovered) 1)
((clicked) 2)))
#t
1)))
(let* ((txt (button-label button))
(text-length (f32vector->list
(measure-text-ex
(resource-contents (label-font txt))
(label-text txt)
(label-size txt)
1)))
(center-offset (vec (- (/ (v-x (button-size button))
2)
(/ (car text-length)
2))
(- (/ (v-y (button-size button))
2)
(/ (cadr text-length)
2)))))
(push-render-object
'screen
(+ 1 (button-layer button))
(lambda ()
(draw-font-text-2d
(v+ (button-pos button)
center-offset)
(label-text txt)
(label-size txt)
(label-color txt)
(label-font txt))))))
(define (do-button-callback button)
(when (eqv? 'clicked (button-state button))
((button-click-fn button))))
(export handle-buttons)
(define handle-buttons
(make-system
'handle-buttons
10
'batch
'(<button>)
(lambda (buttons)
(for-each
(lambda (b)
(let ((button (find button? b)))
(check-button-state button)
(draw-button button)
(do-button-callback button)))
buttons)
(pop-event 'input 'click))))
)

Binary file not shown.

94
res/fonts/OFL.txt Normal file
View file

@ -0,0 +1,94 @@
Copyright (c) 2011 Fontstage (info@fontstage.com),
with Reserved Font Names, "Felipa"
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

15
src/bitter-duel.scm Normal file
View file

@ -0,0 +1,15 @@
(module (bitter-duel) ()
(import scheme
(chicken base)
raylib
(imugi core)
(imugi input)
(main-menu))
(register-action 'click 'mouse-press MOUSE_BUTTON_LEFT)
((main-menu (lambda ()
(display "Loading game scene...")
(newline))))
(create-window))

64
src/main-menu.scm Normal file
View file

@ -0,0 +1,64 @@
(module (main-menu) ()
(import scheme
(chicken base)
(chicken module)
(imugi core)
(imugi input)
(imugi scene)
(imugi math)
(bd ui))
(export main-menu)
(define (main-menu play-callback)
(scene
push-actions
draw-labels
handle-buttons
;;; Text
;; Title
(entity
(title
(vec
;; 0 because automatically centered on x
0
(/ (cdr (*window-size*))
6))
"Bitter Duel"
centered: (cons #t #f)))
;; Subtitle
(entity
(subtitle
(vec
;; 0 because automatically centered on x
0
(/ (cdr (*window-size*))
3))
"Made for the Spring Lisp Game Jam 2026"
centered: (cons #t #f)))
;; Acknowledgements footer
(entity
(footer
(vec
;; 0 because automatically centered on x
0
(- (cdr (*window-size*))
30))
"By Jakub Nowak with Imugi+Raylib"
centered: (cons #t #f)))
(entity
(button
(vec (- (/ (car (*window-size*))
2)
50)
(- (/ (car (*window-size*))
2)
100))
(footer
(vec 0 0)
"Start Game!")
play-callback))
))
)