(ffi:clines " #include SDL_Renderer *renderer; SDL_Window *window; SDL_Event e; const Uint8 *state; int quitted; int up_arrow, down_arrow, left_arrow, right_arrow; ") (Defpackage "jam-no-theme" (:use cl) (:nicknames :ja)) (in-package :ja) (defvar *SCREEN-WIDTH* 640) (defvar *SCREEN-HEIGHT* 480) (defvar *scale* '5) (defvar *unit-width* (/ *screen-width* *scale*)) (defvar *unit-height* (/ *screen-height* *scale*)) ;;;Underlying sdl2 mechanism (defmacro game ((screen-width screen-height) (&rest unused) update-closures) `(let ((screen-width ,screen-width) (screen-height ,screen-height)) ,(append '(declare) '((:int screen-width screen-height))) (multiple-value-bind (up down left right aa bb) (check-arrow-scancodes) (multiple-value-setq (*up* *down* *left* *right* *a* *B*) (values up down left right aa bb))) (unwind-protect (ffi:c-progn (screen-width screen-height) " if (SDL_Init(SDL_INIT_VIDEO) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \"Failed to init %s\", SDL_GetError()); " (error "failed to SDL_Init(video)") " } if (SDL_CreateWindowAndRenderer(#0,#1,SDL_WINDOW_RESIZABLE, &window, &renderer)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, \"Failed to create w & r%s\", SDL_GetError()); " (error "failed to create window and renderer") " } quitted = 0; for (;;) { while(SDL_PollEvent(&e)) if (e.type == SDL_QUIT) quitted = 1; else if (e.type == SDL_KEYDOWN) switch (e.key.keysym.sym) { case SDLK_q: quitted = 1; break; } if (quitted) break; SDL_SetRenderDrawColor(renderer, 0, 10, 20, 255); SDL_RenderClear(renderer); " (mapc 'funcall ,update-closures) " SDL_RenderPresent(renderer); SDL_Delay(125); } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit();") (ffi:c-inline () () nil "SDL_Quit();")))) ;;; SDL_SetRenderDrawColor in lisp. (defun set-color (r g b &optional (a 255)) (ffi:c-inline (r g b a) (:int :int :int :int) nil "SDL_SetRenderDrawColor(renderer, #0, #1, #2, #3)" :one-liner t)) ;;; SDL_RenderFillRect as a lisp function. (defun fill-rectangle (x y w h) (ffi:c-inline ((* *scale* x) (* *scale* y) (* *scale* w) (* *scale* h)) (:int :int :int :int) nil " SDL_RenderFillRect(renderer, &(struct SDL_Rect){.x = #0, .y = #1, .w = #2, .h = #3})" :one-liner t)) ;; Add plants. (defvar *plants* (list)) (defun spawn-plant-in (x y w h) (push (list 'leaves (+ x (random w)) (+ y (random h))) *plants*)) (defparameter *leaves/flowers* 2/31) (defparameter *flowers/berries* 2/21) (defparameter *berry-satiation* 6) (defvar *max-seeds* 4) (defvar *seedbox* 5) ;; leaves->flowers flowers->berries (defun advance-some-plants (&rest from-to-frac/tions) (setf *plants* (loop for plant in *plants* nconc (or (loop for (from to frac/tion) in from-to-frac/tions when (< (random (denominator frac/tion)) (numerator frac/tion)) do (when (eq (car plant) from) (return `((,to ,@(cdr plant)))))) `(,plant))))) ;; Painting the lilly (defun paint-plants () (loop for plant in *plants* for xy-position = (cdr plant) for color = (case (car plant) (leaves '(0 255 0)) (flower '(255 0 255)) (berry '(255 0 0))) when color do (apply 'set-color color) (apply 'fill-rectangle (append (mapcar '- xy-position (funcall (ensure-player) :get-position t) (mapcar '- *orig-player-position*)) '(1 1))))) ;;once the player/robots need to spawn, they should spawn from the base. (defvar *base* nil) (defvar *orig-player-position* (list (truncate *unit-width* 2) (truncate *unit-height* 2))) (defun ensure-base () (or *base* (setf *base* (let ((xy-position '(1 2)) (color '(255 255 255))) (lambda (&key get-position move paint seed-plant create-bot) (cond (seed-plant) (get-position (values xy-position)) (move (case move (e (decf (car xy-position))) (n (incf (cadr xy-position))) (w (incf (car xy-position))) (s (decf (cadr xy-position))))) (create-bot (spawn-bot)) (paint (apply 'set-color color) (apply 'fill-rectangle (append (mapcar '- xy-position (funcall (ensure-player) :get-position t) (mapcar '- *orig-player-position*)) '(1 1)))))))))) (defun make-counter (&optional (default 5)) (lambda (&key (increase nil) (check nil)) (cond (check (values default)) (increase (incf default increase)) (t (decf default))))) (defparameter *default-treasures* 10) (defparameter *treasures* nil) (defun ensure-treasures () (if *treasures* *treasures* (setf *treasures* (let ((locations (list)) (treasure-count (make-counter 0))) (lambda (&key paint generate collect devour treasure-meter count-treasure) (cond (count-treasure (funcall treasure-count :check t)) (treasure-meter (loop initially (set-color 200 175 75) for n below (funcall treasure-count :check t) do (fill-rectangle n 5 1 5))) (paint (loop initially (When (null locations) (funcall *change-secret-image*) (loop repeat *default-treasures* for x = (- (random *unit-width*) (truncate *unit-width* 2)) for y = (- (random *unit-height*) (truncate *unit-height* 2)) do (push (list x y) locations))) for location in locations for (x y) = (mapcar '- location (funcall (ensure-player) :get-position t) (mapcar '- *orig-player-position*)) for color = (loop repeat 3 collect (random 256)) do (apply 'set-color color) do (apply 'fill-rectangle x y '(1 1)))) (devour (when (member devour locations :test 'equal) (setf locations (delete devour locations :test 'equal)) (funcall treasure-count :increase 1) (values t))))))))) ;; controlled by arrowkeys. (defvar *player* nil) (defvar *player-starting-hunger* 20) (defun ensure-player () (if *player* *player* (setf *player* (let ((xy-position (copy-list '(0 0))) (color '(255 255 0)) (satiety (make-counter *player-starting-hunger*))) (lambda (&key get-position move paint consume hunger render-hunger) (cond (get-position (values xy-position)) (render-hunger (loop initially (set-color 100 255 150) for n below (funcall satiety :check t) do (fill-rectangle (- n 6) 1 5 5))) (consume (funcall (ensure-treasures) :devour xy-position) (let ((flora (rassoc xy-position *plants* :test 'equal))) (when flora (when (eq 'berry (car flora)) (setf *plants* (delete flora *plants* :test 'equal)) (funcall satiety :increase *berry-satiation*) (funcall satiety :increase (random (1+ (funcall (ensure-treasures) :count-treasure t)))) (dotimes (n (random *max-seeds*)) (let* ((xy (loop for m below 2 collect (+ (nth m xy-position) (- (random (* 2 *seedbox*)) *seedbox*)))) (flora (rassoc xy *plants* :test 'equal))) (if flora (progn (setf *plants* (delete flora *plants* :Test 'equal)) (setf *plants* (push (rplaca flora 'leaves) *plants*))) (setf *plants* (push `(leaves ,@xy) *plants*))))))))) (hunger (funcall satiety)) (move (case move (e (incf (car xy-position))) (n (decf (cadr xy-position))) (w (decf (car xy-position))) (s (incf (cadr xy-position))))) (paint (apply #'set-color color) (apply #'fill-rectangle `(,@*orig-player-position* 1 1))))))))) ;;Robot stuff (defvar *default-program* '(lambda (self) (Funcall self :move (nth (random 4) '(e n w s))))) (defparameter *programming* (eval *default-program*)) (defvar *bots* (list)) (defun spawn-bot (&key (program *programming*) (hunger 5) (position (copy-list (funcall (ensure-base) :get-position t))) (initial-memories (list))) (let ((new-bot (let ((xy-position position) (satiety hunger) (programming program) (color '(0 255 255)) (memories initial-memories)) (lambda (&key paint perambulate hunger consume move) (cond (perambulate (funcall programming perambulate)) (paint (apply 'set-color color) (let ((rect-args (append (mapcar '- xy-position (funcall (ensure-player) :get-position t) (mapcar '- *orig-player-position*)) '(1 1)))) (apply 'fill-rectangle rect-args))) (move (case move (e (decf (car xy-position))) (n (incf (cadr xy-position))) (w (incf (car xy-position))) (s (decf (cadr xy-position))))) (hunger (decf satiety)) (memories memories) (consume (funcall (ensure-treasures) :devour xy-position) (when (rassoc xy-position *plants* :test 'equal) (when (eq (car (rassoc xy-position *plants* :test 'equal)) 'berry) (incf satiety *berry-satiation*) (incf satiety (random (1+ (funcall (ensure-treasures) :count-treasure t)))) (setf *plants* (delete (rassoc xy-position *plants* :test 'equal) *plants*)) (dotimes (n (random *max-seeds*)) (let ((xy (loop for m below 2 collect (+ (nth m xy-position) (- (random (* 2 *seedbox*)) *seedbox*))))) (if (rassoc xy *plants* :test 'equal) (rplaca (rassoc xy *plants* :test 'equal) 'leaves) (push (cons 'leaves xy) *plants*)))))))))))) (push new-bot *bots*))) (defvar *D34D8075* (list)) (defun queue-remove-bot (bot) (push bot *D34D8075*)) (defun delete-bots () (setf *bots* (delete-if (lambda (bot) (member bot *D34D8075*)) *bots*) *D34D8075* (list))) (defun mechanical-process (&optional (bots *bots*)) (loop initially (funcall (ensure-base) :create-bot t) for bot in bots do (funcall bot :perambulate bot) do (funcall bot :consume t) do (funcall bot :paint t) when (zerop (funcall bot :hunger t)) do (queue-remove-bot bot) finally (delete-bots))) (defun get-key-state (scancode) (let ((state (ffi:c-inline (scancode) (:int) :int "state = SDL_GetKeyboardState(NULL); @(return) = (state[#0]) ? 1 : 0;"))) (values state))) (defun check-arrow-scancodes () (ffi:c-inline () () (values :int :int :int :int :int :int :int :int) "@(return 0) = SDL_SCANCODE_UP; @(return 1) = SDL_SCANCODE_DOWN; @(return 2) = SDL_SCANCODE_LEFT; @(return 3) = SDL_SCANCODE_RIGHT; @(return 4) = SDL_SCANCODE_A; @(return 5) = SDL_SCANCODE_B; @(return 6) = SDL_SCANCODE_RETURN;")) (defparameter *funs* (list)) (defun play-game () (game (*screen-width* *screen-height*) () *funs*)) (defun make-game () " Little-by-little game creation " ;; New game (defparameter *funs* (list)) ;; Add lines in either direction ;; Add player and base (push (lambda () (Funcall (ensure-player) :paint t)) *funs*) (push (lambda () (Funcall (ensure-base) :paint t)) *funs*) (push (lambda () (funcall (ensure-player) :move (cond ((not (zerop (get-key-state *down*))) 's) ((not (zerop (get-key-state *up*))) 'n) ((not (zerop (get-key-state *left*))) 'w) ((not (zerop (get-key-state *right*))) 'e)))) *funs*) (loop repeat 10 do (spawn-plant-in -5 -5 15 15)) (push (lambda () (advance-some-plants `(leaves flower ,*leaves/flowers*) `(flower berry ,*flowers/berries*))) *funs*) (push (lambda () (funcall (ensure-player) :consume t)) *funs*) (push (lambda () (paint-plants)) *funs*) (push (lambda () (mechanical-process *bots*)) *funs*) (push (lambda () (setf *player* (and (not (zerop (funcall (ensure-player) :hunger t))) (ensure-player)))) *funs*) (push (lambda () (funcall (ensure-player) :render-hunger t)) *funs*) (push (lambda () (funcall (ensure-treasures) :paint t)) *funs*) (push (lambda () (funcall (ensure-treasures) :treasure-meter t)) *funs*) ;; secret image - alright not very secret (push (lambda () (loop initially (set-color 100 100 100) for b below 96 do (loop for a below 128 when (not (zerop (funcall *secret-image*))) do (fill-rectangle a b 1 1)))) *funs*) (play-game))