This is a very interim commit. Started work on genetic buildings.

Nothing works!
This commit is contained in:
Simon Brooke 2021-06-08 18:15:24 +01:00
parent 310896cc95
commit 7138c51134
No known key found for this signature in database
GPG key ID: A7A4F18D1D4DF987
12 changed files with 439 additions and 11 deletions

View file

@ -1,11 +1,12 @@
(ns cc.journeyman.the-great-game.agent.agent
"Anything in the game world with agency"
(:require [the-great-game.objects.game-object :refer [ProtoObject]]
[the-great-game.objects.container :refer [ProtoContainer]]))
"Anything in the game world with agency; primarily but not exclusively
characters."
(:require [cc.journeyman.the-great-game.objects.game-object :refer [ProtoObject]]
[cc.journeyman.the-great-game.objects.container :refer [ProtoContainer]]))
;; hierarchy of needs probably gets implemented here
;; I'm probably going to want to defprotocol stuff, to define the hierarchy
;; of things in the gameworld; either that or drop to Java, wich I'd rather not do.
;;; hierarchy of needs probably gets implemented here
;;; I'm probably going to want to defprotocol stuff, to define the hierarchy
;;; of things in the gameworld; either that or drop to Java, wich I'd rather not do.
(defprotocol ProtoAgent
"An object which can act in the world"
@ -37,7 +38,7 @@
(defrecord Agent
;; "A default agent."
[name home tribe]
[name craft home culture]
ProtoObject
ProtoContainer
ProtoAgent

View file

@ -0,0 +1,168 @@
(ns cc.journeyman.the-great-game.buildings.rectangular
(:require [cc.journeyman.the-great-game.holdings.holding :refer [ProtoHolding]]
[cc.journeyman.the-great-game.location.location :refer [ProtoLocation]]
)
(:import [org.apache.commons.math3.random MersenneTwister]
))
;;; Right, the idea behind this namespace is many fold.
;;;
;;; 1. To establish the broad principle of genetic buildings, by creating a
;;; function which reproducibly creates reproducible buildings at specified
;;; locations, such that different buildings are credibly varied but a
;;; building at a specified location is always (modulo economic change) the
;;; same.
;;; 2. Create good rectangular buildings, and investigate whether a single
;;; function can be used to create buildings of more than one family (e.g.
;;; can it produce flat roofed, north African style, mud brick houses as
;;; well as pitch roofed, half timbered northern European houses?)
;;; 3. Establish whether, in my current state of fairly severe mental illness,
;;; I can actually produce any usable code at all.
;;;
;;; ## Key factors in the creation of a building
;;;
;;; ### Holding
;;;
;;; Every building is on a holding, and, indeed, what I mean by 'building' here
;;; may well turn out to be 'the collection of all the permanent structures on
;;; a holding. A holding is a polygonal area of the map which does not
;;; intersect with any other holding, but for the time being we'll make the
;;; simplifying assumption that every holding is a rectangular strip, and that
;;; 'urban' holdings are of a reasonably standard width (see Viking-period
;;; York) and length. Rural holdings (farms, ?wood lots) may be much larger.
;;;
;;; ### Terrain
;;;
;;; A building is made of the stuff of the place. In a forest, buildings will
;;; tend to be wooden; in a terrain with rocky outcrops -- normally found on
;;; steep slopes -- stone. On the flat lands where there's river mud, of brick,
;;; cob, or wattle and daub. So to build a building we need to know the
;;; terrain. Terrain can be inferred from location but in practice this will
;;; be computationally expensive, so we'll pass terrain in as an argument to
;;; the build function.
;;;
;;; For the time being we'll pass it in simply as a keyword from a defined set
;;; of keywords; later it may be a more sophisticated data structure.
;;;
;;; ### Culture
;;;
;;; People of different cultures build distinctively different buildings, even
;;; when using the same materials. So, in our world, a Japanese wooden house
;;; looks quite different from an Anglo Saxon stave house which looks quite
;;; different from a Canadian log cabin, even though the materials are much the
;;; same and the tools available to build with are not much different.
;;;
;;; Culture can affect not just the overall shape of a building but also its
;;; finish and surface detail. For example, in many places in England, stone
;;; buildings are typically left bare; in rural Scotland, typically painted
;;; white or in pastel shades; in Ireland, often quite vivid colours.
;;;
;;; People may also show religious or cultural symbols on their buildings.
;;;
;;; For all these reasons, we need to know the culture of the occupant when
;;; creating a building. Again, this will initially be passed in as a keyword.
;;;
;;; ### Craft
;;;
;;; People in the game world have a craft, and some crafts will require
;;; different features in the building. In the broadly late-bronze-age-to
;;; medieval period within which the game is set, residence and workplace
;;; are for most people pretty much the same.
;;;
;;; So a baker needs an oven, a smith a forge, and so on. All crafts who do
;;; some degree retail trade will want a shop front as part of the ground
;;; floor of their dwelling. Merchants and bankers will probably have houses
;;; that are a bit more showy than others.
;;;
;;; Whether the 'genetic buildings' idea will ever really produce suitable
;;; buildings for aristons I don't know; it seems more likely that significant
;;; strongholds (of which there will be relatively few) should all be hand
;;; modelled rather than procedurally generated.
(def ^:dynamic *terrain-types*
"Types of terrain which affect building families. TODO: This is a placeholder;
a more sophisticated model will be needed."
#{:arable :arid :forest :plateau :upland})
(def ^:dynamic *cultures*
"Cultures which affect building families. TODO: placeholder"
#{:ariston :coastal :steppe-clans :western-clans :wild-herd})
(def ^:dynamic *crafts*
"Crafts which affect building types in the game. See
`Populating a game world`. TODO: placeholder"
#{:baker :banker :butcher :chancellor :innkeeper :lawyer :magus :merchant :miller :priest :scholar :smith :weaver})
(def ^:dynamic *building-families*
{:pitched-rectangular {:terrains #{:arable :forest :upland}
:crafts *crafts*
:cultures #{:coastal :western-clans}
:modules []}
:flatroof-rectangular {:terrains #{:arid :plateau}
:crafts *crafts*
:cultures #{:coastal}
:modules []}})
;; TODO: So, modules need to contain
;;
;; 1. Ground floor modules, having external doors;
;; 2. Craft modules -- workshops -- which will normally be ground floor (except
;; weavers) and may have the constraint that no upper floor module can cover them;
;; 3. Upper floor modules, having NO external doors (but linking internal doors);
;; 4. Roof modules
;;
;; There also needs to be an undercroft or platform module, such that the area of
;; the top of the platform is identical with the footprint of the building, and
;; the altitude of the top of the platform is equal to the altitude of the
;; terrain at the heighest corner of the building; so that the actual
;; building doesn't float in the air, and also so that none of the doors or windows
;; are partly underground.
;;
;; Each module needs to wrap an actual 3d model created in Blender or whatever,
;; and have a list of optional textures with which that model can be rendered.
;; So an upper floor bedroom module might have the following renders:
;;
;; 1. Bare masonry - constrained to upland or plateau terrain, and to coastal culture
;; 2. Painted masonry - constrained to upland or plateau terrain, and to coastal culture
;; 3. Half-timbered - not available on plateau terrain
;; 4. Weatherboarded - constrained to forest terrain
;; 5. Brick - constrained to arable or arid terrain
;;
;; of course these are only examples, and also, it's entirely possible to have
;; for example multiple different weatherboard renders for the same module.
;; There needs to be a way of rendering what can be built above what: for
;; example, you can't have a masonry clad module over a half timbered one,
;; but you can have a half-timbered one over a masonry one
(defn building-family
"A building family is essentially a collection of models of building modules
which can be assembled to create buildings of a particular structural and
architectural style."
[terrain culture craft gene]
(let [candidates (filter #(and
((:terrains %) terrain)
((:crafts %) craft)
((:cultures %) culture))
(vals *building-families*))]
(nth candidates (mod (Math/abs (.nextInt gene)) (count candidates)))))
(building-family :arable :coastal :baker (MersenneTwister. 5))
(defn build!
"Builds a building, and returns a data structure which represents it. In
building the building, it adds a model of the building to the representation
of the world, so it does have a side effect."
[holding terrain culture craft size]
(if (satisfies? ProtoHolding holding)
(let [location (.building-origin holding)
gene (MersenneTwister. (int (+ (* (.easting location) 1000000) (.northing location))))
family (building-family terrain culture craft gene)]
(if
(and (instance? ProtoLocation location) (:orientation location))
:stuff
:nonsense
))
:froboz))
;; (def ol (cc.journeyman.the-great-game.location.location/OrientedLocation. 123.45 543.76 12.34 0.00 {}))

View file

@ -0,0 +1,42 @@
(ns cc.journeyman.the-great-game.holdings.holding
(:require [cc.journeyman.the-great-game.agent.agent :refer [ProtoAgent]]
[cc.journeyman.the-great-game.objects.container :refer [ProtoContainer]]
[cc.journeyman.the-great-game.objects.game-object :refer [ProtoObject]]
;; [cc.journeyman.the-great-game.location.location :refer [OrientedLocation]]
[cc.journeyman.the-great-game.world.routes :refer []]))
;;; A holding is a polygonal area of the map which does not
;;; intersect with any other holding, or with any road or water feature. For
;;; the time being we'll make the
;;; simplifying assumption that every holding is a rectangular strip, and that
;;; 'urban' holdings are of a reasonably standard width (see Viking-period
;;; York) and length. Rural holdings (farms, ?wood lots) may be much larger.
(defprotocol ProtoHolding
(frontage
[holding]
"Returns a sequence of two locations representing the edge of the polygon
which defines this holding which is considered to be the front.")
(building-origin
[holding]
"Returns an oriented location - normally the right hand end of the
frontage, for an urban holding - from which buildings on the holding
should be built."))
(defrecord Holding [perimeter holder]
;; Perimeter should be a list of locations in exactly the same sense as a
;; route in `cc.journeyman.the-great-game.world.routes`. Some sanity checking
;; is needed to ensure this!
ProtoContainer
ProtoHolding
(frontage [holding]
;; TODO: this is WRONG, but will work for now. The frontage should
;; be the side of the perimeter nearest to the nearest existing
;; route.
[(first (perimeter holding)) (nth (perimeter holding) 1)])
(building-origin [holding]
;; TODO: again this is wrong. The default building origin
;; should be the right hand end of the frontage when viewed
;; from outside the holding.
(first (frontage holding)))
ProtoObject)

View file

@ -0,0 +1,47 @@
(ns cc.journeyman.the-great-game.location.location)
;;; There's probably conflict between this sense of a reified location and
;;; the simpler sense of a location described in
;;; `cc.journeyman.the-great-game.world.location`, q.v. This needs to
;;; be resolved!
(defprotocol ProtoLocation
(easting [location]
"Return the easting of this location")
(northing [location] "Return the northing of this location")
(altitude [location]
"Return the absolute altitude of this location, which may be
different from the terrain height at this location, if, for
example, the location is underground or on an upper floor.")
(terrain-altitude [location]
"Return the 'ground level' (altitude of the terrain)
at this location given this world. TODO: possibly
terrain-altitude should be a method of the world.")
(settlement [location]
"Return the settlement record of the settlement in this world
within whose parish polygon this location exists, or if none
whose centre (inn location) is closest to this location"))
(defrecord Location [^Double easting ^Double northing ^Double altitude world]
ProtoLocation
(easting [l] (:easting l))
(northing [l] (:northing l))
(altitude [l] (:altitude l))
(terrain-altitude [l] 0.0) ;; TODO
(settlement [l] :tchahua))
(defrecord OrientedLocation
;; "Identical to a Location except having, additionally, an orientation"
[^Double easting ^Double northing ^Double altitude ^Double orientation world]
ProtoLocation
(easting [l] (:easting l))
(northing [l] (:northing l))
(altitude [l] (:altitude l))
(terrain-altitude [l] 0.0) ;; TODO
(settlement [l] :tchahua)) ;; TODO
;; (.settlement (OrientedLocation. 123.45 543.76 12.34 0.00 {}))
;; (OrientedLocation. 123.45 543.76 12.34 0.00 {})

View file

@ -0,0 +1,68 @@
(ns cc.journeyman.the-great-game.playroom
(require [jme-clj.core :refer :all])
(import [com.jme3.math ColorRGBA]))
;; At present this file is just somewhere to play around with jme-clj examples
(defn init []
(let [cube (geo "jMonkey cube" (box 1 1 1))
mat (unshaded-mat)]
(set* mat :texture "ColorMap" (load-texture "textures/Monkey.jpg"))
(set* cube :material mat)
(add-to-root cube)
{:cube cube}))
;; Let's create simple-update fn with no body for now.
(defn simple-update [tpf]
(let [{:keys [cube]} (get-state)]
(rotate cube 0 (* 2 tpf) 0)))
;; Kills the running app var and closes its window.
;; (unbind-app #'app)
;; We define the `app` var.
(defsimpleapp app
:opts {:show-settings? false
:pause-on-lost-focus? false
:settings {:title "My JME Game"
:load-defaults? true
:frame-rate 60
:width 800
:height 600}}
:init init
:update simple-update)
(start app)
;; Reinitialises the running app
;;(run app
;; (re-init init))
;; By default, there is a Fly Camera attached to the app that you can control with W, A, S and D keys.
;; Let's increase its movement speed. Now, you fly faster :)
(run app
(set* (fly-cam) :move-speed 15))
;; Updates the app
(run app
(let [{:keys [cube]} (get-state)]
(set* cube :local-translation (add (get* cube :local-translation) 1 1 1))))
;; Updates the app adding a second cube
(run app
(let [cube (geo "jMonkey cube" (box 1 1 1))
mat (unshaded-mat)]
(set* mat :texture "ColorMap" (load-texture "textures/Monkey.jpg"))
(setc cube
:material mat
:local-translation [-3 0 0])
(add-to-root cube)
(set-state :cube2 cube)))
;; We added the new cube, but it's not rotating. We need to update the simple-update fn.
(defn simple-update [tpf]
(let [{:keys [cube cube2]} (get-state)]
(rotate cube 0 (* 2 tpf) 0)
(rotate cube2 0 (* 2 tpf) 0)))