More documentation; code organisation; started work on launcher.
This commit is contained in:
parent
9490c9fd3e
commit
96d61e7116
75 changed files with 540 additions and 80 deletions
|
|
@ -1,9 +0,0 @@
|
|||
(ns cc.journeyman.the-great-game.character.character
|
||||
"A character that can talk; either human or dragon (although very probably
|
||||
we won't do talking dragons until really well into this process). All
|
||||
characters have the news-passing abilities of a gossip, but we use `gossip`
|
||||
to mean a special character who is part of the news-passing network."
|
||||
(:require [cc.journeyman.the-great-game.gossip.gossip :refer [dialogue]]
|
||||
[cc.journeyman.the-great-game.agent.agent :refer [Agent]]))
|
||||
|
||||
|
||||
|
|
@ -69,61 +69,63 @@
|
|||
(defrecord Agent
|
||||
;; "A default agent."
|
||||
[name craft home culture]
|
||||
ProtoObject
|
||||
ProtoContainer
|
||||
;; ProtoObject
|
||||
;; ProtoContainer
|
||||
|
||||
(contents
|
||||
"The `contents` of an actor are the contents of their pack(s) (if any), where
|
||||
a pack may be any sort of bag or container which the actor could reasonably
|
||||
be carrying."
|
||||
[actor]
|
||||
(flatten
|
||||
(map contents (filter #(satisfies? ProtoContainer %)
|
||||
(:burden actor)))))
|
||||
;; (contents
|
||||
;; "The `contents` of an actor are the contents of their pack(s) (if any), where
|
||||
;; a pack may be any sort of bag or container which the actor could reasonably
|
||||
;; be carrying."
|
||||
;; [actor]
|
||||
;; (flatten
|
||||
;; (map contents (filter #(satisfies? ProtoContainer %)
|
||||
;; (:burden actor)))))
|
||||
|
||||
(is-empty?
|
||||
[actor]
|
||||
(empty? (:burden actor)))
|
||||
;; (is-empty?
|
||||
;; [actor]
|
||||
;; (empty? (:burden actor)))
|
||||
|
||||
ProtoAgent
|
||||
;; ProtoAgent
|
||||
|
||||
(act
|
||||
“Return a map in which :world is bound to a world like this `world `except that this `actor `has acted in it; and `:actor` is bound to an
|
||||
actor like this `actor `except modified by the consequences of the action.
|
||||
‘Circle’ indicates which activation circle the actor is in.
|
||||
;; (act
|
||||
;; “Return a map in which :world is bound to a world like this `world `except that this `actor `has acted in it; and `:actor` is bound to an
|
||||
;; actor like this `actor `except modified by the consequences of the action.
|
||||
;; ‘Circle’ indicates which activation circle the actor is in.
|
||||
|
||||
Note that this implies that a `plan `is a function of three arguments, an
|
||||
actor, a world. and a circle, and returns exactly the sort of map this
|
||||
function returns.”
|
||||
[actor world circle]
|
||||
(let [urgent (case circle
|
||||
:other (cond
|
||||
(pending-scheduled-action? actor world circle)
|
||||
(plan-scheduled-action actor world circle))
|
||||
:background (cond
|
||||
(threatened? actor world circle)
|
||||
(plan-fight-or-flight actor world circle)
|
||||
(pending-scheduled-action? actor world circle)
|
||||
(plan-scheduled-action actor world circle))
|
||||
;; else
|
||||
(cond
|
||||
(threatened? actor world circle)
|
||||
(plan-fight-or-flight actor world circle)
|
||||
(hungry? actor world circle)
|
||||
(plan-find-food actor world circle)
|
||||
(tired? actor world circle)
|
||||
(plan-find-rest actor world circle)
|
||||
(pending-scheduled-action? actor world circle)
|
||||
(plan-scheduled-action actor world circle)))
|
||||
next-action (cond urgent urgent
|
||||
(empty? (:plans actor))
|
||||
(plan-goal actor world circle)
|
||||
:else (first (:plans actor)))
|
||||
consequences (apply next-action (list actor world circle))]
|
||||
;; we return consequences of the action, except that, if the action
|
||||
;; was on the plans of the actor, we remove it.
|
||||
(if-not (= next-action (first (:plans actor)))
|
||||
consequences
|
||||
(assoc consequences :actor
|
||||
(assoc (:actor consequences) :plans
|
||||
(rest (-> consequences :actor :plans))))))))
|
||||
;; Note that this implies that a `plan `is a function of three arguments, an
|
||||
;; actor, a world. and a circle, and returns exactly the sort of map this
|
||||
;; function returns.”
|
||||
;; [actor world circle]
|
||||
;; (let [urgent (case circle
|
||||
;; :other (cond
|
||||
;; (pending-scheduled-action? actor world circle)
|
||||
;; (plan-scheduled-action actor world circle))
|
||||
;; :background (cond
|
||||
;; (threatened? actor world circle)
|
||||
;; (plan-fight-or-flight actor world circle)
|
||||
;; (pending-scheduled-action? actor world circle)
|
||||
;; (plan-scheduled-action actor world circle))
|
||||
;; ;; else
|
||||
;; (cond
|
||||
;; (threatened? actor world circle)
|
||||
;; (plan-fight-or-flight actor world circle)
|
||||
;; (hungry? actor world circle)
|
||||
;; (plan-find-food actor world circle)
|
||||
;; (tired? actor world circle)
|
||||
;; (plan-find-rest actor world circle)
|
||||
;; (pending-scheduled-action? actor world circle)
|
||||
;; (plan-scheduled-action actor world circle)))
|
||||
;; next-action (cond urgent urgent
|
||||
;; (empty? (:plans actor))
|
||||
;; (plan-goal actor world circle)
|
||||
;; :else (first (:plans actor)))
|
||||
;; consequences (apply next-action (list actor world circle))]
|
||||
;; ;; we return consequences of the action, except that, if the action
|
||||
;; ;; was on the plans of the actor, we remove it.
|
||||
;; (if-not (= next-action (first (:plans actor)))
|
||||
;; consequences
|
||||
;; (assoc consequences :actor
|
||||
;; (assoc (:actor consequences) :plans
|
||||
;; (rest (-> consequences :actor :plans)))))))
|
||||
|
||||
)
|
||||
114
src/clj/cc/journeyman/the_great_game/character/character.clj
Normal file
114
src/clj/cc/journeyman/the_great_game/character/character.clj
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
(ns cc.journeyman.the-great-game.character.character
|
||||
"A character that can talk; either human or dragon (although very probably
|
||||
we won't do talking dragons until really well into this process). All
|
||||
characters have the news-passing abilities of a gossip, but we use `gossip`
|
||||
to mean a special character who is part of the news-passing network."
|
||||
(:require [cc.journeyman.the-great-game.gossip.gossip :refer [dialogue]]
|
||||
[cc.journeyman.the-great-game.agent.agent :refer [ProtoAgent]]
|
||||
[cc.journeyman.the-great-game.character.container :refer [ProtoContainer]]
|
||||
[clojure.string :as cs :only [join]])
|
||||
(:import [clojure.lang IPersistentMap]))
|
||||
|
||||
(defn honorific
|
||||
"Placeholder. If a character is a teir one craftsman, they get called 'Master';
|
||||
if a teir two ariston, they get called 'Ariston' and if a teir one ariston,
|
||||
'Tyrranos'. But the logic of this is about occupations, which probably isn't
|
||||
this namespace."
|
||||
[_character]
|
||||
nil)
|
||||
|
||||
(defn place-name
|
||||
"Placeholder. We're going to have to have names of villages, towns, regions
|
||||
and so on, and we're going to have to be able to retrieve those efficiently,
|
||||
but I don't yet know how this is going to work. Definitely doesn't belong
|
||||
in this namespace."
|
||||
[_cell]
|
||||
nil)
|
||||
|
||||
(defn match-on?
|
||||
"Placeholder, utility function. Do all these `objects` have the same values for
|
||||
these `keys`?"
|
||||
[keys & objects]
|
||||
(reduce = (map #(select-keys % keys) objects)))
|
||||
|
||||
(defprotocol ProtoCharacter
|
||||
(full-name [character]
|
||||
"Return the full name of this `character`, constructed according
|
||||
to the default construction sequence")
|
||||
(relative-name [character other]
|
||||
"Return the name that `other` would naturally use in an
|
||||
informal context to describe `character`")
|
||||
(personal-name [character]
|
||||
"Return the personal name of this `character`."))
|
||||
|
||||
(defrecord Character [object
|
||||
agent
|
||||
family-name
|
||||
personal-name
|
||||
occupation
|
||||
rank
|
||||
epithet
|
||||
knowledge
|
||||
wallet]
|
||||
;; A character; obviously, normally, a non-player character, although the
|
||||
;; player character is one of these. Essentially, an Agent which can speak,
|
||||
;; which has knowledge, which has a set of affective relationships with other
|
||||
;; characters.
|
||||
;; ProtoContainer
|
||||
ProtoAgent
|
||||
ProtoCharacter
|
||||
|
||||
(personal-name [character] (:personal-name character))
|
||||
(full-name [character]
|
||||
(let [e (:epithet character)
|
||||
h (honorific character)
|
||||
f (:family-name character)
|
||||
p (:personal-name character)
|
||||
o (:occupation character)
|
||||
l (place-name (:cell character))]
|
||||
(cs/join " "
|
||||
(remove nil?
|
||||
(flatten
|
||||
[e
|
||||
h
|
||||
f
|
||||
p
|
||||
(when o ["the" o])
|
||||
(when l ["of" l])])))))
|
||||
(relative-name [character other]
|
||||
(let [e (:epithet character)
|
||||
h (honorific character)
|
||||
f (:family-name character)
|
||||
p (:personal-name character)
|
||||
o (:occupation character)
|
||||
h (place-name (:cell character))
|
||||
same-family? (= f (:family-name other))]
|
||||
(cs/join " "
|
||||
(remove nil?
|
||||
(flatten
|
||||
[(when-not (match-on?
|
||||
[:family-name :cell] character other)
|
||||
|
||||
e)
|
||||
(when-not same-family? h)
|
||||
(when-not same-family? h)
|
||||
p
|
||||
(when (and o (not (match-on? :occupation))) ["the" o])
|
||||
(when (and h
|
||||
(not (match-on? [:cell] character other))) ["of" h])]))))))
|
||||
|
||||
|
||||
(defn make-character
|
||||
"Construct a Character record from this `seed` map"
|
||||
[^IPersistentMap seed]
|
||||
(let [object (make-object seed)
|
||||
agent (make-actor seed)]
|
||||
(apply Character.
|
||||
(list (map seed [:agent
|
||||
:family-name
|
||||
:personal-name
|
||||
:occupation
|
||||
:rank
|
||||
:epithet
|
||||
:knowledge
|
||||
:wallet])))))
|
||||
1
src/clj/cc/journeyman/the_great_game/character/sex.clj
Normal file
1
src/clj/cc/journeyman/the_great_game/character/sex.clj
Normal file
|
|
@ -0,0 +1 @@
|
|||
(ns cc.journeyman.the-great-game.character.sex)
|
||||
87
src/clj/cc/journeyman/the_great_game/launcher.clj
Normal file
87
src/clj/cc/journeyman/the_great_game/launcher.clj
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
(ns cc.journeyman.the-great-game.launcher
|
||||
"Launcher for the game"
|
||||
(:require [clojure.tools.cli :refer [parse-opts]]
|
||||
[jme-clj.core :refer [add-control add-to-root app-settings cam
|
||||
defsimpleapp fly-cam get-height-map image
|
||||
image-based-height-map load-height-map
|
||||
load-texture material set* start
|
||||
terrain-lod-control terrain-quad]])
|
||||
(:import (com.jme3.texture Texture$WrapMode))
|
||||
(:gen-class))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; Launcher: parses any command line options, and launches the game.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the GNU General Public License
|
||||
;;;; as published by the Free Software Foundation; either version 2
|
||||
;;;; of the License, or (at your option) any later version.
|
||||
;;;;
|
||||
;;;; This program is distributed in the hope that it will be useful,
|
||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;;; GNU General Public License for more details.
|
||||
;;;;
|
||||
;;;; You should have received a copy of the GNU General Public License
|
||||
;;;; along with this program; if not, write to the Free Software
|
||||
;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
;;;; USA.
|
||||
;;;;
|
||||
;;;; Copyright (C) 2024 Simon Brooke
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def cli-options
|
||||
;; An option with a required argument
|
||||
[["-p" "--port PORT" "Port number"
|
||||
:default 80
|
||||
:parse-fn #(Integer/parseInt %)
|
||||
:validate [#(< 0 % 0x10000) "Must be a number between 0 and 65536"]]
|
||||
;; A non-idempotent option (:default is applied first)
|
||||
["-v" nil "Verbosity level"
|
||||
:id :verbosity
|
||||
:default 0
|
||||
:update-fn inc] ; Prior to 0.4.1, you would have to use:
|
||||
;; :assoc-fn (fn [m k _] (update-in m [k] inc))
|
||||
;; A boolean option defaulting to nil
|
||||
["-h" "--help"]])
|
||||
|
||||
(defn init []
|
||||
(set* (fly-cam) :move-speed 50)
|
||||
(let [grass (set* (load-texture "textures/terrain/splat/grass.jpg") :wrap Texture$WrapMode/Repeat)
|
||||
dirt (set* (load-texture "textures/terrain/splat/dirt.jpg") :wrap Texture$WrapMode/Repeat)
|
||||
rock (set* (load-texture "textures/terrain/splat/road.jpg") :wrap Texture$WrapMode/Repeat)
|
||||
mat (material "Common/MatDefs/Terrain/Terrain.j3md")
|
||||
height-map-tex (load-texture "textures/terrain/splat/mountains512.png")
|
||||
height-map (->> height-map-tex image image-based-height-map load-height-map)
|
||||
patch-size 65
|
||||
terrain (terrain-quad "my terrain" patch-size 513 (get-height-map height-map))]
|
||||
(-> mat
|
||||
(set* :texture "Alpha" (load-texture "textures/terrain/splat/alphamap.png"))
|
||||
(set* :texture "Tex1" grass)
|
||||
(set* :float "Tex1Scale" (float 64))
|
||||
(set* :texture "Tex2" dirt)
|
||||
(set* :float "Tex2Scale" (float 32))
|
||||
(set* :texture "Tex3" rock)
|
||||
(set* :float "Tex3Scale" (float 128)))
|
||||
(-> terrain
|
||||
(set* :material mat)
|
||||
(set* :local-translation 0 -100 0)
|
||||
(set* :local-scale 2 1 2)
|
||||
(add-to-root)
|
||||
(add-control (terrain-lod-control terrain (cam))))))
|
||||
|
||||
(defsimpleapp app :init init)
|
||||
|
||||
(defn -main
|
||||
"Launch the game."
|
||||
[& args]
|
||||
(parse-opts args cli-options)
|
||||
|
||||
;; this isn't working, not sure why not.
|
||||
;; (.setSettings app (app-settings false :dialog-image "images/splash.png"))
|
||||
|
||||
(start app))
|
||||
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
microworld style world tagged with vegetation, etc, data). "
|
||||
([height-map]
|
||||
(let [drained-world (get-drainage-map height-map)]
|
||||
(run-world drained-world (compile (slurp "resources/baking/biome-rules.txt")) (count drained-world))))
|
||||
(run-world drained-world (compile (slurp "resources/data/baking/biome-rules.txt")) (count drained-world))))
|
||||
([height-map _rainfall-map]
|
||||
(get-biome-map height-map)))
|
||||
|
||||
|
|
@ -70,6 +70,7 @@
|
|||
:camp (populate-npcs {:world world :cell cell :occupation :nomad})
|
||||
:house (populate-npcs {:world world :cell cell :occupation :peasant})
|
||||
:inn (populate-npcs {:world world :cell cell :occupation :innkeeper})
|
||||
:market (populate-npcs {:world world :cell cell :occupation :merchant})
|
||||
;; else
|
||||
nil)]
|
||||
(if npcs (assoc cell :npcs npcs)
|
||||
|
|
@ -80,7 +81,7 @@
|
|||
(probably some form of database) and return a structure which allows that
|
||||
database o be interrogated."
|
||||
[biome-map]
|
||||
(let [world (run-world biome-map (compile (slurp "resources/baking/settlement-rules.txt")) (count biome-map))
|
||||
(let [world (run-world biome-map (compile (slurp "resources/data/baking/settlement-rules.txt")) (count biome-map))
|
||||
with-npcs (map-world world (vary-meta (fn [w c] (populate-cell w c)) assoc :rule-type :ad-hoc))]
|
||||
;; right, with that settled world, I'm going to put one herdsman with
|
||||
;; five animals (either all sheep, all cattle, all goats, all horses or
|
||||
|
|
@ -101,7 +102,7 @@
|
|||
:roadmap []}))
|
||||
|
||||
(defn get-road-map
|
||||
[populated-world])
|
||||
[_populated-world])
|
||||
|
||||
(defn prove
|
||||
"Given this `height-map` (a monochrome raster) and optionally this
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package java.cc.journeyman.the_great_game.game_objects;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/** Java implementation of a game object which is a container. */
|
||||
|
||||
public class JContainer extends JObject {
|
||||
/** The contents of this container. */
|
||||
private LinkedList<JObject> contents = new LinkedList<JObject>();
|
||||
|
||||
/** Get the contents of this container. */
|
||||
public LinkedList<JObject> getContents() {
|
||||
return this.contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this single item to this container.
|
||||
*
|
||||
* @param item
|
||||
*/
|
||||
public void addItem(JObject item) {
|
||||
}
|
||||
|
||||
public void addItem(Object item) {
|
||||
if (item instanceof JObject) {
|
||||
this.addItem((JObject)item);
|
||||
} else {
|
||||
throw new IllegalArgumentException("All items added to a container must be game objects");
|
||||
}
|
||||
}
|
||||
|
||||
public void addItems(Collection<Object> items) {
|
||||
for (Object item : items) {
|
||||
|
||||
this.contents.add((JObject) item);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package java.cc.journeyman.the_great_game.game_objects;
|
||||
|
||||
import com.jme3.bounding.BoundingBox;
|
||||
import com.jme3.bounding.BoundingSphere;
|
||||
import com.jme3.bounding.BoundingVolume;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.scene.Node;
|
||||
import java.lang.Math;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Highly experimental! Using Java beans instead of Clojure records
|
||||
* for underlying game objects, to allow type inheritance. It has the
|
||||
* additional advantage that Java objects are inherently mutable. This is the
|
||||
* root of the hierarchy, a basic object.
|
||||
*
|
||||
* See [JavaBeans Spec]
|
||||
* (http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html).
|
||||
*
|
||||
* If this works(!), it is likely that object persistence and (re-loading) will
|
||||
* happen in the Java layer rather than in the Clojure layer.
|
||||
*
|
||||
* A game object model in jMonkeyEngine is loaded as a 'Spatial' which
|
||||
* is a subclass of 'Node', which has a 'BoundingVolume' i.v. which is
|
||||
* instantiated as either a 'BoundingSphere' or a 'BoundingBox'. We
|
||||
* can't directly extract length/width/height data from these... but we
|
||||
* ought not to tolerate majow inconsistency between the size we report
|
||||
* and the volume reported by the model.
|
||||
*/
|
||||
public class JObject {
|
||||
private static SecureRandom idSeed = new SecureRandom();
|
||||
|
||||
/**
|
||||
* When we have an actual database we'll probably get the id from
|
||||
* the database...
|
||||
*/
|
||||
private long id = idSeed.nextLong();
|
||||
|
||||
private int weight = 0;
|
||||
|
||||
private int length = 0;
|
||||
|
||||
private int width = 0;
|
||||
|
||||
private int height = 0;
|
||||
|
||||
private Node model;
|
||||
|
||||
private BoundingVolume getBoundingVolume() {
|
||||
BoundingVolume result = null;
|
||||
// if (model instanceof Spatial) {
|
||||
// result = (Spatial) model.getWorldBound();
|
||||
// }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private double getMaxExtent() {
|
||||
BoundingVolume v = this.getBoundingVolume();
|
||||
double result = -1;
|
||||
|
||||
if (v instanceof BoundingBox) {
|
||||
BoundingBox vb = (BoundingBox) v;
|
||||
result = Math.max( Math.max( vb.getXExtent(), vb.getYExtent()),
|
||||
vb.getZExtent());
|
||||
} else if (v instanceof BoundingSphere ) {
|
||||
result = 2 * ((BoundingSphere) v).getRadius();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** */
|
||||
|
||||
public int getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public void setLength(int length) {
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
public int getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public void setWeight(int weight) {
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
/** Return the id of this instance, obviously. */
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue