Created
December 31, 2015 01:41
-
-
Save samskivert/39dab8a7801084e4cf56 to your computer and use it in GitHub Desktop.
Hypothetical game logic rules/scripts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; | |
;; Utility functions | |
;; returns a randomly chosen unit stat | |
(defun randstat () (pickrand 'hp 'attack)) | |
;; returns a randomly chosen battle unit kind | |
;; (TODO: base this on all unit kinds that have >0 attack) | |
(defun randbattler () (pickrand 'strike 'defend 'drone)) | |
;; returns true if a unit is a battle unit (can attack things) | |
(defun isbattle (unit) (> (stat unit 'attack) 0)) | |
;; list helpers | |
(defun non-empty (list) (> list.size 0)) | |
(defun is-empty (list) (= list.size 0)) | |
;; | |
;; Planet generation | |
;; Example | |
;; (defpgen 'PKIND | |
;; (tags 'TAG 'TAG ...) | |
;; ...actions to init planet.. | |
;; ) | |
;; | |
;; PKIND defines the kind of planet, which is used later in the planet logic rules. It is | |
;; also used to resolve the icon image for the planet, so don't misspell it. | |
;; | |
;; TAGs are used in by the galaxy generation algorithm: | |
;; - 'seed planets are used to seed the starting area | |
;; - 'boss planets are used to create boss areas "around" the seed area | |
;; - 'neutral planets are scattered all around both seed and boss areas | |
;; some helpers to simplify creating certain kinds of planets | |
(defun init-resource (planet output) | |
(set planet.recipe (recipe ('miner 1) output)) | |
(set planet.doubleProb (* (rand# 1 3) 5)) // 5-15 | |
) | |
(defun init-trade (planet recipe boost-stat boost%) | |
(set planet.recipe recipe) | |
(set planet.boost-stat boost-stat) | |
(set planet.boost% boost%) | |
) | |
(defun init-factory (planet output) | |
(set planet.recipe (recipe () (output 1))) | |
(set planet.boost-stat (randstat)) | |
(let boostIdx (rand# 1 4)) // 1-4 | |
(set planet.boost% (* boostIdx 25)) // 25-100% | |
(let maxProb (- 7 boostIdx)) // 3-6 | |
;; if output kind is not a battle unit, set boost prob to 0 (never boost) | |
(set planet.boostProb (if (isbattle kind) (* (rand# 1 maxProb) 5) 0)) | |
) | |
;; seed planets (with some also in neutral) | |
(defpgen 'factory | |
(tags 'seed) | |
(init-factory planet 'miner) | |
) | |
(defpgen 'resource | |
(tags 'seed) | |
(init-resource planet ('metals 2 'crystals 1)) | |
) | |
(defpgen 'resource | |
(tags 'seed) | |
(init-resource planet ('fuels 2 'crystals 1)) | |
) | |
(defpgen 'trade | |
(tags 'seed) | |
(init-trade (recipe ('crystals 2) ('diplo 1)) 'hp 0) | |
) | |
(defpgen 'trade | |
(tags 'seed) | |
(init-trade (recipe ('metals 1 'fuels 1) ('strike 1 'defend 1)) 'attack 50) | |
) | |
(defpgen 'factory | |
(tags 'seed) | |
(init-factory planet (randbattler)) | |
) | |
(defpgen 'buff | |
(tags 'seed) | |
(set planet.buffStat 'hp) | |
(set planet.buffAmount 10) ;; TODO: more random? | |
) | |
;; neutral planets | |
(defpgen 'trade | |
(tags 'neutral) | |
(init-trade (recipe ('fuels 2) ('strike 1)) 'attack 50) | |
) | |
(defpgen 'trade | |
(tags 'neutral) | |
(init-trade (recipe ('fuels 2) ('strike 1)) 'attack 50) | |
) | |
;; ...TODO: the rest... | |
;; enemy planets | |
(defpgen `stronghold | |
(tags `enemy) | |
(set planet.mode `init-colonizers) ;; part of stronghold state machine | |
) | |
;; | |
;; Planet logic | |
;; Example | |
;; (defprule "NAME" | |
;; (event 'EVENT) | |
;; (conds (...condition...) (...condition...) ...) | |
;; ...actions... | |
;; ) | |
;; | |
;; NAME is just a human readable name that makes life easier for debugging. | |
;; EVENT is one of: | |
;; - 'onDock fired when a unit docks at a planet | |
;; - 'onSpawn fired when a unit is spawned | |
;; - 'onDeath fired when a unit is destroyed | |
;; - 'tick fired for each planet when the clock ticks | |
;; - 'produce fired after 'tick for planets which meet production criteria (no opponents in orbit; | |
;; recipe ingredients satisfied; can fire multiple times on a single tick) | |
;; | |
;; In the actions that are executed when a prule fires, 'planet' is bound to the planet in | |
;; question and for rules that involve a unit ('onDock & 'onSpawn) 'unit' is bound to that unit. | |
(defprule "buff-apply" | |
(event 'onDock) | |
(conds (= planet.type 'buff) | |
(= planet.dockedEnemies 0) | |
(> (stat unit 'attack) 0) ;; only buff battle units | |
(= bot.boosted 'false)) ;; don't buff already buffed units | |
(boost unit planet.buffStat planet.buffAmount) | |
) | |
(defprule "tomb-rebirth" | |
(event 'tick) | |
(conds (= planet.type 'tomb)) | |
(rebirth planet) ;; TODO: make 'what kind of new planet' explicit rather than implicit | |
) | |
(defprule "hideout-spawn" | |
(event 'tick) | |
(conds (= planet.type 'hideout) | |
(let kind (if (prob planet.diploProb) 'diplo 'pirate)) | |
(spawn planet kind) | |
) | |
(defprule "factory-produce" | |
(event 'produce) | |
(conds (= planet.type 'factory) | |
(< planet.dockedProducts 2)) | |
(let unit (spawn planet planet.recipe.output-kind)) | |
(if (prob planet.boostProb) (boost% unit planet.boost-stat planet.boost%)) | |
) | |
(defprule "resource-produce" | |
(event 'produce) | |
(conds (= planet.type 'resource)) | |
(spawn planet planet.recipe.output-kind) | |
(if (prob planet.doubleProp) (spawn planet planet.recipe.output-kind)) | |
) | |
(defprule "trade-produce" | |
(event 'produce) | |
(conds (= planet.type 'trade)) | |
(let unit (spawn planet planet.recipe.output-kind)) | |
(if (> planet.boost% 0) (boost% unit planet.boost-stat planet.boost%)) | |
) | |
;; | |
;; Enemy logic | |
;; note that enemy logic makes use of a special construct called a 'theater' which is populated | |
;; with an enumeration of all friendly, neutral and enemy planets within 'influence range' of | |
;; the planet in question as well all enemy units (by kind) in the theater | |
(defprule "init-colonizers" | |
(event 'tick) | |
(conds (= planet.type `stronghold) | |
(= planet.mode `init-colonizers)) | |
(repeat 3 | |
(let unit (spawn planet `diplo)) | |
(set unit.mode (pickrand `colonize-factory `colonize-trade `colonize-resource)) | |
) | |
(set planet.mode `defend) | |
) | |
(defprule "init-defenders" | |
(event 'tick) | |
(conds (= planet.type `stronghold) | |
(= planet.mode `init-defenders)) | |
(repeat 3 (spawn planet `defend)) | |
(set planet.mode `idle) | |
) | |
(defprule "react-to-intruders" | |
(event 'tick) | |
(conds (= planet.type `stronghold) | |
(= planet.mode `idle)) | |
(for kind (`strike `strike `drone `drone) | |
(let unit (spawn planet kind)) | |
(set unit.mode `seek-and-destroy) | |
) | |
(set planet.mode `on-defensive) | |
) | |
;; might have other rules that only fire when we're on the defensive | |
(defprule "relax-guard" | |
(event 'tick) | |
(conds (= planet.type `stronghold) | |
(= planet.mode `on-defensive) | |
(is-empty (theater.friendly-units `any))) | |
(set planet.mode `idle) | |
) | |
(defprule "attack-planet" | |
(event 'tick) | |
(conds (= planet.type 'stronghold) | |
(non-empty theater.friendly-planets.size) | |
(non-empty (theater.enemy-units 'strike))) | |
(let target (pickrand theater.friendly-planets)) | |
(let attacker (theater.find-closest target (theater.enemy-units 'strike))) | |
(set-course attacker target) | |
) | |
(defprule "defend-hold" | |
(event 'tick) | |
(conds (= planet.type 'stronghold) | |
(< planet.defense-power 50) | |
(non-empty theater.enemy-units `defend)) | |
(let defender (theater.find-closest planet (theater.enemy-units 'defend))) | |
(set-course defender planet) | |
) | |
(defprule "seek-and-destroy" | |
(event 'onDock) | |
(conds (= unit.mode `seek-and-destroy)) | |
(if (is-empty (theater.friendly-units `any)) | |
(set unit.mode `idle) | |
(set-course unit (theater.closet-friendly unit)) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment