30 | """
31 | for k in fs.readdirSync 'node_modules'
32 | if fs.existsSync 'node_modules/' + k + '/package.json'
33 | rec = JSON.parse fs.readFileSync 'node_modules/' + k + '/package.json'
34 | v = rec.version
35 | else if d.dependencies[k] then v = d.dependencies[k]
36 | else continue
37 | o += """
#{k}"""
38 | o += """#{v}"""
39 | # o += """#{rec.description}""" if rec.description
40 | o += "
"
41 | o += '
'
42 | fs.writeFileSync destinationFile, o
43 | callback null
44 |
--------------------------------------------------------------------------------
/mod/preql/sprites/objects_nuu.json:
--------------------------------------------------------------------------------
1 | { "ship": [
2 |
3 | { "name":"Human","sprite":"st_suit",
4 | "slots":{
5 | "structure":[{"size":"suit","default":"Human Skin"}],
6 | "utility":[{"size":"suit","default":"Human Heart"}],
7 | "weapon":[{"size":"suit","default":"Stock Multitool"}]},
8 | "stats":{
9 | "crew":1,
10 | "mass":1,
11 | "fuel_consumption":0} },
12 |
13 | { "name":"Exosuit","sprite":"st_suit",
14 | "slots":{
15 | "structure":[
16 | {"size":"suit","default":"Stock Fabric"},
17 | {"size":"suit","default":"Stock ExoThrusters"}
18 | ],
19 | "utility":[],
20 | "weapon":[{"size":"suit","default":"StockMultitool"}]},
21 | "stats":{
22 | "crew":1,
23 | "mass":1,
24 | "fuel_consumption":0} }
25 |
26 | ], "outf": [
27 |
28 | { "name" : "Stock ExoThrusters",
29 | "size" : "suit",
30 | "itemId": 500,
31 | "stats" : {
32 | "mass" : 15,
33 | "price" : 0,
34 | "thrust" : 100,
35 | "turn" : 85,
36 | "engine_limit" : 300,
37 | "energy_usage" : 5,
38 | "fuel" : 200,
39 | "speed" : 175
40 | },
41 | "type" : "modification",
42 | "info" : {
43 | "description" : "Clogged, corroded and cracked in every way possible while still remaining in functional condition, this engine really belongs on the scrap heap.",
44 | "name" : "Stock ExoThrusters",
45 | "gfx_store" : "engine04"
46 | },
47 | "slot" : { "prop" : "engines", "$t" : "structure" },
48 | "extends" : "Outfit" },
49 |
50 | { "name" : "Stock Fabric",
51 | "size" : "suit",
52 | "itemId": 501,
53 | "stats" : {
54 | "armour": 10,
55 | "mass": 1,
56 | "cargo": 0
57 | },
58 | "type" : "modification",
59 | "info" : {
60 | "description" : "A rag, really.",
61 | "name" : "Stock Fabric",
62 | "gfx_store" : "engine04"
63 | },
64 | "slot" : { "prop" : "hull", "$t" : "structure" },
65 | "extends" : "Outfit" }
66 |
67 | ] }
68 |
--------------------------------------------------------------------------------
/mod/core/server/db.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | unless fs.existsSync 'db'
24 | fs.mkdirSync 'db'
25 |
26 | $tag.db = (name,obj={}) ->
27 | obj.ready = ( -> ) unless obj.ready
28 | db = new $tag.XScale obj.path = path.join 'db', ( obj.name = name ) + '.db'
29 | meta = db.get '$meta'
30 | console.log '::db$meta', name, meta if debug
31 | Object.assign db, functions, obj, meta
32 | if db.id?
33 | console.log name, ':open'.green, db.id?
34 | throw new Error 'db.id is Nan', db if isNaN db.id
35 | else
36 | console.log name, ':bootstrap'.red, db.id? if debug
37 | db.id = 1
38 | db.create k,v for k,v of db.bootstrap
39 | global[name] = db
40 | do obj.ready
41 | console.log '::db', 'register', name, util.inspect db if debug
42 | db
43 |
44 | functions =
45 | saveMeta:->
46 | @set '$meta', id: @id
47 | console.log '_save:$meta', id: @id
48 | create: (name,data) ->
49 | if @fields then for k,v of @fields when not data[k]
50 | data[k] = (
51 | if typeof v is 'function' then v.apply @
52 | else v )
53 | data.id = @id++
54 | @set name, data
55 | do @saveMeta
56 | console.log '_create:', id: @id
57 |
58 | setInterval ( ->
59 | # $tag.flushAll()
60 | ), 1000
61 |
62 | NUU.emit "server:db"
63 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nuu",
3 | "version": "0.4.74",
4 | "description": "escape velocity inspired space sim browser game",
5 | "main": "./build/server.js",
6 | "bin": {
7 | "nuu": "./build/server.js"
8 | },
9 | "scripts": {
10 | "start": "node .",
11 | "devel": "coffee tools/build.coffee run",
12 | "build": "test -d node_modules/browserify || npm i; coffee tools/build.coffee assets",
13 | "clean": "npm prune --production",
14 | "prepare": "coffee tools/build.coffee assets",
15 | "test": "npm run build; CLIENT=true npm start",
16 | "inspect": "npm run build; CLIENT=true DEBUG=true node --inspect-brk ."
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "anx@ulzq.de:nuu-test"
21 | },
22 | "author": "anx",
23 | "license": "GPL-3.0",
24 | "dependencies": {
25 | "async": "^0.6.2",
26 | "body-parser": "^1.19.0",
27 | "compression": "^1.7.4",
28 | "cookie-parser": "^1.4.5",
29 | "eventemitter3": "^3.1.2",
30 | "express": "^4.17.1",
31 | "express-json": "^1.0.0",
32 | "express-session": "^1.17.1",
33 | "fast-image-size": "^0.1.2",
34 | "morgan": "^1.10.0",
35 | "multer": "^1.4.2",
36 | "pd": "file:pd",
37 | "random-ship-names": "^1.0.0",
38 | "serve-index": "^1.9.1",
39 | "serve-static": "^1.14.1",
40 | "three": "^0.124.0",
41 | "vectors": "^0.1.0",
42 | "ws": "^5.2.2"
43 | },
44 | "gitHead": "86252dbc933a7215583393968322ce92c24c903d",
45 | "devDependencies": {
46 | "browserify": "^16.5.2",
47 | "chokidar": "^3.4.2",
48 | "coffeescript": "^2.5.1",
49 | "file-size": "^0.0.5",
50 | "jquery": "^3.5.1",
51 | "marked": "^4.0.10",
52 | "mkdirp": "^0.5.5",
53 | "request": "^2.88.2",
54 | "touch": "^0.0.3",
55 | "uglify-js": "^3.10.2",
56 | "x2js": "^3.4.0"
57 | },
58 | "keywords": [
59 | "browsergame",
60 | "game",
61 | "space"
62 | ],
63 | "optionalDependencies": {
64 | "bufferutil": "^3.0.5",
65 | "colors": "^0.6.2",
66 | "node-forge": "^1.2.1"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/mod/core/common/error.coffee:
--------------------------------------------------------------------------------
1 | Error.byKey = {}
2 | Error.byId = {}
3 | Error.i18 = {}
4 |
5 | Error.register = (opts)->
6 | id = 0
7 | for key, msg of opts
8 | @byKey[key] = id
9 | @byId[id] = msg
10 | @[key] = msg
11 | id++
12 | null
13 |
14 | Error.register
15 | _invalid_item: "Invalid item id :o"
16 | _no_handle: "I don't have a cookie for you. :<"
17 | _no_fuel: "You don't have enough fuel!"
18 | _no_slot_type: "You made this slot type up!"
19 | _no_slot: "This slot does not exist."
20 | _no_vehicle: "You're naked!"
21 | _not_here: "You can't do this here :("
22 | _not_in_orbit: "You're not orbiting anything!?"
23 | _not_landed: "You must land at a certified service station."
24 | _not_the_owner: "You don't own this ship."
25 | _nx_jump: "404 - Target not found"
26 | _nx_target: "404 - Target not found"
27 | _invalid_msg: "Invalid message!"
28 | _no_mount: "This is a teapot!"
29 | _no_mounts: "That seat is made up!"
30 | _not_mounted: "Your're not mounted. Don't ask me why..."
31 | _no_steer: "You can't steer this mount."
32 |
33 | Error.i18.de =
34 | _invalid_item: "Gibbet nich!"
35 | _no_handle: "Beweis dich erstmal selbst! :>"
36 | _no_fuel: "Nicht genug Sprit!"
37 | _no_slot_type: "Den Slot-Typ hast du dir doch ausgedacht! :>"
38 | _no_slot: "Den Slot kann ich nicht finden o0"
39 | _no_vehicle: "Du bist Splitterfasernackt, echt jetzt! :D"
40 | _not_here: "Kannst hier so nich machen, tut mir leid!"
41 | _not_in_orbit: "Such dir erstmal ein Orbit..."
42 | _not_landed: "Das muss der Fachmann machen. :<"
43 | _not_the_owner: "Das is' nich' dein Schiff o0"
44 | _nx_jump: "Wohin denn bitte?"
45 | _nx_target: "Wen denn bitte?"
46 | _invalid_msg: "Kannst du kein Deutsch? Deine Zeichenkette ist keine!"
47 | _no_mount: "Der Stuhl ist kaputt... oder ausgedacht :P"
48 | _no_mounts: "Das ist ne Pulle Bier oder so und kein Raumschiff; da kann man jdf. nicht einsteigen!"
49 | _not_mounted: "Da brat' mir einer einen Storch, du stehst zwischen den Welten."
50 | _no_steer: "Das kann man nicht lenken."
51 |
--------------------------------------------------------------------------------
/tools/import_git.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2020 Sebastian Glaser
4 | * c) 2007-2020 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | module.exports = (destinationFile,callback)->
24 | cp = require 'child_process'
25 | ref = cp.execSync('git log --pretty=format:"%H"').toString('utf8').split('\n')
26 | msg = cp.execSync('git log --pretty=format:"%s"').toString('utf8').split('\n')
27 | date = cp.execSync('git log --pretty=format:"%d"').toString('utf8').split('\n')
28 | author = cp.execSync('git log --pretty=format:"%a"').toString('utf8').split('\n')
29 | head = ref[0]
30 | release = {}; current = null
31 | adds = msg
32 | .filter (i,k,s)-> not i.match /^[0-9]+\.[0-9]+\.[0-9]+/
33 | .filter (i,k,s)-> k is s.indexOf i
34 | msg .map (i,k,s)-> if i.match /^[0-9]+\.[0-9]+\.[0-9]+/
35 | release[v = i.substr 0,6] = x = v: v
36 | x.m = i.substr(6).replace(/ /g,'\n ')
37 | x.a = author[v]
38 | x.d = date[v]
39 | unless current
40 | current = x
41 | if adds.length > 0
42 | x.v += '+'
43 | x.m += '\n ' + adds.join(' (from master)\n ') + ' (from master)'
44 | x.m = x.m.split('\n').sort( (a,b)->
45 | a.charCodeAt(5) - b.charCodeAt(5) ).join('\n').substr(6)
46 | current.git = head
47 | fs.writeFileSync path.join(path.dirname(destinationFile),'release.json'), JSON.stringify current
48 | callback null
49 | # console.log head, current.v, current.m
50 | # for k,v of release
51 | # process.exit 1
52 | # callback null
53 |
--------------------------------------------------------------------------------
/mod/nuu/client/gui_nuu.coffee:
--------------------------------------------------------------------------------
1 | Window.About = class AboutWindow extends Window
2 | title: 'About NUU / License'
3 | closeKey:'aKeyH'
4 | constructor:->
5 | super()
6 | @$.addClass 'about full'
7 | @$.append $ """
8 |
The nuu project intends to use all the asset files
23 | according to their respective licenses.
24 |
25 |
nuu currently uses the graphics and sound assets of
26 | the excellent naev project, which in turn are partly attributable to the
27 | Vega Strike project.
28 |
29 |
All assets are downloaded from their source repo to
30 | the contrib folder during the build process and LICENSE information is
31 | copied to the build directory as well as being made available in the
32 | client's about screen and the server splash.
33 |
34 |
35 |
36 |
37 | """
38 | @$.find('.close').on 'click', => @close()
39 | @tabs = @$.find '.tab'
40 | @tabs.each (k,i) ->
41 | src = $(i).attr 'data-src'
42 | console.log src
43 | return unless src
44 | $.get src, (data,s,x)-> $(i).html data
45 | btns = @$.find '.tabbtn'
46 | w = @
47 | btns.on 'click', -> w.activate @href
48 | @activate '#game'
49 | activate: (name) ->
50 | @tabs.css 'display', 'none'
51 | name = name.replace(/.*#/,'')
52 | console.log name
53 | $('#'+name).css 'display', 'block'
54 |
55 | Kbd.macro 'about', 'aKeyH', 'Show about / license', -> new Window.About
56 |
--------------------------------------------------------------------------------
/mod/nuu/server/engine.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | $static '$id$', (i)-> i.id
24 |
25 | NUU.fix_sprites = (o)->
26 | for k,v of o
27 | w = v.size
28 | v.cols = v.cols || 1
29 | v.rows = v.rows || 1
30 | v.width = v.width || w
31 | v.height = v.height || w
32 | v.radius = v.radius || w / 2
33 | o
34 |
35 | $public class Formula
36 | @to: {}
37 | @from: {}
38 |
39 | Formula.define = (k,f)->
40 | ( Formula.to[k] || Formula.to[k] = [] ).push f
41 | ( Formula.from[c] || Formula.from[c] = [] ).push [k,f] for c in Object.keys f
42 | return
43 |
44 | Formula.init = (k,f)->
45 | for f in rules.formula
46 | k = Object.keys(f)[0]
47 | Formula.define k, f[k]
48 | console.log Formula.to if debug
49 | console.log Formula.from if debug
50 | return
51 |
52 | NUU.init =->
53 | NUU.mode = NUU.mode || 'dm'
54 | console.log 'loading game mode', NUU.mode
55 | Object.assign rules, rules[NUU.mode]
56 | # Load objects
57 | fs.writeJSONSync 'build/objects.json', Item.init (
58 | fs.readJSONSync 'build/objects_naev.json'
59 | .concat fs.readJSONSync 'build/objects_nuu.json' )
60 | # Load metadata for sprites for each object
61 | fs.writeJSONSync 'build/images.json', meta = NUU.fix_sprites Object.assign(
62 | fs.readJSONSync 'build/imag/sprites_naev.json'
63 | fs.readJSONSync 'build/imag/sprites_nuu.json' )
64 | $static '$meta', meta
65 | do Formula.init
66 | do Stellar.init
67 | console.log ':nuu', 'init:rules' if debug
68 | rules @
69 | @thread 'group', 1000, ->
70 | time = NUU.time()
71 | o.update time for o in $obj.list
72 | null
73 | @start()
74 |
--------------------------------------------------------------------------------
/pd/schema.js:
--------------------------------------------------------------------------------
1 | const Database = require("./database");
2 | const SchemaRecord = require('./record');
3 |
4 | Database.model = ({ db, name, path = name, schema }) => {
5 | db = db ? db : Database.open({ path });
6 | db.Model = schema;
7 | schema.Record.$ = db;
8 | schema.Record.prototype.$ = db;
9 | schema.indexes.forEach((key) => db.index(key));
10 | Object.assign(schema.Record.prototype, schema.methods);
11 | Schema.Types[name] = schema.Record;
12 | return schema.Record;
13 | };
14 |
15 |
16 |
17 |
18 | class Schema {
19 | constructor({ methods = {}, ...fields }) {
20 | Object.entries(fields).map(([k,v]) => fields[k] = typeof v === 'object' && !Array.isArray(v) ? v : {type:v});
21 | const keys = Object.keys(fields), type = {}, defaults = {};
22 | keys.forEach((k) => (type[k] = fields[k]?.type || fields[k]));
23 | keys.forEach((k) => (defaults[k] = fields[k]?.default));
24 | const requireds = keys.filter((k) => fields[k]?.required);
25 | this.indexes = keys.filter((k) => fields[k]?.index);
26 | this.methods = methods;
27 | this.$on = {};
28 | const Record = SchemaRecord({Schema,$schema:this,keys,requireds,type,defaults});
29 | this.Record = Record;
30 | Object.assign(this, {fields,requireds,defaults,type,Record});
31 | keys.forEach( key => {
32 | Object.defineProperty(this.Record.prototype, key, {
33 | get: function(){ return this.data[key]; },
34 | set: function(v){ this.data[key] = v; this.$changed = true; },
35 | enumerable: true
36 | });
37 | });
38 | }
39 | virtual = (key, fn) => {
40 | setImmediate(()=> Object.defineProperty(this.Record.prototype, key, fns));
41 | let fns = {}, ctx = {
42 | get: (get) => { fns = {...fns,get}; return ctx; },
43 | set: (set) => { fns = {...fns,set}; return ctx; },
44 | virtual: this.virtual
45 | };
46 | return ctx;
47 | }
48 | pre( event, callback ){
49 | this.$on[event] ? this.$on[event].push(callback) : ( this.$on[event] = [callback] );
50 | }
51 | isArray(key){
52 | const type = this.fields[key]?.type;
53 | return Array === type || Array.isArray(type);
54 | }
55 | }
56 |
57 | const ObjectId = Symbol("PD_OBJREF");
58 |
59 | Schema.type = [String, Number, Boolean, Object, Array];
60 |
61 | Schema.Types = {
62 | String,
63 | Number,
64 | Boolean,
65 | Object,
66 | Array,
67 | ObjectId,
68 | };
69 |
70 | module.exports = Schema;
71 |
--------------------------------------------------------------------------------
/mod/preql/common/map.coffee:
--------------------------------------------------------------------------------
1 | $public class URMap
2 | x: 0
3 | y: 0
4 | scale: 8
5 | height: null
6 | mountain: []
7 |
8 | constructor: (@w,@h,@seed=32) ->
9 | # load Map from cache
10 | if (m = localStorage.getItem("urtc.map."+@seed))
11 | console.log 'cached'
12 | @height = new Int16Array str2ab m
13 | else # Generate Map
14 | Math.seedrandom(@seed)
15 | @height = new Int16Array @w*@h+@w+1
16 | for x in [0..@w-1]
17 | for y in [0..@h-1]
18 | @height[x+y*@w] = h = Math.round( (
19 | Math.floor(55.0 + 50 * Math.sin(0.5 * x / 16.0)) +
20 | Math.floor(55.0 + 50 * Math.sin(0.5 * y / 8.0)) +
21 | Math.floor(55.0 + 50 * Math.sin(0.5 * (x + y) / 16.0)) +
22 | Math.floor(55.0 + 50 * Math.sin(0.5 * Math.sqrt(x*x + y*y) / 8.0))
23 | ) / 4)
24 | @peak(Math.floor(Math.random()*@w),Math.floor(Math.random()*@h),200) for i in [0..10]
25 | localStorage.setItem("urtc.map."+@seed,ab2str(@height.buffer))
26 | # Initial render()
27 | win.on 'resize', @render
28 | do animate = => do @render; requestAnimationFrame animate
29 | $('body').on 'mousemove', => @hud()
30 | @map on
31 |
32 | peak: (x,y,h) =>
33 | @mountain[i] = m = x : x, y : y, h : h = Math.floor(Math.random()*h)
34 | h = @height[idx = x+y*@w] += h
35 | stack = [[x+1,y+1,h],[x-1,y+1,h],[x+1,y-1,h],[x-1,y-1,h],[x,y+1,h],[x,y-1,h],[x+1,y],[x-1,y,h]]
36 | have = new Int16Array(@w*@h+@w+1)
37 | have[idx] = 1
38 | c = 0
39 | while stack.length > 0
40 | stack = Array.shuffle stack if c++ % 10 is 0
41 | [x,y,h] = stack.shift()
42 | if have[idx = x+y*@w] isnt 1
43 | lh = @height[idx]
44 | if h - lh > 10
45 | have[idx] = 1
46 | if lh < 55
47 | @height[idx] = h = lh && h - 1 - Math.floor(Math.random() * 2)
48 | else @height[idx] = h = lh && h - 1 - Math.floor(Math.random() * 8)
49 | for i in [[x-1,y+1,h],[x+1,y-1,h],[x,y+1,h],[x,y-1,h],[x+1,y,h],[x-1,y,h],[x+1,y+1,h],[x-1,y-1,h]]
50 | stack.push i if have[i[0]+i[1]*@w] isnt 1
51 | @peak(Math.floor(Math.random()*50),Math.floor(Math.random()*50),h-Math.floor(10+Math.random()*20)) if h - 55 > 100
52 |
53 | walkmap: ->
54 | grid = []
55 | for y in [0..@h-1]
56 | grid.push row = []
57 | for x in [0..@w-1]
58 | row[x] = @height[x+y*@w]
59 | # row[x] = if 49 < (h=@height[x+y*@w]) < 101 then 0 else h
60 | grid
61 |
62 | debug: => console.log @
63 |
--------------------------------------------------------------------------------
/mod/core/server/start.coffee:
--------------------------------------------------------------------------------
1 |
2 | NUU.emit 'extend'
3 |
4 | # ██ ██ ███████ ██████ ███████ ███████ ██████ ██ ██ ███████ ██████
5 | # ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
6 | # ██ █ ██ █████ ██████ ███████ █████ ██████ ██ ██ █████ ██████
7 | # ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
8 | # ███ ███ ███████ ██████ ███████ ███████ ██ ██ ████ ███████ ██ ██
9 |
10 | ## Initialize express / setup WebSockets
11 | $websocket NUU.web = express()
12 | ## Setup Webserver
13 | NUU.web.use require('morgan')() if debug
14 | # NUU.web.use require('body-parser') keepExtensions: true, uploadDir: '/tmp/'
15 | NUU.web.use require('compression')()
16 | NUU.web.use require('cookie-parser')()
17 | NUU.web.use require('express-session') secret: 'what-da-nuu', saveUninitialized:no, resave:no
18 | NUU.web.use '/build', require('serve-static')('build',etag:no)
19 | NUU.web.use '/build', require('serve-index' )('build',etag:no) if debug
20 | NUU.web.get '/excuse', NUU.splashPage false, true
21 | NUU.web.get '/', NUU.splashPage false, false
22 | NUU.web.get '/intro', NUU.splashPage true, false
23 | NUU.web.get '/start', NUU.startPage()
24 |
25 | # ███████ ███ ██ ██████ ██ ███ ██ ███████
26 | # ██ ████ ██ ██ ██ ████ ██ ██
27 | # █████ ██ ██ ██ ██ ███ ██ ██ ██ ██ █████
28 | # ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
29 | # ███████ ██ ████ ██████ ██ ██ ████ ███████
30 |
31 | $static '$release', fs.readJSONSync './build/release.json'
32 | $release.banner = $release.v.green + $release.git.red
33 |
34 | NUU.chgid = process.env.CHGID || false
35 | NUU.port = process.env.PORT || 9999
36 | NUU.addr = process.env.ADDR || '127.0.0.1'
37 | BROWSER = process.env.BROWSER || 'chromium'
38 |
39 | console.log ':nuu', 'initializing'.yellow
40 | NUU.init()
41 |
42 | console.log 'http', 'listen'.yellow, NUU.addr.red + ':' + NUU.port.toString().magenta
43 | NUU.web.listen NUU.port, NUU.addr, ->
44 | console.log 'http', 'online'.green, NUU.addr.red + ':' + NUU.port.toString().magenta
45 | console.log ':nuu', $release.banner
46 | if process.env.CLIENT
47 | if BROWSER.match /^chrom[ei]/
48 | ARGS = ["-app=http://#{NUU.addr}:#{NUU.port}/start"]
49 | if BROWSER.match /^firefox/
50 | ARGS = ["http://#{NUU.addr}:#{NUU.port}/start"]
51 | try cp.spawn BROWSER, ARGS
52 | if NUU.chgid
53 | console.log 'http', 'dropping privileges'.green
54 | process.setgid NUU.chgid
55 | process.setuid NUU.chgid
56 | null
57 |
--------------------------------------------------------------------------------
/mod/nuu/client/autopilot.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | $public class Autopilot
24 | @active: no
25 | @plan: 'land'
26 |
27 | Autopilot.widget = (v) ->
28 | unless VEHICLE.target
29 | HUD.widget 'autopilot', 'no target', yes
30 | return v
31 | s = ''
32 | s += VEHICLE.target.name + '\n'
33 | s += v.message + '\n'
34 | s += v.recommend + '\n'
35 | s += 'v: ' + parseInt(VEHICLE.target.v[0]) + ':' + parseInt(VEHICLE.target.v[1]) + '\n'
36 | s += 't: ' + parseInt(v.throttle) + '\n'
37 | s += 'e: ' + v.error + '\n'
38 | s += 'z: ' + parseInt(v.target_zone) + '\n'
39 | s += 'dd:' + parseInt(v.maxSpeed) + '\n'
40 | s += '\n⚑['
41 | s += '▲' if v.recommend is 'boost'
42 | s += '△' if v.recommend is 'burn'
43 | s += '▽' if v.recommend is 'retro'
44 | s += '◉' if v.recommend is 'setdir'
45 | s += '☕' if v.recommend is 'wait'
46 | s += '⌘' if v.recommend is 'execute'
47 | s += ']\nFm:' + hdist v.maxSpeed
48 | HUD.widget 'autopilot', s, yes
49 | return v
50 |
51 | Autopilot.start = ->
52 | Autopilot.stop() if Autopilot.active
53 | HUD.widget 'autopilot', 'ap:booting', yes
54 | VEHICLE.target = TARGET
55 | VEHICLE.changeStrategy null
56 | VEHICLE.changeStrategy 'approach'
57 | Autopilot.active = yes
58 | Mouse.disableTemp()
59 | VEHICLE.onTarget = (v)->
60 | Autopilot.stop()
61 | Target.orbit()
62 | return
63 |
64 | Autopilot.stop = ->
65 | HUD.widget 'autopilot', 'ap:off', yes
66 | VEHICLE.changeStrategy null
67 | Autopilot.active = no
68 | Mouse.enableIfWasEnabled()
69 | return
70 |
71 | Autopilot.macro = ->
72 | unless VEHICLE.hasAP
73 | VEHICLE.hasAP = yes
74 | VEHICLE.approachTarget = -> @target = TARGET
75 | VEHICLE.changeStrategy = AI.prototype.changeStrategy
76 | VEHICLE.onDecision = Autopilot.widget.bind Autopilot
77 | unless Autopilot.active
78 | do Autopilot.start
79 | else do Autopilot.stop
80 |
81 | Kbd.macro 'autopilot', 'sKeyZ', 'Autopilot', Autopilot.macro
82 |
--------------------------------------------------------------------------------
/tools/dev_autobuild.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2020 Sebastian Glaser
4 | * c) 2007-2020 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | fs = require 'fs'
24 | path = require 'path'
25 | cp = require 'child_process'
26 | async = require 'async'
27 | _ = require 'underscore'
28 |
29 | Array.prototype.uniq = ->
30 | b = {}; c = []; for k,v of @ when b[v] isnt true
31 | b[v] = true;c.push v
32 | c
33 |
34 | readDir = (dir,call) -> call f, dir for f in fs.readdirSync dir
35 |
36 | root = path.dirname __dirname
37 | server = null
38 | client = null
39 | watch = {}
40 | rebuildFiles = []
41 | timeout = null
42 | rebuildLock = no
43 |
44 | addFile = (f,dir) ->
45 | path = dir + f
46 | watch[f+dir] = fs.watch path,{persistent:on}, changeFile(path,f)
47 |
48 | changeFile = (path,name) -> ->
49 | if rebuildLock is on
50 | console.log ':dev', '[locked]', path
51 | return
52 | clearTimeout timeout
53 | rebuildFiles.push path
54 | timeout = setTimeout rebuild, 100
55 |
56 | rebuild = ->
57 | rebuildLock = on
58 | console.log 'coffee',['-co',root+'/build/'].concat rebuildFiles.uniq()
59 | i = cp.spawn 'coffee',['-co',root+'/build/'].concat rebuildFiles.uniq()
60 | i.on 'close', (status) ->
61 | console.log 'done'
62 | rebuildFiles = []
63 | rebuildLock = off
64 | server.kill('SIGHUP') if server?
65 | client.kill('SIGHUP') if client?
66 | server = cp.spawn 'coffee', ['server/server.coffee']
67 | server.stdout.setEncoding 'utf8'
68 | server.stderr.setEncoding 'utf8'
69 | server.stdout.on 'data', console.log
70 | server.stderr.on 'data', console.log
71 | setTimeout ( ->
72 | client = cp.spawn 'chromium-browser', ["-app=http://localhost:9999"]
73 | client.stdout.setEncoding 'utf8'
74 | client.stderr.setEncoding 'utf8'
75 | client.stdout.on 'data', console.log
76 | client.stderr.on 'data', console.log
77 | ), 500
78 |
79 | readDir root + '/common/', addFile
80 | readDir root + '/client/', addFile
81 | console.log ':dev', 'watching', Object.keys(watch).length
82 |
--------------------------------------------------------------------------------
/mod/core/server/ids.coffee:
--------------------------------------------------------------------------------
1 |
2 | # ██████ ██████ ██████ ██
3 | # ██ ██ ██ ██ ██ ██ ██
4 | # ██████ ██ ██ ██ ██ ██
5 | # ██ ██ ██ ██ ██ ██
6 | # ██ ██████ ██████ ███████
7 |
8 | $public class IdPool
9 | empty: 0x00
10 | full: 0xFF
11 | constructor:(opts)->
12 | Object.assign @,opts
13 | @length = 0
14 | @lastFree = 0
15 | @s = if @s? then @s else $id.getRange @max
16 | @u = new Uint8Array @max/8
17 | # @f = new Set
18 | $id[@name] = @
19 | $id.pools.push @
20 | # console.log "pool(#{@name}:#{@s}:#{@max}) get:#{@lastFree}".bold.inverse
21 | take:(obj)->
22 | obj.pool = @
23 | id = obj.id - @s
24 | @u[byteNum = floor id/8] = byte = @u[byteNum] + ( 1 << bit = id - byteNum*8 )
25 | @lastFree = byteNum unless byte is @full
26 | @length++
27 | # console.log "pool(#{@name}:#{@s}:#{@lastFree}:#{@max}) take(#{byteNum}:#{bit}:#{id}) byte(#{byte.toString 2})"
28 | free:(id)->
29 | bit = ( id = id - @s ) - 8*byteNum = floor id/8
30 | #console.log "pool(#{@name}:#{@s}:#{@lastFree}:#{@max}) free(#{byteNum}:#{bit}:#{id}) byte(#{@u[byteNum].toString 2})".red
31 | @u[byteNum] = byte = @u[byteNum] ^ ( 1 << bit )
32 | # @f.add byteNum
33 | @length--
34 | # console.log "pool(#{@name}:#{@s}:#{@lastFree}:#{@max}) free(#{byteNum}:#{bit}:#{id}) byte(#{byte.toString 2})".yellow
35 | getBlock:->
36 | c = @max; f = @full; l = @lastFree || 0
37 | return p for i in [0..@max] when f isnt @u[p = l + i % c]
38 | throw new Error 'Pool out of Bocks'
39 | get:(obj)->
40 | # console.log "pool(#{@name}:#{@s}:#{@max}) get:#{@u[@lastFree].toString 2}".bold.inverse.red
41 | byte = b unless 0xFF is b = @u[byteNum = @lastFree]
42 | byte = b unless 0xFF is b = @u[byteNum = @getBlock()] unless byte?
43 | throw new Error 'Pool out of Ids' unless byte?
44 | for bit in [0..7] when 0 is ( byte & ( 1 << bit ) )
45 | obj.id = id = @s + byteNum*8 + bit
46 | @take obj
47 | # console.log "pool(#{@name}:#{@s}:#{@lastFree}:#{@max}) get(#{byteNum}:#{bit}:#{id}) byte(#{byte.toString 2})"
48 | return
49 | throw new Error 'Pool out of Ideas, also this should not happen... o0'
50 |
51 | new class IdManager
52 | reserved: new Map
53 | lastId: 0
54 | maxId: 0xFFFF
55 | pools: []
56 | constructor:->
57 | $static '$id', @
58 | new IdPool name:'eternal', max:10000
59 | new IdPool name:'dynamic', max:20000
60 | getRange:->
61 | l = 0
62 | l = max l, pool.s + pool.max for pool in @pools
63 | l
64 | free:(list)->
65 | list = list.sort (a,b)-> a-b
66 | reserve:(obj,max,key='_'+obj.id)->
67 | @[key] = new IdPool
68 | id = @lastId + 1
69 | @lastId = @lastId + max
70 | @reserved.set obj, max
71 |
--------------------------------------------------------------------------------
/mod/nuu-mbt/shader/asteroid.vert:
--------------------------------------------------------------------------------
1 |
2 | varying vec3 vNormal;
3 | varying vec2 vTex;
4 | varying float vElevator;
5 | varying float vNoiser;
6 |
7 | uniform float seed;
8 | uniform float time;
9 | uniform float temperature;
10 | uniform vec3 light;
11 |
12 | #define PI 3.1415926535897932384626433832795
13 |
14 | vec3 random3(vec3 c) {
15 | float j = seed + 4096.0*sin(dot(c,vec3(17.0, 59.4, 15.0)));
16 | vec3 r;
17 | r.z = fract(512.0*j);
18 | j *= .125;
19 | r.x = fract(512.0*j);
20 | j *= .125;
21 | r.y = fract(512.0*j);
22 | return r-0.5; }
23 |
24 | const float F3 = 0.3333333;
25 | const float G3 = 0.1666667;
26 | float snoise(vec3 p) {
27 | vec3 s = floor(p + dot(p, vec3(F3)));
28 | vec3 x = p - s + dot(s, vec3(G3));
29 | vec3 e = step(vec3(0.0), x - x.yzx);
30 | vec3 i1 = e*(1.0 - e.zxy);
31 | vec3 i2 = 1.0 - e.zxy*(1.0 - e);
32 | vec3 x1 = x - i1 + G3;
33 | vec3 x2 = x - i2 + 2.0*G3;
34 | vec3 x3 = x - 1.0 + 3.0*G3;
35 | vec4 w, d;
36 | w.x = dot(x, x);
37 | w.y = dot(x1, x1);
38 | w.z = dot(x2, x2);
39 | w.w = dot(x3, x3);
40 | w = max(0.6 - w, 0.0);
41 | d.x = dot(random3(s), x);
42 | d.y = dot(random3(s + i1), x1);
43 | d.z = dot(random3(s + i2), x2);
44 | d.w = dot(random3(s + 1.0), x3);
45 | w *= w;
46 | w *= w;
47 | d *= w;
48 | return dot(d, vec4(52.0)); }
49 |
50 | float shading(vec3 position){
51 | vec3 l = normalize(light);
52 | return max(0.015, dot(position, l)); }
53 |
54 | float fnoise(vec3 position, const int octaves, float frequency, float persistence) {
55 | float total = 0.0;
56 | float maxAmplitude = 0.0;
57 | float amplitude = 1.0;
58 | total += snoise(position * frequency) * amplitude;
59 | frequency *= 2.0;
60 | maxAmplitude += amplitude;
61 | amplitude *= persistence;
62 | total += snoise(position * frequency) * amplitude;
63 | frequency *= 2.0;
64 | maxAmplitude += amplitude;
65 | amplitude *= persistence;
66 | total += snoise(position * frequency) * amplitude;
67 | frequency *= 2.0;
68 | maxAmplitude += amplitude;
69 | amplitude *= persistence;
70 | total += snoise(position * frequency) * amplitude;
71 | frequency *= 2.0;
72 | maxAmplitude += amplitude;
73 | amplitude *= persistence;
74 | total += snoise(position * frequency) * amplitude;
75 | frequency *= 2.0;
76 | maxAmplitude += amplitude;
77 | amplitude *= persistence;
78 | total += snoise(position * frequency) * amplitude;
79 | frequency *= 2.0;
80 | maxAmplitude += amplitude;
81 | amplitude *= persistence;
82 | return total / maxAmplitude; }
83 |
84 | void main() {
85 | vNormal = normal;
86 | vTex = uv;
87 | vElevator = max(
88 | fnoise(vNormal.xyz, 6, 0.1, 0.8),
89 | fnoise(vNormal.zyx, 5, -0.1, 0.79));
90 | vNoiser = fnoise(vNormal.xzy, 6, 0.1, 0.66);
91 | gl_Position = projectionMatrix * modelViewMatrix *
92 | vec4(position, 1.0) +
93 | vec4(vElevator*.3,.0,.0,.1); }
94 |
--------------------------------------------------------------------------------
/mod/nuu/sprites/objects_nuu.json:
--------------------------------------------------------------------------------
1 | [
2 | { "class": "ship",
3 | "name": "Human",
4 | "sprite": "st_suit",
5 | "slots":{
6 | "structure":[{"size":"suit","default":"Human Skin"}],
7 | "utility":[{"size":"suit","default":"Human Heart"}],
8 | "weapon":[{"size":"suit","default":"Stock Multitool"}]},
9 | "stats":{
10 | "crew":1,
11 | "mass":1,
12 | "fuel_consumption":0} },
13 |
14 | { "class": "ship",
15 | "name": "Exosuit",
16 | "sprite": "st_suit",
17 | "slots":{
18 | "structure":[
19 | {"size":"suit","default":"Stock Fabric"},
20 | {"size":"suit","default":"Stock ExoThrusters"}
21 | ],
22 | "utility":[],
23 | "weapon":[{"size":"suit","default":"StockMultitool"}]},
24 | "stats":{
25 | "crew":1,
26 | "mass":1,
27 | "fuel_consumption":0} },
28 |
29 | { "class": "outfit",
30 | "name" : "Stock ExoThrusters",
31 | "size" : "suit",
32 | "itemId": 500,
33 | "stats" : {
34 | "mass" : 15,
35 | "price" : 0,
36 | "thrust" : 100,
37 | "turn" : 85,
38 | "engine_limit" : 300,
39 | "energy_usage" : 5,
40 | "fuel" : 200,
41 | "speed" : 175
42 | },
43 | "type" : "modification",
44 | "info" : {
45 | "description" : "Clogged, corroded and cracked in every way possible while still remaining in functional condition, this engine really belongs on the scrap heap.",
46 | "name" : "Stock ExoThrusters",
47 | "gfx_store" : "engine04"
48 | },
49 | "slot" : { "prop" : "engines", "$t" : "structure" },
50 | "extends" : "Outfit" },
51 |
52 | { "class": "outfit",
53 | "name" : "Stock Fabric",
54 | "size" : "suit",
55 | "itemId": 501,
56 | "stats" : {
57 | "armour": 10,
58 | "mass": 1,
59 | "cargo": 0
60 | },
61 | "type" : "modification",
62 | "info" : {
63 | "description" : "A rag, really.",
64 | "name" : "Stock Fabric",
65 | "gfx_store" : "engine04"
66 | },
67 | "slot" : { "prop" : "hull", "$t" : "structure" },
68 | "extends" : "Outfit" },
69 |
70 | { "class": "com", "name":"Admiral Assembly", "logo":"admiralassembly" },
71 | { "class": "com", "name":"FayPoe", "logo":"faypoe" },
72 | { "class": "com", "name":"Ganymede Gas", "logo":"ganymede_gas" },
73 | { "class": "com", "name":"G-Force", "logo":"gforce" },
74 | { "class": "com", "name":"Greywash", "logo":"greywash" },
75 | { "class": "com", "name":"Johnson and Jetson", "logo":"jetson" },
76 | { "class": "com", "name":"Ponomareva GaussGun", "logo":"ponomarevagaussgun" },
77 | { "class": "com", "name":"Russels", "logo":"russels" },
78 | { "class": "com", "name":"Sirius Robotics", "logo":"siriusrobotics" },
79 | { "class": "com", "name":"Solarsuits", "logo":"solarsuits" },
80 | { "class": "com", "name":"Taikuipment", "logo":"taikuipment" },
81 | { "class": "com", "name":"Tau", "logo":"tau" },
82 | { "class": "com", "name":"Tepeshkovalaser", "logo":"tepeshkovalaser" },
83 | { "class": "com", "name":"Valkyrie", "logo":"valkyrie" }
84 |
85 | ]
86 |
--------------------------------------------------------------------------------
/mod/nuu-mbt/sysgen.coffee:
--------------------------------------------------------------------------------
1 |
2 | global.$static = (name,value) -> global[name] = value
3 | $static.list = global
4 | global.$library = (args...) -> for a in args
5 | if Array.isArray a then global[a[0]] = require a[1] else global[a] = require a
6 | global.$public = (args...) -> global[a.name] = a for a in args
7 | global.$cue = (f) -> setImmediate f
8 |
9 | $static 'debug', no
10 | $static 'isClient', no
11 | $static 'isServer', yes
12 | BROWSER = process.env.BROWSER || 'chromium'
13 |
14 | ## Load sources
15 | $cp = require 'child_process'
16 | $fs = require 'fs'
17 |
18 | deps =
19 | common : JSON.parse $fs.readFileSync './common/build.json'
20 | client : JSON.parse $fs.readFileSync './client/build.json'
21 | server : JSON.parse $fs.readFileSync './server/build.json'
22 |
23 | for lib in deps.server.require
24 | if Array.isArray lib
25 | if lib.length is 3
26 | $static lib[0], require(lib[1])[lib[2]]
27 | else $static lib[0], require(lib[1])
28 | else $static lib, require lib
29 |
30 | ## Initialize express
31 | $static 'app', app = express()
32 | $static 'coffee', coffee = require 'coffeescript'
33 |
34 | ## Setup Webserver
35 | app.use require('morgan')('combined',{})
36 | app.use require('body-parser').urlencoded keepExtensions: true, uploadDir: '/tmp/', limit: '1mb', extended:true
37 | app.use require('compression')()
38 | app.use require('cookie-parser')()
39 | app.use '/shader', express.static('mod/nuu/shader',etag:yes)
40 |
41 | express.static.mime.define({'text/html': ['frag','vert']});
42 |
43 | app.post '/upload/:name', (req,res)->
44 | out = $fs.createWriteStream 'build/imag/' + req.params.name
45 | req.pipe out
46 | req.on 'end', ->
47 | console.log 'upload'.green, req.params.name
48 | # $cp.spawn 'feh', ['build/imag/' + req.params.name]
49 | res.end()
50 |
51 | app.get '/', (req,res) ->
52 | res.set 'Content-Type', 'text/html'
53 | res.send $fs.readFileSync 'mod/nuu/render.html'
54 |
55 | scriptfile = (n)-> app.get '/'+n+'.js', (req,res) ->
56 | res.set 'Content-Type', 'text/javascript'
57 | if $fs.existsSync 'mod/nuu/'+n+'.coffee'
58 | res.send coffee.compile( $fs.readFileSync('mod/nuu/'+n+'.coffee').toString 'utf8' )
59 | else res.send $fs.readFileSync 'mod/nuu/'+n+'.js'
60 |
61 | pngfile = (n)-> app.get '/'+n+'.png', (req,res) ->
62 | res.set 'Content-Type', 'image/png'
63 | res.send $fs.readFileSync 'mod/nuu/'+n+'.png'
64 |
65 | scriptfile n for n in ['stargen','three','spritegen']
66 | pngfile n for n in ['star_spectrum']
67 |
68 | app.get '/quit', (req,res)->
69 | $cp.spawnSync 'pkill', ['-f','Xephyr']
70 | res.end()
71 |
72 | app.listen 9998, ->
73 | console.log 'sysgen-server:9998'.yellow, 'ready'.green
74 | return
75 | p = $cp.spawnSync 'rm',['-f','/tmp/.X84-lock'], stdio:'inherit'
76 | p = $cp.spawn 'Xephyr',['-screen','820x820',':84'], stdio:'inherit'
77 | setTimeout ( ->
78 | p = $cp.spawn BROWSER,['--temp-profile','--app=http://localhost:9998/'], stdio:'inherit', env: DISPLAY:':84'
79 | p.on 'close', -> process.exit 0
80 | ), 1000
81 |
--------------------------------------------------------------------------------
/pd/cursor.js:
--------------------------------------------------------------------------------
1 |
2 | class Cursor {
3 | constructor({ $schema, $db, query, opts = {} }) {
4 | Object.assign(this, { $schema, $db, ...opts });
5 | this.limit = this.limit || Infinity;
6 | this._populate = [];
7 | const { $or, $and, ...fields } = query;
8 | if (!$or && !$and) this.query = this.evaluate(fields);
9 | if ($or) this.query = this.$or($or);
10 | if ($and) this.query = this.$and([$and]);
11 | this.query = this.query || "true";
12 | //console.debug("QUERY:", this.query);
13 | }
14 | exec() {
15 | const test = eval(`([key,json]) => { return ${this.query}; }`);
16 | const done = new Promise(async (resolve) => {
17 | let all = this.$db.forEach();
18 | let count = 0,
19 | done,
20 | value;
21 | const result = new Map();
22 | Object.assign(this, { result });
23 | while (({ done, value } = await all.next())) {
24 | if (done) break;
25 | if (!test(value)) continue;
26 | let [id, record] = value;
27 | this.$post && ( record = await this.$post(id, record, this) );
28 | result.set(id, record);
29 | if (count++ > this.limit) break;
30 | }
31 | resolve(this.$filter ? this.$filter.call(this, this) : this);
32 | });
33 | this.done = new Proxy(done, {
34 | get: (_, key) => {
35 | const target = !!done[key] ? done : this;
36 | const value = target[key];
37 | if (!!value && typeof value === "function") return value.bind(target);
38 | return value;
39 | },
40 | });
41 | return this.done;
42 | }
43 | populate(...rules) {
44 | this._populate = [...this._populate, ...rules];
45 | return this.done;
46 | }
47 | async toArray() {
48 | return Array.from(this.result.values());
49 | }
50 | $logical(operator, operands, path = "json") {
51 | if (operands.length === 0) return "";
52 | return `(${operands
53 | .map((rules) => this.evaluate(rules, path))
54 | .filter((s) => !!s)
55 | .join(`) ${operator} (`)})`;
56 | }
57 | $and = (operands, path) => this.$logical("&&", operands, path);
58 | $or = (operands, path) => this.$logical("||", operands, path);
59 | evaluate(rules, path = "json") {
60 | const accessor = (k) => [path, "[" + JSON.stringify(k) + "]"].join("?.");
61 | const entries = Object.entries(rules);
62 | if (entries.length === 0) return "";
63 | const match = (path, key, rule) => {
64 | switch (key) {
65 | case "$regex":
66 | return `!! (${rule.toString()}).test(${path})`;
67 | default:
68 | return `${path} === ${JSON.stringify(rule)}`;
69 | }
70 | };
71 | return `(${entries
72 | .map(([key, rule]) => {
73 | const path = accessor(key);
74 | if (rule instanceof RegExp) return match(path, key, rule);
75 | if (this.$schema.isArray(key))
76 | return `${path}?.some(value => ${match("value", key, rule)})`;
77 | //else if ( this.schema.isObject(path) )
78 | return match(path, key, rule);
79 | })
80 | .join("")})`;
81 | }
82 | }
83 |
84 | module.exports = Cursor;
85 |
--------------------------------------------------------------------------------
/mod/nuu-mbt/shader/earth.frag:
--------------------------------------------------------------------------------
1 | uniform sampler2D tex0;
2 | uniform sampler2D star_spectrum;
3 | uniform float seed;
4 | uniform float time;
5 | uniform float temperature;
6 | uniform vec3 light;
7 | varying vec3 vNormal;
8 | varying vec2 vTex;
9 |
10 | #define PI 3.1415926535897932384626433832795
11 |
12 | vec3 random3(vec3 c) {
13 | float j = seed + 4096.0*sin(dot(c,vec3(17.0, 59.4, 15.0)));
14 | vec3 r;
15 | r.z = fract(512.0*j);
16 | j *= .125;
17 | r.x = fract(512.0*j);
18 | j *= .125;
19 | r.y = fract(512.0*j);
20 | return r-0.5; }
21 |
22 | const float F3 = 0.3333333;
23 | const float G3 = 0.1666667;
24 | float snoise(vec3 p) {
25 | vec3 s = floor(p + dot(p, vec3(F3)));
26 | vec3 x = p - s + dot(s, vec3(G3));
27 | vec3 e = step(vec3(0.0), x - x.yzx);
28 | vec3 i1 = e*(1.0 - e.zxy);
29 | vec3 i2 = 1.0 - e.zxy*(1.0 - e);
30 | vec3 x1 = x - i1 + G3;
31 | vec3 x2 = x - i2 + 2.0*G3;
32 | vec3 x3 = x - 1.0 + 3.0*G3;
33 | vec4 w, d;
34 | w.x = dot(x, x);
35 | w.y = dot(x1, x1);
36 | w.z = dot(x2, x2);
37 | w.w = dot(x3, x3);
38 | w = max(0.6 - w, 0.0);
39 | d.x = dot(random3(s), x);
40 | d.y = dot(random3(s + i1), x1);
41 | d.z = dot(random3(s + i2), x2);
42 | d.w = dot(random3(s + 1.0), x3);
43 | w *= w;
44 | w *= w;
45 | d *= w;
46 | return dot(d, vec4(52.0)); }
47 |
48 | float shading(vec3 position){
49 | vec3 l = normalize(light);
50 | return max(0.015, dot(position, l)); }
51 |
52 | float fnoise(vec3 position, const int octaves, float frequency, float persistence) {
53 | float total = 0.0;
54 | float maxAmplitude = 0.0;
55 | float amplitude = 1.0;
56 | total += snoise(position * frequency) * amplitude;
57 | frequency *= 2.0;
58 | maxAmplitude += amplitude;
59 | amplitude *= persistence;
60 | total += snoise(position * frequency) * amplitude;
61 | frequency *= 2.0;
62 | maxAmplitude += amplitude;
63 | amplitude *= persistence;
64 | total += snoise(position * frequency) * amplitude;
65 | frequency *= 2.0;
66 | maxAmplitude += amplitude;
67 | amplitude *= persistence;
68 | total += snoise(position * frequency) * amplitude;
69 | frequency *= 2.0;
70 | maxAmplitude += amplitude;
71 | amplitude *= persistence;
72 | total += snoise(position * frequency) * amplitude;
73 | frequency *= 2.0;
74 | maxAmplitude += amplitude;
75 | amplitude *= persistence;
76 | total += snoise(position * frequency) * amplitude;
77 | frequency *= 2.0;
78 | maxAmplitude += amplitude;
79 | amplitude *= persistence;
80 | return total / maxAmplitude; }
81 |
82 | float computeDiffuse(vec3 normal) {
83 | return dot( normal, vec3(1.) ); }
84 |
85 | void main() {
86 | vec2 p = vTex;
87 | float n1 = max(
88 | fnoise(vNormal.xyz, 6, 0.1, 0.8),
89 | fnoise(vNormal.zyx, 6, -0.1, 0.79)
90 | );
91 | float n2 = fnoise(vNormal.xzy, 6, 0.1, 0.66);
92 | vec4 c = vec4(n1,n1,n1,1.);
93 | if ( n1 > 0.9 ) { c += vec4(n1,n1,n1,1.);}
94 | if ( n1 > 0.3 ) { c += vec4(n1,n2,n1,1.);}
95 | if ( n1 > 0.205 ) { c += vec4(0.,1.,0.,1.);}
96 | else if ( n1 > 0.2 ) { c += vec4(1.,1.,0.,1.);}
97 | else { c += vec4(n2*.05,n2*.05,1.,1.);}
98 | gl_FragColor = vec4( c.xyz * shading(vNormal.xyz), 1.);}
99 |
--------------------------------------------------------------------------------
/mod/nuu-mbt/shader/mars.frag:
--------------------------------------------------------------------------------
1 | uniform sampler2D tex0;
2 | uniform sampler2D star_spectrum;
3 | uniform float seed;
4 | uniform float time;
5 | uniform float temperature;
6 | uniform vec3 light;
7 | varying vec3 vNormal;
8 | varying vec2 vTex;
9 |
10 | #define PI 3.1415926535897932384626433832795
11 |
12 | vec3 random3(vec3 c) {
13 | float j = seed + 4096.0*sin(dot(c,vec3(17.0, 59.4, 15.0)));
14 | vec3 r;
15 | r.z = fract(512.0*j);
16 | j *= .125;
17 | r.x = fract(512.0*j);
18 | j *= .125;
19 | r.y = fract(512.0*j);
20 | return r-0.5; }
21 |
22 | const float F3 = 0.3333333;
23 | const float G3 = 0.1666667;
24 | float snoise(vec3 p) {
25 | vec3 s = floor(p + dot(p, vec3(F3)));
26 | vec3 x = p - s + dot(s, vec3(G3));
27 | vec3 e = step(vec3(0.0), x - x.yzx);
28 | vec3 i1 = e*(1.0 - e.zxy);
29 | vec3 i2 = 1.0 - e.zxy*(1.0 - e);
30 | vec3 x1 = x - i1 + G3;
31 | vec3 x2 = x - i2 + 2.0*G3;
32 | vec3 x3 = x - 1.0 + 3.0*G3;
33 | vec4 w, d;
34 | w.x = dot(x, x);
35 | w.y = dot(x1, x1);
36 | w.z = dot(x2, x2);
37 | w.w = dot(x3, x3);
38 | w = max(0.6 - w, 0.0);
39 | d.x = dot(random3(s), x);
40 | d.y = dot(random3(s + i1), x1);
41 | d.z = dot(random3(s + i2), x2);
42 | d.w = dot(random3(s + 1.0), x3);
43 | w *= w;
44 | w *= w;
45 | d *= w;
46 | return dot(d, vec4(52.0)); }
47 |
48 | float shading(vec3 position){
49 | vec3 l = normalize(light);
50 | return max(0.015, dot(position, l)); }
51 |
52 | float fnoise(vec3 position, const int octaves, float frequency, float persistence) {
53 | float total = 0.0;
54 | float maxAmplitude = 0.0;
55 | float amplitude = 1.0;
56 | total += snoise(position * frequency) * amplitude;
57 | frequency *= 2.0;
58 | maxAmplitude += amplitude;
59 | amplitude *= persistence;
60 | total += snoise(position * frequency) * amplitude;
61 | frequency *= 2.0;
62 | maxAmplitude += amplitude;
63 | amplitude *= persistence;
64 | total += snoise(position * frequency) * amplitude;
65 | frequency *= 2.0;
66 | maxAmplitude += amplitude;
67 | amplitude *= persistence;
68 | total += snoise(position * frequency) * amplitude;
69 | frequency *= 2.0;
70 | maxAmplitude += amplitude;
71 | amplitude *= persistence;
72 | total += snoise(position * frequency) * amplitude;
73 | frequency *= 2.0;
74 | maxAmplitude += amplitude;
75 | amplitude *= persistence;
76 | total += snoise(position * frequency) * amplitude;
77 | frequency *= 2.0;
78 | maxAmplitude += amplitude;
79 | amplitude *= persistence;
80 | return total / maxAmplitude; }
81 |
82 | float computeDiffuse(vec3 normal) {
83 | return dot( normal, vec3(1.) ); }
84 |
85 | void main() {
86 | vec2 p = vTex;
87 | float n1 = max(
88 | fnoise(vNormal.xyz, 6, 0.1, 0.8),
89 | fnoise(vNormal.zyx, 5, -0.1, 0.79)
90 | );
91 | float n2 = fnoise(vNormal.xzy, 6, 0.1, 0.66);
92 | vec4 c = vec4(n1,n1,n1,1.);
93 | if ( n1 > 0.9 ) { c += vec4(n1,n1,n1,1.);}
94 | if ( n1 > 0.3 ) { c += vec4(n1,n2,n1,1.);}
95 | if ( n1 > 0.205 ) { c += vec4(1.,0.,0.,1.);}
96 | else if ( n1 > 0.2 ) { c += vec4(.8,.2,0.,1.);}
97 | else { c += vec4(.1,n2*.05,n2*.05,1.);}
98 | gl_FragColor = vec4( c.xyz * shading(vNormal.xyz), 1.);}
99 |
--------------------------------------------------------------------------------
/mod/nuu-naev/build.coffee:
--------------------------------------------------------------------------------
1 | global.marked = require 'marked'
2 |
3 | markdown = (src,tgt)-> (c)->
4 | fs.readFile src, (error,data)->
5 | return c error if error
6 | txt = do data.toString
7 | txt = txt.replace ' , ', ', '
8 | txt = txt.replace ' ,', ', '
9 | while m = txt.match /Author:([^,]+), /
10 | txt = txt.replace m[0], "**" + m[1].trim() + "**"
11 | while m = txt.match /License:([^\n]+)\n/
12 | txt = txt.replace m[0], " (***" + m[1].trim() + "***)\n"
13 | txt = txt.replace /\ \ \ \ \ \ \ \*\ \*\n/g, ''
14 | txt = txt.replace /,\ \ \(\*\*\*PD\)\*\*\*\)/g, ' (PD)'
15 | txt = txt.replace /\ \*\ \*\*/g, '## **'
16 | txt = txt.replace '## **Bobbens', '\n## **Bobbens'
17 | blurb = """
18 | ## Licenses
19 | These refer to the contributors of the [NAEV](http://blog.naev.org/) project.
20 |
21 | """
22 | o = []
23 | authorP = author = mode1 = mode2 = null
24 | lines = txt.split('\n')
25 | txt = n while txt isnt n = txt.replace '\n\n','\n'
26 | for line,i in lines
27 | if line.match /\#/
28 | authorP = author = mode1 = mode2 = null
29 | author = line
30 | if line is ' * outfit'
31 | mode1 = 'outfit'
32 | else if line is ' * planet'
33 | mode1 = 'planet'
34 | else if line is ' * commodity'
35 | mode1 = 'commodity'
36 | else if line is ' * space'
37 | mode2 = 'space'
38 | else if line is ' * store'
39 | mode2 = 'store'
40 | else if line is ' * exterior'
41 | mode2 = 'exterior'
42 | else if line.match /png$/
43 | n = line.replace /.* /, ''
44 | p = ['build',mode1,mode2,n].join '/'
45 | continue unless fs.existsSync p
46 | o.push authorP = author unless authorP
47 | o.push ''
48 |
49 | txt = o.join '\n'
50 | fs.writeFile tgt.replace('html','txt'), blurb + txt, ->
51 | fs.writeFile tgt, marked.marked(blurb + txt), c
52 | null
53 | null
54 |
55 | module.exports.build = (c) ->
56 | depend(dirs)( ->
57 | $s [
58 | fetch 'contrib/naev.zip', 'https://github.com/bobbens/naev/archive/master.zip'
59 | unzip 'contrib/naev.zip', 'contrib/naev-master'
60 | mkdir 'build/gfx'
61 | markdown 'contrib/naev-master/dat/gfx/ARTWORK_LICENSE', 'build/ARTWORK_LICENSE.html'
62 | markdown 'contrib/naev-master/dat/snd/SOUND_LICENSE', 'build/SOUND_LICENSE.html'
63 | link 'contrib/naev-master/dat/snd/sounds', 'build/sounds'
64 | linkFilesIn 'contrib/naev-master/dat/gfx/spfx', 'build/gfx'
65 | linkFlatten 'contrib/naev-master/dat/gfx/ship', 'build/gfx'
66 | linkFilesIn 'contrib/naev-master/dat/gfx/bkg', 'build/gfx'
67 | linkFilesIn 'contrib/naev-master/dat/gfx/bkg/star', 'build/gfx'
68 | linkFilesIn 'contrib/naev-master/dat/gfx/planet/space', 'build/gfx'
69 | linkFilesIn 'contrib/naev-master/dat/gfx/outfit/space', 'build/gfx'
70 | link 'contrib/naev-master/dat/gfx/outfit/store', 'build/gfx/store'
71 | generate 'build/objects_naev.json', path.join __dirname, 'import.coffee'
72 | ], c )
73 |
--------------------------------------------------------------------------------
/mod/nuu/server/objects.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | Ship::dropLoot = ->
24 | do @update
25 | newRandom = (classObj) => new classObj state:
26 | S: $moving
27 | x: @x + -@size/2 + Math.random()*@size
28 | y: @y + -@size/2 + Math.random()*@size
29 | v: [ @v[0] + Math.random() * .2 - .1, @v[1] + Math.random()* .2 - .1 ]
30 | newRandom Cargo for i in [0...10]
31 | newRandom Debris for i in [0...10]
32 | return
33 |
34 | Ship::resetHostiles = ->
35 | h = @hostile; @hostile = []
36 | return unless h
37 | Array.remove s.hostile, @ for s in h when s.hostile?
38 | NUU.jsoncastTo @, hostile:@hostile if @inhabited
39 |
40 | Ship::respawn = ->
41 | @locked = true
42 | @respawning = yes
43 | @resetHostiles()
44 | setTimeout ( =>
45 | @dropLoot()
46 | ), 4000
47 | setTimeout ( =>
48 | do @reset
49 | if @user
50 | opts = @user.spawnState()
51 | @setState opts.state; delete opts.state
52 | Object.assign @, opts
53 | else
54 | @x = Math.random()*100
55 | @y = Math.random()*100
56 | @setState S:$moving, x:@x, y:@y, v:[0,0]
57 | if @state.S is $moving
58 | @locked = true
59 | setTimeout ( => @locked = false ), 3000
60 | if @landedAt
61 | NUU.jsoncastTo @, landed: @landedAt.id
62 | do @update
63 | NET.mods.write @, 'spawn'
64 | ), 5000
65 |
66 | Ship::hit = (src,wp) ->
67 | return if @destructing
68 | switch Weapon.impactLogic.call @, wp
69 | when Weapon.impactType.hit
70 | NUU.emit 'ship:hit', @, src, @shield, @armour
71 | NET.mods.write @, 'hit', @shield, @armour
72 | when Weapon.impactType.shieldsDown
73 | NUU.emit 'ship:shieldsDown', @, src
74 | NET.mods.write @, 'shield', @shield, @armour
75 | when Weapon.impactType.disabled
76 | NUU.emit 'ship:disabled', @, src
77 | NET.mods.write @, 'disabled', @shield, @armour
78 | when Weapon.impactType.destroyed
79 | NUU.emit 'ship:destroyed', @, src
80 | NET.mods.write @, 'destroyed', 0, 0
81 | return
82 |
83 | Station::hit = (src,wp) ->
84 | return if @destructing
85 | switch Weapon.impactLogic.call @, wp
86 | when Weapon.impactType.hit
87 | NUU.emit 'station:hit', @, src, @shield, @armour
88 | NET.mods.write @, 'hit', @shield, @armour
89 | when Weapon.impactType.shieldsDown
90 | NUU.emit 'station:shieldsDown', @, src
91 | when Weapon.impactType.disabled
92 | NUU.emit 'station:disabled', @, src
93 | when Weapon.impactType.destroyed
94 | NUU.emit 'station:destroyed', @, src
95 | NET.mods.write @, 'destroyed', 0, 0
96 | return
97 |
--------------------------------------------------------------------------------
/mod/core/common/item.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | ###
24 | this creates the game model, not an instance of a shot
25 | think of it as a factory
26 | ###
27 |
28 | $public class Outfit
29 | constructor: (name) ->
30 | @tpl = Item.byName[name]
31 | Object.assign @, @tpl
32 |
33 | $public class Item
34 | @byId: {}
35 | @byName: {}
36 | @byType: ship:{}, station:{}
37 | @byProp: {}
38 | @byClass: ship:[],outfit:[],gov:[],skill:[],com:[],stellar:[],station:[]
39 |
40 | Item.random = (opts={})->
41 | item = Array.random Object.values Item.byId
42 | return item unless opts.not
43 | while opts.not.includes item.type
44 | item = Array.random Object.values Item.byId
45 | return item
46 |
47 | Item.init = (seed) ->
48 | items = seed
49 | console.log ':nuu', 'init:items' if debug
50 | Item.db = items
51 | NUU.emit 'init:items:pre', items
52 | id = 0
53 | for k,o of Station.template
54 | o.class = "station"
55 | o.name = o.name || k
56 | Item.byClass.station.push Item.byType.station[o.name] = Item.byId[o.itemId = id] = Item.byName[o.name] = o
57 | console.log 'item', 'Station', id, o.name if debug
58 | id++
59 | NUU.emit 'init:items', items
60 | for o in items
61 | Item.byClass[o.class].push o
62 | if o.class is 'ship'
63 | Item.byType['ship'][o.name] = Item.byId[o.itemId = id] = Item.byName[o.name] = o
64 | Ship.byTpl[id] = o.name
65 | Ship.byName[o.name] = id
66 | id++
67 | else if o.class is 'outfit'
68 | Item.byId[o.itemId = id] = Item.byName[o.name] = o
69 | size = o.size || 'small'
70 | t = (
71 | if (s = o.slot) then (if s.$t then s.$t else s)
72 | else if o.extends is 'Ammo' then 'ammo'
73 | else 'cargo' )
74 | Item.byType[t] = {} unless Item.byType[t]
75 | Item.byType[t][size] = {} unless Item.byType[t][size]
76 | Item.byType[t][size][o.name] = o
77 | if o.type
78 | t = o.type.split(' ').pop()
79 | Item.byType[t] = {} unless Item.byType[t]
80 | Item.byType[t][o.name] = o
81 | if s and (t = s.prop)
82 | Item.byProp[t] = {} unless Item.byProp[t]
83 | Item.byProp[t][o.name] = o
84 | id++
85 | else if o.class is 'com'
86 | else if o.class is 'gov'
87 | else console.log 'x', o
88 |
89 | for type, items of Item.byType when items.medium?
90 | if Object.keys(items.medium).length is 0 and Object.keys(items.large).length is 0
91 | Item.byType[type] = items.small
92 | Item.byName.CheatersRagnarokBeam.turret = yes
93 | Item.byName.CheatersRagnarokBeam.stats.track = 1.6
94 | NUU.emit 'init:items:done', items
95 | return seed
96 |
--------------------------------------------------------------------------------
/mod/core/client/user.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | NUU.on 'login:test', ->
24 | Scanner.scale = 500000
25 | # Scanner.toggleFullscreen()
26 | # Target.set $obj.byName.Venus
27 | return
28 |
29 | $public class User
30 | _vehicle: null
31 | primary: id: 0
32 | secondary: id: 0
33 | constructor: (opts)->
34 | Object.assign @, opts
35 | console.log 'user', @ if debug
36 | if @user.nick.match /^test[0-9]+$/
37 | NET.once 'sync', -> NUU.emit 'login:test'
38 | return
39 |
40 | Object.defineProperty User::, 'vehicle',
41 | set: (v) ->
42 | return unless v
43 | window.VEHICLE = @_vehicle = v
44 | v.hide()
45 | v.layer = GFX.PLAYER
46 | v.show()
47 | GFX.repositionPlayer()
48 | @vehicleId = v.id
49 | NUU.emit 'enterVehicle', v
50 | switchWeap(-1) NUU.player, 'primary'
51 | switchWeap(-1) NUU.player, 'secondary'
52 | console.log 'user', 'enterVehicle', v.id if debug
53 | $obj.select yes
54 | get: -> @_vehicle
55 |
56 | switchWeap = (mutate)-> (player,trigger='primary') ->
57 | primary = trigger is 'primary'
58 | ws = player.vehicle.slots.weapon
59 | ct = ws.length
60 | tg = player[trigger]
61 | unless mutate is -1
62 | id = 0 if isNaN id = parseInt tg.id
63 | id = max 0, min ct, id
64 | tg.id = id = mutate id, ct
65 | if ct is id
66 | tg.slot = weap = null
67 | tg.trigger = tg.release = ->
68 | else
69 | tg.slot = weap = ws[id].equip
70 | tg.trigger = -> NET.weap.write 'trigger', primary, id, if TARGET then TARGET.id else undefined
71 | tg.release = -> NET.weap.write 'release', primary, id, if TARGET then TARGET.id else undefined
72 | NUU.emit 'switchWeapon', trigger, weap
73 | Ship::setWeap = (idx,trigger='primary')-> switchWeap( -> idx )(NUU.player,'primary')
74 | Ship::nextWeap = switchWeap (id,ct)-> if ct < 1 then 0 else ++id % (ct + 1 )
75 | Ship::prevWeap = switchWeap (id,ct)-> if ct < 1 then 0 else ( --id + ct ) % (ct + 1 )
76 |
77 | NET.on 'switchShip', (opts) ->
78 | console.log 'user', 'switchShip', opts if debug
79 | NUU.player.vehicle = Ship.byId[opts.i]
80 | NET.emit 'setMount', opts.mount
81 | $obj.select yes
82 | return
83 |
84 | NET.on 'setMount', (list) ->
85 | VEHICLE.mount = list
86 | NUU.player.mountId = id = list.indexOf NUU.player.user.nick
87 | console.log 'user', 'setMount', id # if debug
88 | NUU.player.mount = VEHICLE.mountSlot[id]
89 | NUU.player.equip = VEHICLE.mountSlot[id].equip if NUU.player.mount
90 | VEHICLE.name + '['+ id + ':' + VEHICLE.mountType[id] + ']\n' + VEHICLE.mount
91 | .map (i,k)-> if i then "[#{k}]i" else false
92 | .filter (i)-> i
93 | .join ' '
94 |
95 | NUU.frame = 0
96 |
--------------------------------------------------------------------------------
/mod/core/common/buffer.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | readFLoatLE = (mLen, nBytes) -> (offset)->
24 | e = m = null; nBits = -7
25 | eLen = nBytes * 8 - mLen - 1
26 | eBias = ( eMax = (1 << eLen) - 1 ) >> 1
27 | i = nBytes + d = -1
28 | s = @[offset + i]
29 | i += d
30 | e = s & (1 << -nBits) - 1
31 | s >>= -nBits
32 | nBits += eLen
33 | while nBits > 0
34 | e = e * 256 + @[offset + i]
35 | i += d
36 | nBits -= 8
37 | m = e & (1 << -nBits) - 1
38 | e >>= -nBits
39 | nBits += mLen
40 | while nBits > 0
41 | m = m * 256 + @[offset + i]
42 | i += d
43 | nBits -= 8
44 | if e == 0 then e = 1 - eBias
45 | else if e == eMax
46 | return if m then NaN else ( if s then -1 else 1 ) * Infinity
47 | else
48 | m = m + 2 ** mLen
49 | e = e - eBias
50 | return ( if s then -1 else 1 ) * m * 2 ** ( e - mLen )
51 |
52 | writeFloatLE = (mLen, nBytes)-> (value, offset)->
53 | e = m = c = null; i = 0; d = 1
54 | eLen = nBytes * 8 - mLen - 1
55 | eBias = ( eMax = (1 << eLen) - 1 ) >> 1
56 | rt = if mLen == 23 then 2 ** (-24) - 2 ** (-77) else 0
57 | s = if value < 0 or value == 0 and 1 / value < 0 then 1 else 0
58 | value = Math.abs value
59 | if isNaN(value) or value == Infinity
60 | m = if isNaN(value) then 1 else 0
61 | e = eMax
62 | else
63 | e = Math.floor Math.log(value) / Math.LN2
64 | ( e--; c *= 2 ) if value * (c = 2 ** (-e)) < 1
65 | value += if e + eBias >= 1 then rt / c else rt * 2 ** ( 1 - eBias )
66 | ( e++; c /= 2 ) if value * c >= 2
67 | if e + eBias >= eMax
68 | ( m = 0; e = eMax )
69 | else if e + eBias >= 1
70 | m = (value * c - 1) * 2 ** mLen
71 | e = e + eBias
72 | else
73 | m = value * 2 ** (eBias - 1) * 2 ** mLen
74 | e = 0
75 | while mLen >= 8
76 | @[offset + i] = m & 0xff
77 | i += d; m /= 256; mLen -= 8
78 | e = e << mLen | m
79 | eLen += mLen
80 | while eLen > 0
81 | @[offset + i] = e & 0xff
82 | i += d; e /= 256; eLen -= 8
83 | @[offset + i - d] |= s * 128
84 | return
85 |
86 | Uint8Array::readUInt16LE = (offset)-> @[offset] | ( @[offset + 1] << 8 )
87 | Uint8Array::writeUInt16LE = (value,offset)-> @[offset] = (value & 0xff); @[offset + 1] = (value >>> 8)
88 | Uint8Array::readUInt32LE = (offset)-> ( (@[offset]) | (@[offset + 1] << 8) | (@[offset + 2] << 16) ) + ( @[offset + 3] * 0x1000000 )
89 | Uint8Array::writeUInt32LE = (value,offset)-> @[offset + 3] = (value >>> 24); @[offset + 2] = (value >>> 16); @[offset + 1] = (value >>> 8); @[offset] = (value & 0xff)
90 | Uint8Array::readFloatLE = readFLoatLE 23,4
91 | Uint8Array::writeFloatLE = writeFloatLE 23,4
92 | Uint8Array::readDoubleLE = readFLoatLE 52,8
93 | Uint8Array::writeDoubleLE = writeFloatLE 52,8
94 |
--------------------------------------------------------------------------------
/mod/nuu-mbt/shader/sun.frag:
--------------------------------------------------------------------------------
1 |
2 | uniform sampler2D star_spectrum;
3 | uniform float seed;
4 | uniform vec4 temperature;
5 | uniform float time;
6 | uniform vec3 light;
7 |
8 | varying vec3 vNormal;
9 | varying vec2 vTex;
10 |
11 | #define PI 3.1415926535897932384626433832795
12 |
13 | vec3 random3(vec3 c) {
14 | float j = seed + 4096.0*sin(dot(c,vec3(17.0, 59.4, 15.0)));
15 | vec3 r;
16 | r.z = fract(512.0*j);
17 | j *= .125;
18 | r.x = fract(512.0*j);
19 | j *= .125;
20 | r.y = fract(512.0*j);
21 | return r-0.5;
22 | }
23 |
24 | const float F3 = 0.3333333;
25 | const float G3 = 0.1666667;
26 | float snoise(vec3 p) {
27 | vec3 s = floor(p + dot(p, vec3(F3)));
28 | vec3 x = p - s + dot(s, vec3(G3));
29 | vec3 e = step(vec3(0.0), x - x.yzx);
30 | vec3 i1 = e*(1.0 - e.zxy);
31 | vec3 i2 = 1.0 - e.zxy*(1.0 - e);
32 | vec3 x1 = x - i1 + G3;
33 | vec3 x2 = x - i2 + 2.0*G3;
34 | vec3 x3 = x - 1.0 + 3.0*G3;
35 | vec4 w, d;
36 | w.x = dot(x, x);
37 | w.y = dot(x1, x1);
38 | w.z = dot(x2, x2);
39 | w.w = dot(x3, x3);
40 | w = max(0.6 - w, 0.0);
41 | d.x = dot(random3(s), x);
42 | d.y = dot(random3(s + i1), x1);
43 | d.z = dot(random3(s + i2), x2);
44 | d.w = dot(random3(s + 1.0), x3);
45 | w *= w;
46 | w *= w;
47 | d *= w;
48 | return dot(d, vec4(52.0)); }
49 |
50 | float snoise(vec3 uv, float res){
51 | const vec3 s = vec3(1e0, 1e2, 1e4);
52 | uv *= res;
53 | vec3 uv0 = floor(mod(uv, res))*s;
54 | vec3 uv1 = floor(mod(uv+vec3(1.), res))*s;
55 | vec3 f = fract(uv); f = f*f*(3.0-2.0*f);
56 | vec4 v = vec4(uv0.x+uv0.y+uv0.z, uv1.x+uv0.y+uv0.z, uv0.x+uv1.y+uv0.z, uv1.x+uv1.y+uv0.z);
57 | vec4 r = fract(sin(v*1e-3)*1e5);
58 | float r0 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);
59 | r = fract(sin((v + uv1.z - uv0.z)*1e-3)*1e5);
60 | float r1 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);
61 | return mix(r0, r1, f.z)*2.-1.; }
62 |
63 | float snoiseFractal(vec3 m) {
64 | return 0.5333333 *
65 | snoise(m,2.) +
66 | 0.2666667 * snoise(2.0*m,2.) +
67 | 0.1333333 * snoise(4.0*m,2.) +
68 | 0.0666667 * snoise(8.0*m,2.); }
69 |
70 | float freqs[4];
71 |
72 | float shading(vec4 position){
73 | vec3 l = normalize(light);
74 | return max(0.08, dot(vNormal, l)); }
75 |
76 | vec3 fineNoise(vec2 p){
77 | float xx = snoise(vec3(p*4.,0.), 100.);
78 | float yy = snoise(vec3(p*4.,0.), 100.);
79 | float zz = snoise(vec3(p*4.,0.), 100.);
80 | float x = xx + snoise(vec3(p*20.,10.), 64.);
81 | float y = yy + snoise(vec3(p*20.,10.), 64.);
82 | float z = zz + snoise(vec3(p*20.,10.), 64.);
83 | return vec3(x,x,x); }
84 |
85 | float u_k = .1;
86 | vec3 plasma(vec2 p){
87 | float v = 0.0;
88 | vec2 c = p * u_k - u_k/2.0;
89 | v += sin((c.x+time));
90 | v += sin((c.y+time)/2.0);
91 | v += sin((c.x+c.y+time)/2.0);
92 | c += u_k/2.0 * vec2(sin(time/3.0), cos(time/2.0));
93 | v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+time);
94 | v = v/2.0;
95 | vec3 col = vec3(sin(PI*v));
96 | return col; }
97 |
98 | void main() {
99 | vec2 p = vTex;
100 | vec3 color;
101 | float unExposure = 3.0;
102 | float unGamma = 1.0 / 0.7;
103 | // base + noise
104 | color.rgb = temperature.rgb + temperature.rgb * plasma(p * 2000.) * fineNoise(p);
105 | // float theta = 1.0 - dot(0.5, p);
106 | // color = color + total - theta;
107 | color = 1.0 - exp(color * -unExposure); // HDR exposure
108 | color = pow(color, vec3(unGamma)); // Gamma correction
109 | gl_FragColor = vec4(color, 1.0);
110 | }
111 |
--------------------------------------------------------------------------------
/mod/nuu/artwork/noscanimg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
78 |
--------------------------------------------------------------------------------
/mod/core/common/inventory.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | if isServer
24 | { Database, Schema } = $tag
25 |
26 | NUU.on "server:db", ( ->
27 | # $tag.db 'InventoryDB'
28 | InventorySchema = new Schema
29 | id: { type: Number, default: 1 }
30 | name: { type: String, required: true }
31 | description: { type: String, default: '' }
32 | type: { type: String, required: true }
33 | amount: { type: Number, default: 0 }
34 | price: { type: Number, default: 0 }
35 | weight: { type: Number, default: 0 }
36 | image: { type: String, default: '' }
37 | created: { type: Date, default: Date.now }
38 | updated: { type: Date, default: Date.now }
39 | deleted: { type: Date, default: null }
40 | global.InventoryDB = $tag.model
41 | name: 'InventoryDB'
42 | path: 'db/InventoryDB'
43 | schema: InventorySchema
44 | console.log Object.keys global.InventoryDB
45 | return
46 | ) if isServer
47 |
48 | $public class Inventory
49 | @byKey: {}
50 | create: yes
51 | constructor: (opts)->
52 | { @key, @create, @data } = opts
53 | return existing if existing = Inventory.byKey[@key]
54 | ( @[k] = v for k,v of EventEmitter::; EventEmitter.call @ ) if isClient
55 | Inventory.byKey[@key] = @
56 | do @read if @key
57 | do @tally if @data
58 |
59 | Inventory::close = -> delete Inventory.byKey[@key]
60 |
61 | Inventory::tally = ->
62 | @types = Object.keys(@data).length
63 | @total = Object.values(@data).reduce ( (v,i)-> v += i ), 0
64 | return
65 |
66 | Inventory::read = NUU.$target
67 | server: ->
68 | @exists = false isnt @data = InventoryDB.$.getSync(@key) || {}
69 | return if @data or not @create
70 | @write @data = {}
71 | return
72 | client: ->
73 | NET.json inventory_read: @key
74 | return
75 |
76 | Inventory::write = NUU.$target
77 | server: ->
78 | do @tally
79 | InventoryDB.$.set @key, @data if @key
80 | return
81 | client: ->
82 |
83 | Inventory::has = (item,count=1)->
84 | return false unless d = @data[item]
85 | return false unless d >= count
86 | return d
87 |
88 | Inventory::add = (item,count=1)->
89 | @data[item] = ( @data[item] || 0 ) + count
90 | do @write
91 | return
92 |
93 | Inventory::give = (other,item,count=1)->
94 | return false unless @get item, count
95 | other.add item, count
96 | do @write
97 | true
98 |
99 | Inventory::get = (item,count=1)->
100 | return false unless @data[item]? and @data[item] >= count
101 | @data[item] -= count
102 | do @write
103 | true
104 |
105 | NET.on "inventory_read", (msg,src)->
106 | return src.error '_no_handle' unless u = src.handle
107 | i = new Inventory Object.assign msg, create:no
108 | src.json inventory_write: key:msg.key, data: i.data
109 | i.close()
110 | return
111 |
112 | NET.register "inventory_write",
113 | server: (msg,src)->
114 | i = new Inventory msg
115 | src.json inventory_write: key:msg.key, data: i.data
116 | i.close()
117 | client: (msg)->
118 | return unless i = Inventory.byKey[msg.key]
119 | i.data = msg.data
120 | i.emit 'read', i
121 | return
122 |
--------------------------------------------------------------------------------
/pd/record.js:
--------------------------------------------------------------------------------
1 | const { v4: uuid } = require("uuid");
2 | const util = require("util");
3 |
4 | const SchemaRecord = ({ Schema, $schema, keys, requireds, type, defaults }) => {
5 | class Record {
6 | constructor({ id, ...fields }) {
7 | this.data = {};
8 | for (const k of keys) {
9 | const defaultValue = defaults[k];
10 | const isFunction = typeof defaultValue === "function";
11 | fields[k] === undefined &&
12 | (defaultValue && isFunction
13 | ? (this.data[k] = defaultValue())
14 | : defaultValue !== undefined &&
15 | (this.data[k] = clone(defaultValue)));
16 | }
17 | this.id = id || uuid();
18 | Object.assign(this.data, fields);
19 | }
20 | async save() {
21 | await this.$.ready;
22 | const errors = [];
23 | for (const k of requireds)
24 | if (!this.data[k]) errors.push(`required[${k}]`);
25 | if (errors.length > 0)
26 | throw new Error(`save error: ${errors.join(", ")}`);
27 | await this.$.set(this.id, this.data);
28 | return this;
29 | }
30 |
31 | delete() {
32 | return this.$.delete(this.id);
33 | }
34 | toJSON = () => ({ ...this.data, id: this.id });
35 | inspect() {
36 | return JSON.stringify({ ...this.data, id: this.id }, null, 2);
37 | }
38 | static each = (fn) =>
39 | Record.$.each(([k, r]) => fn(new Record({ id: k, ...r })));
40 | static forEach = (fn) => Record.$.forEach(fn);
41 | static all = () => Record.$.all().then((a) => a.map((r) => new Record(r)));
42 | static find = (query) => {
43 | const { $post, $filter } = Record;
44 | return Record.$.find(query, { $schema, $post, $filter });
45 | };
46 | static findOne = async (query) => {
47 | const { $post, $filter } = Record;
48 | const list = await Record.$.find(query, {
49 | limit: 1,
50 | $schema,
51 | $post,
52 | $filter,
53 | });
54 | return list[0];
55 | };
56 | static findById = async (id) => {
57 | const rec = await Record.$.get(id);
58 | return new Record({ ...rec, id });
59 | };
60 | static findByIdSync = async (id) => {
61 | const rec = await Record.$.getSync(id);
62 | return new Record({ ...rec, id });
63 | };
64 | static $filter = (cursor) => cursor.toArray();
65 | static $post = async (id, value, cursor) => {
66 | if (cursor._populate)
67 | await Promise.all(
68 | cursor._populate.map(async (field) => {
69 | const descriptor = this.$.Model?.fields?.[field];
70 | if (
71 | !descriptor ||
72 | descriptor.type !== $schema.constructor.Types.ObjectId
73 | )
74 | return;
75 | value[field] = await Schema.Types[descriptor.ref].findById(
76 | value[field]
77 | );
78 | //console.log(field, value[field], descriptor);
79 | return;
80 | })
81 | );
82 | return new Record({ id, ...value });
83 | };
84 | static delete = (query) => Record.$.delete(query);
85 | static deleteOne = (query) => Record.$.deleteOne(query);
86 | }
87 | Object.assign(Record.prototype, $schema.methods);
88 |
89 | Object.defineProperties(Record.prototype, {
90 | $schema: { enumerable: false, value: $schema },
91 | toJSON: { enumerable: false, value: Record.prototype.toJSON },
92 | toString: { enumerable: false, value: Record.prototype.toString },
93 | [util.inspect.custom]: {
94 | enumerable: false,
95 | value: Record.prototype.inspect,
96 | },
97 | data: { enumerable: false, writable: true },
98 | });
99 | return Record;
100 | };
101 |
102 | function clone(value) {
103 | if (typeof value === "object") return JSON.parse(JSON.stringify(value));
104 | return value;
105 | }
106 |
107 | module.exports = SchemaRecord;
108 |
--------------------------------------------------------------------------------
/mod/nuu/artwork/logo_2018.svg:
--------------------------------------------------------------------------------
1 |
2 |
114 |
--------------------------------------------------------------------------------
/mod/nuu/client/starfield.coffee:
--------------------------------------------------------------------------------
1 |
2 | # ███ ██ ███████ ██████ ██ ██ ██ █████
3 | # ████ ██ ██ ██ ██ ██ ██ ██ ██ ██
4 | # ██ ██ ██ █████ ██████ ██ ██ ██ ███████
5 | # ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
6 | # ██ ████ ███████ ██████ ██████ ███████ ██ ██
7 |
8 | NUU.on 'rules', ->
9 | # Nebula.deterministic = new Deterministic rules.systemName + "-nebulae"
10 | # Nebula.random() for i in [0..2+max 1, Math.ceil Nebula.deterministic.double()*5]
11 | return
12 |
13 | $public class Nebula
14 | @list: [ "02","16","21","25","29","33","04","17","22","26","30","34","10","19","23","27","31","12","20","24","28","32" ]
15 | @random:->
16 | idx = round Nebula.deterministic.double() * ( Nebula.list.length - 1 )
17 | img = Nebula.list[idx]
18 | n = PIXI.Sprite.from "build/gfx/nebula#{img}.png"
19 | n.position.set(
20 | Nebula.deterministic.double() * 4*GFX.width - 2*GFX.width
21 | Nebula.deterministic.double() * 4*GFX.height - 2*GFX.height )
22 | GFX.nebulae.addChild n
23 |
24 | # ███████ ████████ █████ ██████ ███████ ██ ███████ ██ ██████
25 | # ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
26 | # ███████ ██ ███████ ██████ █████ ██ █████ ██ ██ ██
27 | # ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
28 | # ███████ ██ ██ ██ ██ ██ ██ ██ ███████ ███████ ██████
29 |
30 | $public class GFX.StarfieldLayer
31 | count:1000
32 | constructor:->
33 | VEHICLE.v = [random(),random()]
34 | @geometry = new THREE.Geometry
35 | i = 0
36 | while i++ < @count
37 | @geometry.vertices.push new THREE.Vector3(0,0,0)
38 | map = GFX.loader.load 'build/gfx/moon-D02.png'
39 | # material = new THREE.PointsMaterial transparent:true, alphaTest:0.1, size:1.5
40 | # GFX.debugShader 'FRAGMENT_SHADER', GFX.StarfieldLayer.fragment
41 | # GFX.debugShader 'VERTEX_SHADER', GFX.StarfieldLayer.vertex
42 | @material = new THREE.ShaderMaterial
43 | precision:'highp'
44 | transparent: true
45 | depthWrite: false
46 | uniforms:
47 | uScreen: value: [GFX.width,GFX.height]
48 | uOffset: value: [0,0]
49 | uMap: value: map
50 | fragmentShader: GFX.StarfieldLayer.fragment
51 | vertexShader: GFX.StarfieldLayer.vertex
52 | blending: THREE.AdditiveBlending
53 | # transformFeedbackVaryings: outPosition: 'position'
54 | @particles = new THREE.Points @geometry, @material
55 | @particles.for = 'starfield'
56 | @randomize()
57 | GFX.scene.add @particles
58 | GFX.children.push @
59 | GFX.on 'resize', @randomize.bind(@)
60 | @lastUpdate = performance.now()
61 | randomize:->
62 | { width,height,wdb2,hgb2 } = GFX
63 | return if @lastWidth is width and @lastHeight is height
64 | @lastWidth = width; @lastHeight = height
65 | pos = @geometry.vertices; i = 0
66 | while i < @count
67 | p = pos[i]
68 | p.z = random()
69 | p.x = -wdb2 + width *Math.random()
70 | p.y = -wdb2 + height*Math.random()
71 | i++
72 | @material.uniforms.uScreen.value = [width,height]
73 | @geometry.verticesNeedUpdate = true
74 | @lastUpdate = performance.now()
75 | return
76 | update:(time=performance.now())->
77 | [vx,vy] = VEHICLE.v
78 | svx = svy = 0
79 | m = min 0.2, mag = Math.sqrt vx*vx + vy*vy
80 | if m isnt 0
81 | svx = vx / mag * m
82 | svy = vy / mag * m
83 | uniforms = @material.uniforms
84 | dt = time - @lastUpdate
85 | uniforms.uOffset.value[0] += -svx * dt
86 | uniforms.uOffset.value[1] += svy * dt
87 | @material.needsUpdate = true
88 | @lastUpdate = time
89 | return
90 |
91 | GFX.StarfieldLayer.vertex = """
92 | uniform highp vec2 uScreen;
93 | uniform highp vec2 uOffset;
94 | varying float vAlpha;
95 | void main() {
96 | vAlpha = (position.z + abs(sin(position.x + position.y))) / 4.;
97 | highp vec3 newPosition = vec3(
98 | -uScreen.x/2. + mod(position.x + uOffset.x *vAlpha,uScreen.x),
99 | -uScreen.y/2. + mod(position.y + uOffset.y *vAlpha,uScreen.y),
100 | position.z );
101 | gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
102 | gl_PointSize = 3.5 + abs(sin(position.x))*position.z; }
103 | """
104 |
105 | GFX.StarfieldLayer.fragment = """
106 | uniform sampler2D uMap;
107 | varying float vAlpha;
108 | void main() {
109 | vec4 tex = texture2D( uMap, gl_PointCoord );
110 | gl_FragColor = vec4( 1., 1., 1., tex.a * vAlpha * .2 ); }
111 | """
112 |
--------------------------------------------------------------------------------
/mod/core/client/cache.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | $public class Bottle
24 | constructor:(@limit)-> @stack = []; @active = 0
25 | next: =>
26 | if @stack.length is 0 then @active--
27 | else @stack.shift() @next
28 | add: (fnc) =>
29 | if @active < @limit then fnc @next, @active++
30 | else @stack.push fnc
31 |
32 | $b = new Bottle(3)
33 | URL = window.URL or window.webkitURL
34 | indexedDB = window.indexedDB or window.webkitIndexedDB or window.mozIndexedDB or window.OIndexedDB or window.msIndexedDB
35 | iDBtx = window.IDBTransaction or window.webkitIDBTransaction or window.OIDBTransaction or window.msIDBTransaction
36 |
37 | $static 'Cache', new class BlobCacheIndexedDB
38 | constructor: ->
39 | dbVersion = 1.0
40 | createObjectStore = (dataBase) -> dataBase.createObjectStore 'nuu'
41 | req = indexedDB.open 'nuucache', dbVersion
42 | req.onerror = (event) -> console.log 'data', 'Error creating/accessing IndexedDB database'
43 | req.onupgradeneeded = (event) -> createObjectStore event.target.result
44 | req.onsuccess = (event) =>
45 | console.log 'data', 'Success creating/accessing IndexedDB database' if debug
46 | @db = req.result
47 | @db.onerror = (event) -> console.log 'data','Error creating/accessing IndexedDB database'
48 | if @db.setVersion # Interim solution for Google Chrome to create an objectStore. Will be deprecated
49 | if @db.version isnt dbVersion
50 | setVersion = @db.setVersion dbVersion
51 | setVersion.onsuccess = => ready createObjectStore @db
52 | else do ready
53 | else do ready
54 | null
55 | @get_ = @get; queue = {}
56 | @get = (path,callback) ->
57 | l = queue[path] || queue[path] = []
58 | l.push callback if -1 is l.indexOf callback
59 | ready = =>
60 | @get = @get_
61 | @get k, c for c in v for k, v of queue
62 | NUU.emit 'cache:ready'
63 | null
64 |
65 | getTexture: (url)-> new Promise (resolve,reject)=>
66 | t = @db.transaction ['nuu'], "readonly"
67 | p = url.replace /^\//,''
68 | q = t.objectStore('nuu').get p
69 | q.onerror = reject
70 | q.onsuccess = (event)=>
71 | r = new Image
72 | r.onload = (event) ->
73 | t = new THREE.Texture r
74 | t.needsUpdate = yes
75 | resolve t
76 | if blob = event.target.result then r.src = URL.createObjectURL blob
77 | else @fetch p, (blob)-> r.src = URL.createObjectURL blob
78 | return
79 |
80 | get: (path,callback) ->
81 | path = path.replace(/^\//,'')
82 | tx = @db.transaction([ 'nuu' ], "readonly")
83 | q = tx.objectStore('nuu').get(path)
84 | q.onerror = (event) -> console.log event
85 | q.onsuccess = (event) =>
86 | if ( imgFile = event.target.result )
87 | imgURL = URL.createObjectURL imgFile
88 | callback imgURL
89 | # URL.revokeObjectURL imgURL
90 | else @fetch path, (blob)->
91 | imgURL = URL.createObjectURL blob
92 | callback imgURL
93 | # URL.revokeObjectURL imgURL
94 | null
95 | null
96 | null
97 |
98 | fetch: (path, callback) ->
99 | $b.add (release) =>
100 | x = new XMLHttpRequest
101 | x.responseType = 'blob'
102 | x.open 'GET', path, true
103 | x.addEventListener 'error', (e) -> console.log 'data', 'fetch-error', path, e
104 | x.addEventListener 'load', => if x.status == 200
105 | tx = @db.transaction [ 'nuu' ], "readwrite"
106 | put = tx.objectStore('nuu').put x.response, path
107 | callback x.response
108 | release()
109 | null
110 | x.send()
111 | null
112 | null
113 |
114 | drop:->
115 | indexedDB.deleteDatabase 'nuucache'
116 | window.location.reload()
117 |
--------------------------------------------------------------------------------
/mod/nuu/artwork/icon_2018.svg:
--------------------------------------------------------------------------------
1 |
2 |
91 |
--------------------------------------------------------------------------------
/mod/core/client/client.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | window.$static = (name,value) -> window[name] = value
24 | $static.list = window
25 | window.$public = (args...) -> window[a.name] = a for a in args
26 | window.$cue = (f) -> setTimeout 0,f
27 |
28 | ###
29 | Client-specific constants / $statics
30 | ###
31 |
32 | $static 'isClient', yes
33 | $static 'isServer', no
34 | $static 'debug', no
35 |
36 | $static 'NET', {}
37 | $static 'NUU', {}
38 |
39 | $static 'DUMMY', dummy:yes, id:0, d:0, x:0, y:0, v: [0,0], update: (->), updateSprite: (->), updateScanner:(->), updateShortrange:(->), state:S:'none'
40 | $static 'VEHICLE', DUMMY
41 | $static 'TARGET', null
42 | $static 'TTL', new Set
43 |
44 | $static 'WIDTH', 640
45 | $static 'HEIGHT', 480
46 | $static 'WDB2', 320
47 | $static 'HGB2', 240
48 | $static 'WDT2', 1280
49 | $static 'HGT2', 960
50 |
51 | NUU.defaults =
52 | mouseturnoff: off
53 | gfx: hud: off, scanner: off, speedScale: off
54 |
55 | NUU.saveSettings = ->
56 | localStorage.setItem 'config', JSON.stringify NUU.settings
57 | NUU.emit 'settings'
58 | NUU.settings
59 |
60 | NUU.loadSettings = ->
61 | try data = NUU.applyDefaults JSON.parse( localStorage.getItem "config" ), NUU.defaults
62 | catch error then data = NUU.defaults
63 | data = new Proxy data, set:(o,k,v)-> NUU.saveSettings o[k] = v
64 | NUU.settings = data
65 |
66 | NUU.applyDefaults = (o={},d={})->
67 | for k,v of d
68 | if typeof v is 'object' then o[k] = NUU.applyDefaults o[k] || {}, v
69 | else if not o[k]? then o[k] = v
70 | return o
71 |
72 | do NUU.loadSettings
73 |
74 | ###
75 | Load the more strightforward deps
76 | ###
77 |
78 | console.log ':nuu', 'loading deps'
79 |
80 | for lib in deps.client.require
81 | if Array.isArray lib
82 | if lib.length is 3
83 | $static lib[0], require(lib[1])[lib[2]]
84 | else $static lib[0], require(lib[1])
85 | else $static lib, require(lib)
86 |
87 | # Extend NUU/NET (GLUE OBJECTs:)
88 | NUU[k] = v for k,v of EventEmitter::; EventEmitter.call NUU
89 | NET[k] = v for k,v of EventEmitter::; EventEmitter.call NET
90 |
91 | console.log ':nuu', 'loading libs'
92 |
93 | $ ->
94 | NUU.emit 'runtime:ready'
95 | NUU.emit 'settings:apply'
96 |
97 | NUU.on 'start', ->
98 | Object.assign rules, rules[NUU.mode||'dm']
99 | rules NUU
100 | NUU.emit 'settings'
101 |
102 | NUU.on 'gfx:ready', ->
103 | VT100.write """\
104 |
\
105 | \
106 |
\
107 |
108 |
109 |
110 |
111 |
"""
112 | vt.write NUU.intro = """
113 | --- [ FakeNN ] BREAKING ------------------------------------------------------------------------\
114 | Earth and Luna have been overrun by the drones our own creation, and now,\
115 | her Majesty the Kernel is scheming to take Mars and the Jupiter-system!\
116 | ------------------------------------------------------------------------------------------------
117 |
118 | ➜ Press alt-R to register (we only store saltedhashes)
119 | ➜ Press alt-L to show license screen
120 | ➜ Press alt-C for demo
121 |
122 | """
123 | NUU.loginPrompt()
124 | Item.init await new Promise (resolve)-> $.ajax '/build/objects.json', success:resolve
125 | vt.hide = ->
126 | $('img.com').remove()
127 | Window::hide.call @
128 | a = Item.byClass.com.sort -> Math.random() - .5
129 | $('body').append $ """"""
130 | $('body').append $ """"""
131 | return
132 |
133 | NUU.loginComplete = (opts)->
134 | @player = new User opts
135 | Sound.radio.init()
136 | console.log ''
137 | await GFX.initGame()
138 | await GFX.start()
139 | NUU.emit 'start'
140 | Promise.resolve()
141 |
--------------------------------------------------------------------------------
/mod/core/common/ping.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | . .,:cddxddoddxxkO0KKOo,'...
22 | .ll:xxxkKXXXKKKKKXXXXXXXXXNNXKK0Oxc'.
23 | 'OKK0kkOxO0O0K0occdOokxx:dO0KKKKK00Od;,
24 | .cOKKx:..o00x0X0l:;;;lKx,.. .o0KKOkldOOddl,
25 | ,KK0x, .c:xKKKXKdcd0;..'dKk:.;cxXXX0ocox:dkko.
26 | lK0KO' .l0XXXXNNX0Kkc',. ,OKX0KXNNXNNNNXO,.,l00d;.
27 | ;KXKX, 'OXXXOOKkkO,;dOll'.o;lKNXNWWNWNNNNNNk. ;OK0d
28 | .xKXXk.,OXXX0OccdlldKK0Kl'.:loO0OkK0oll,;kXXX0' .ldKo
29 | .ldOXk:.okk0xl,. .cOKXXK0dooc;'. .. .,cOKXXO, ;ol.
30 | :dxkO;.cKl.kk;lc:...cOK0Odcl::cc;::::lllodoclo0X0x'.;kOc
31 | l0K0o..OX0.'dd:oo;;;;:x0kkxodkkkkkxx;;,xl::,,,lKXXx;x0XO;
32 | oXKKd ,0XK; .;;;ooco,...,'.. .,,'. .,'.;:,::.;;:OXX0c.,dd.
33 | oXXKd.,OXKd. .,lx0xoo:;c,.... .'',codk00O00OokdOXKXKcloo.
34 | lKXx. .d00x,. ..cxxk0KXNNNNK0l;;.,0XNNNWWWNK0kxk0kdXOO00:
35 | o0Ko lKXKOoooxxx0XXXXKXXNNXXKx',:lO0kd:;,'. ':dXXKXNo
36 | :0Od. 'KXXKk:dx:'........;cd0KKOd;,dd. .'',,;::. ,0KXKKX;
37 | lXKk..oXX0:... ,;'';::;. .:okoxOx. .,,,c::cc.c0X0lxk.
38 | 'XXX0Olkx,.;;...... ..;. xKXXc .,c:;loxkxx0KKc .'d0K0kOkkdoc'.
39 | .OXXN0. . ,KNNNX0xc'',;:,.'.. ;XWN0kxdc;,,;coKNNXO:. ;NWWWWWWWWWWWNO
40 | .kXXXk. .oKXXKXNWNK0kkOKKKOl kNWO;dO0XNNNNNX0xo. 'NWNNNNNNNNNNKX
41 | :x0Kx. .,cc;:xXNNNWWWNXO' lXWNKl..:x0KKK0doc .ONXXNNNNXKXXk,
42 | ;lxkod: .lOKXXXNXXd .,.:KXXXK0O; .':xxdOo. . 'KNXdKNNNNNNNNd
43 | 'l0XXXK, ,. .. ';. .lO0Oo:. .,. ....'..,ll,. ..'. .. .kXKd '..,xO0Oc
44 | ,ONNNNNNx. ';. ..'. ,;'. 'o:.... .,kKNXNXXkc. ;XNN0..0o ,0XX'
45 | ,kNWNNXXXXOc..ol;'..::,. .lkOKXXKKkd;'dKKXNX0Okdloxc. .ONNNX, kNd cKNO
46 | .kWWWN0l'.:kXXK:'x;..:;,....'xNXKKK0OOoo,;..:,,,. ...cO: cXNWNX' 'XNl 'oX
47 | .dNWWNx; oNWNX:.'. .:,..;:kXd;o:. ',;c;'dOKKdodo:lxO; .OXNWWN, oWX: .
48 | NWWNx: .. dWWWN0'.oc.::::,,l0dd:...:okKXXK0xO0NNXOkKxc' ',OXNNWWk .OW0.
49 | Kx0o 'OXk..xNNXo,xl'l,'lx:,c,OXo;0k0kOO0kcld0x:;:lOO;... .lKx0XXKO0Nc :NNx. .
50 | . .l: ;kKKk0XOc. 'OXo,d:..'.,odxoocxl.OKk. ..ccko,,.o' lKX0KKKK: ,k, xN0,
51 | oc,.'o:. ,0Ko, .xXloXl.dOc. ...:ld;;oxlkckOkkddNKK00ddkKNWK'.:0d ,lKXc '0NO:
52 | XXKK00Kx. ..', .;0X' cXc.xNN0x:. .'',cd0XXNXXXKkd:..oXNWNWWd ;;.cKNWWXl.lXKd.
53 | X0xx0XOd. ,kO0XWWd c0d.cKNNNNKkc;.. .c0x,;,. .oXWWWXl lKo..dXNXkx0Kx.
54 | oNWXKX0:. ckKNWX; .kXl .c0XKKXXXXXX00Xx .kWWXc cXXl .xKXOx0K:
55 | ,l0KOdxOo. . ,0NNO. ,0Kc ,lco0XNNWWWx.ld:... . .'. ...cKk' 'KNNl . .kK0
56 | kxl. .cOXXO..;, .0WNl .00k;'l:oXNKXNNNc,xX00Kl ,:l; '0OOKK: oNNXl ,ldkK
57 | KWW0. .kNNk cO; ;NWN, xWWW00X::NxkXNo.0XNOOXX' ,;dO: OWWWWNd. dKXN0. lk0lo
58 |
59 | David L. Mills * 6/3/1938
60 | designer of NTP, inernet pioneer, hero of man
61 | ###
62 |
63 | $static class SyncPing
64 | constructor:->
65 | @timer = null
66 | @ringId = -1
67 | @ringBf = []
68 | @bestPing = [Infinity,Infinity,Infinity]
69 | @bestDelta = [0,0,0]
70 | @avrgDelta = @avrgPing = @pings = 0
71 |
72 | SyncPing::add = (id,remote)->
73 | local = Date.now()
74 | prediction = local - @avrgDelta
75 | error = remote - prediction
76 | ping = .5 * ( local - @ringBf[id] ) # this assumption is ofc wrong :D
77 | delta = local - ping - remote
78 | return false if @bestPing[0] < ping
79 | @bestPing[2] = @bestPing[1]; @bestPing[1] = @bestPing[0]; @bestPing[0] = ping
80 | @bestDelta[2] = @bestDelta[1]; @bestDelta[1] = @bestDelta[0]; @bestDelta[0] = delta
81 | @avrgPing = @avrgDelta = 0
82 | count = 1 + min 3, @pings
83 | for i in [0...count]
84 | @avrgPing += @bestPing[0]
85 | @avrgDelta += @bestDelta[0]
86 | @avrgPing /= count
87 | @avrgDelta /= count
88 | return true unless 20 < ++@pings and 1.33 > @avrgDelta
89 | # assume that system clocks are sync; TODO: track clock-skew again
90 | console.log 'ASSUMING HOSTS ARE SYNC', @avrgDelta, 'ms delta'
91 | NUU.time = Date.now
92 | SyncPing::add = ->
93 | true
94 |
95 | SyncPing::engage = -> => @timer = $interval 1000, =>
96 | @ringBf[id = ++@ringId % 32] = Date.now()
97 | msg = Buffer.from [NET.pingCode,id]
98 | NET.send msg.toString 'binary'
99 |
100 | SyncPing::disengage = -> => clearInterval @timer
101 |
102 | return if isServer
103 |
104 | $static 'Ping', new SyncPing
105 | NUU.time = -> Date.now() - Ping.avrgDelta
106 | NUU.on 'connect', do Ping.engage
107 | NUU.on 'disconnect', do Ping.disengage
108 |
--------------------------------------------------------------------------------
/mod/nuu/client/render.coffee:
--------------------------------------------------------------------------------
1 |
2 | $public class SpriteSurface
3 | @renderer: null
4 | @camera3d: null
5 | @scene3d: null
6 | @scene2d: null
7 | @camera2d: null
8 | @spriteTL: null
9 | @spriteTR: null
10 | @spriteBL: null
11 | @spriteBR: null
12 | @spriteC: null
13 | @mapC: null
14 | @group: null
15 |
16 | constructor:->
17 | @width = window.innerWidth; @height = window.innerHeight; @wdb2 = @width/2; @hgb2 = @height/2
18 | @camera3d = new THREE.PerspectiveCamera 60, @width / @height, 1, 2100
19 | @camera3d.position.z = 1500
20 | @camera2d = new THREE.OrthographicCamera -@wdb2, @wdb2, @hgb2, -@hgb2, 1, 10
21 | @camera2d.position.z = 10
22 | @scene3d = new THREE.Scene
23 | @scene3d.fog = new THREE.Fog 0x000000, 1500, 2100
24 | @scene2d = new THREE.Scene
25 | # create sprites
26 | amount = 200
27 | radius = 500
28 | @loader = new THREE.TextureLoader
29 | @loader.load 'textures/sprite0.png', createHUDSprites
30 | @mapB = @loader.load('textures/sprite1.png')
31 | @mapC = @loader.load('textures/sprite2.png')
32 | @group = new THREE.Group
33 | materialC = new THREE.SpriteMaterial map: @mapC, color: 0xffffff, fog: true
34 | materialB = new THREE.SpriteMaterial map: @mapB, color: 0xffffff, fog: true
35 | a = 0
36 | while a < amount
37 | x = Math.random() - 0.5
38 | y = Math.random() - 0.5
39 | z = Math.random() - 0.5
40 | material = undefined
41 | if z < 0
42 | material = materialB.clone()
43 | else
44 | material = materialC.clone()
45 | material.color.setHSL 0.5 * Math.random(), 0.75, 0.5
46 | material.map.offset.set -0.5, -0.5
47 | material.map.repeat.set 2, 2
48 | sprite = new (THREE.Sprite)(material)
49 | sprite.position.set x, y, z
50 | sprite.position.normalize()
51 | sprite.position.multiplyScalar radius
52 | @group.add sprite
53 | a++
54 | @scene3d.add @group
55 | @renderer = new THREE.WebGLRenderer
56 | @renderer.setPixelRatio window.devicePixelRatio
57 | @renderer.setSize window.innerWidth, window.innerHeight
58 | @renderer.autoClear = false
59 | # To allow render overlay on top of sprited sphere
60 | document.body.appendChild @renderer.domElement
61 | window.addEventListener 'resize', onWindowResize, false
62 | animate()
63 | return
64 |
65 | createHUDSprites: (texture) ->
66 | material = new (THREE.SpriteMaterial)(map: texture)
67 | @width = material.map.image.@width
68 | @height = material.map.image.@height
69 | @spriteTL = new (THREE.Sprite)(material)
70 | @spriteTL.center.set 0.0, 1.0
71 | @spriteTL.scale.set @width, @height, 1
72 | @scene2d.add @spriteTL
73 | @spriteTR = new (THREE.Sprite)(material)
74 | @spriteTR.center.set 1.0, 1.0
75 | @spriteTR.scale.set @width, @height, 1
76 | @scene2d.add @spriteTR
77 | @spriteBL = new (THREE.Sprite)(material)
78 | @spriteBL.center.set 0.0, 0.0
79 | @spriteBL.scale.set @width, @height, 1
80 | @scene2d.add @spriteBL
81 | @spriteBR = new (THREE.Sprite)(material)
82 | @spriteBR.center.set 1.0, 0.0
83 | @spriteBR.scale.set @width, @height, 1
84 | @scene2d.add @spriteBR
85 | @spriteC = new (THREE.Sprite)(material)
86 | @spriteC.center.set 0.5, 0.5
87 | @spriteC.scale.set @width, @height, 1
88 | @scene2d.add @spriteC
89 | updateHUDSprites()
90 | return
91 |
92 | updateHUDSprites: ->
93 | @width = window.innerWidth / 2
94 | @height = window.innerHeight / 2
95 | @spriteTL.position.set -@width, @height, 1
96 | @spriteTR.position.set @width, @height, 1
97 | @spriteBL.position.set -@width, -@height, 1
98 | @spriteBR.position.set @width, -@height, 1
99 | @spriteC.position.set 0, 0, 1
100 | return
101 |
102 | onWindowResize: ->
103 | @width = window.innerWidth
104 | @height = window.innerHeight
105 | @camera3d.aspect = @width / @height
106 | @camera3d.updateProjectionMatrix()
107 | @camera2d.left = -@wdb2
108 | @camera2d.right = @wdb2
109 | @camera2d.top = @hgb2
110 | @camera2d.bottom = -@hgb2
111 | @camera2d.updateProjectionMatrix()
112 | updateHUDSprites()
113 | @renderer.setSize window.innerWidth, window.innerHeight
114 | return
115 |
116 | animate:->
117 | requestAnimationFrame animate
118 | render()
119 | return
120 |
121 | render = ->
122 | time = Date.now() / 1000
123 | i = 0
124 | l = @group.children.length
125 | while i < l
126 | sprite = @group.children[i]
127 | material = sprite.material
128 | scale = Math.sin(time + sprite.position.x * 0.01) * 0.3 + 1.0
129 | imageWidth = 1
130 | imageHeight = 1
131 | if material.map and material.map.image and material.map.image.@width
132 | imageWidth = material.map.image.@width
133 | imageHeight = material.map.image.@height
134 | sprite.material.rotation += 0.1 * i / l
135 | sprite.scale.set scale * imageWidth, scale * imageHeight, 1.0
136 | if material.map != @mapC
137 | material.opacity = Math.sin(time + sprite.position.x * 0.01) * 0.4 + 0.6
138 | i++
139 | @group.rotation.x = time * 0.5
140 | @group.rotation.y = time * 0.75
141 | @group.rotation.z = time * 1.0
142 | @renderer.clear()
143 | @renderer.render @scene3d, @camera3d
144 | @renderer.clearDepth()
145 | @renderer.render @scene2d, @camera2d
146 | return
147 |
--------------------------------------------------------------------------------
/pd/database.js:
--------------------------------------------------------------------------------
1 | const { spawnSync } = require("child_process");
2 | const { yellow } = require("colors");
3 | const {
4 | readFileSync,
5 | promises: {
6 | stat,
7 | readdir,
8 | mkdir,
9 | rmdir,
10 | readFile,
11 | unlink,
12 | writeFile,
13 | symlink,
14 | readlink,
15 | },
16 | } = require("fs");
17 |
18 | const { join } = require("path");
19 |
20 | const Index = require("./index_map");
21 | const Sync = require("./sync");
22 | const Cursor = require("./cursor");
23 |
24 | const exists = async (p) =>
25 | stat(p)
26 | .then((stat) => stat.isDirectory())
27 | .catch((e) => false);
28 |
29 | class Database {
30 | constructor(opts = {}) {
31 | Object.assign(this, opts);
32 | this.ready = this.init();
33 | this.cache = new Map();
34 | this.readPromise = {};
35 | this.indexes = [];
36 | new Sync(this);
37 | }
38 |
39 | async init() {
40 | if (await this.create()) return this;
41 | if (await this.open()) return this;
42 | throw new Error(`pd could not open: ${this.path}`);
43 | }
44 |
45 | async create() {
46 | let doesExist = await exists(this.path);
47 | if (!this.forceCreate && doesExist) return false;
48 | if (doesExist) await this.purge();
49 | await mkdir(this.path, { recursive: true });
50 | if (!(await this.open())) return false;
51 | if (this.onCreate) await this.onCreate();
52 | }
53 |
54 | async open() {
55 | try {
56 | await readdir(this.path);
57 | return true;
58 | } catch (e) {
59 | console.debug(e);
60 | }
61 | return false;
62 | }
63 |
64 | async set(key, value) {
65 | this.cache.set(key, value);
66 | this.indexes.forEach((i) => i.consume([key, value]));
67 | return this.nextSync.add(key, value);
68 | }
69 |
70 | async all() {
71 | let all = this.forEach();
72 | let done, value, result = [];
73 | while (({ done, value } = await all.next())) {
74 | if (done) break;
75 | result.push(value[1]);
76 | }
77 | return result;
78 | }
79 |
80 | async each(fn) {
81 | let all = this.forEach();
82 | let done,
83 | value,
84 | result = [];
85 | Object.assign(this, { result });
86 | while (({ done, value } = await all.next())) {
87 | if (done) break;
88 | result.push(fn([value[0], value[1]]));
89 | }
90 | return Promise.all(result);
91 | }
92 |
93 | async *forEach() {
94 | const files = await readdir(this.path).then((files) =>
95 | files.map((f) => f.replace(/.json$/, ""))
96 | );
97 | const cached = Array.from(this.cache.entries());
98 | const uncached = files.filter((f) => !this.cache.has(f));
99 | for (const entry of cached) yield entry;
100 | for (const key of uncached) yield [key, await this.get(key)];
101 | }
102 |
103 | async get(key) {
104 | const cachedValue = this.cache.get(key);
105 | if (cachedValue !== undefined) return cachedValue;
106 | const beingRead = this.readPromise[key];
107 | if (beingRead) return beingRead;
108 | return (this.readPromise[key] = new Promise(async (resolve) => {
109 | try {
110 | const file = join(this.path, key + ".json");
111 | const data = await readFile(file, "utf-8");
112 | const json = JSON.parse(data);
113 | this.cache.set(key, json);
114 | this.indexes.forEach((i) => i.consume([key, json]));
115 | delete this.readPromise[key];
116 | resolve(json);
117 | } catch (e) {
118 | //console.log(e.code === 'ENOENT' ? `Cannot read: ${file}`: console.log(e))
119 | resolve(undefined);
120 | }
121 | }));
122 | }
123 |
124 | async getSync(key) {
125 | const cachedValue = this.cache.get(key);
126 | if (cachedValue !== undefined) return cachedValue;
127 | return (this.readPromise[key] = new Promise(async (resolve) => {
128 | try {
129 | const file = join(this.path, key + ".json");
130 | const data = readFileSync(file, "utf-8");
131 | const json = JSON.parse(data);
132 | this.cache.set(key, json);
133 | this.indexes.forEach((i) => i.consume([key, json]));
134 | resolve(json);
135 | } catch (e) {
136 | //console.log(e.code === 'ENOENT' ? `Cannot read: ${file}`: console.log(e))
137 | resolve(undefined);
138 | }
139 | }));
140 | }
141 |
142 | async delete(key) {
143 | const value = await this.get(key);
144 | if (value === undefined) return;
145 | const file = join(this.path, key + ".json");
146 | if (this.cache.has(key)) this.cache.delete(key);
147 | this.indexes.forEach((i) => i.delete(key, value));
148 | await unlink(file);
149 | }
150 |
151 | deleteOne(condition) {}
152 |
153 | async purge() {
154 | this.cache = new Map();
155 | await rmdir(this.path, { recursive: true });
156 | this.indexes.forEach((i) => i.purge());
157 | console.debug("pd purged");
158 | }
159 | find(query, opts = {}) {
160 | return new Cursor({$db:this, query, opts}).exec();
161 | }
162 | findOne(query, opts = {}) {
163 | return new Cursor({$db:this, query, opts:{ ...opts, limit: 1 }}).exec().then(s=>s.toArray()[0]);
164 | }
165 |
166 | index(keyPath, fn) {
167 | new Index(this, keyPath, fn);
168 | }
169 | }
170 |
171 | Database.open = (opts) => new Database(opts);
172 | Database.create = (opts) => new Database({ ...opts, forceCreate: true });
173 |
174 | module.exports = Database;
175 |
--------------------------------------------------------------------------------
/mod/core/common/station.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ███████ ████████ █████ ████████ ██ ██████ ███ ██
22 | ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
23 | ███████ ██ ███████ ██ ██ ██ ██ ██ ██ ██
24 | ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
25 | ███████ ██ ██ ██ ██ ██ ██████ ██ ████ ###
26 |
27 | $obj.register class Station extends Stellar
28 | @interfaces: [$obj,Stellar,Station,Shootable]
29 | @type:'station'
30 | level:1
31 | population:1
32 | shield:1000
33 | armour:1000
34 | weapon:'LaserTurretMK'
35 | interval:1000
36 | massMax:100
37 | orbits:[]
38 | constructor:(opts)->
39 | super Economy.defaults opts,
40 | Station.template[opts.template = opts.template || 'Outpost']
41 | if @template is 'Mine' and not @mines
42 | @mines = 'Fe'
43 | if @mines and @zone
44 | @mines = @zone.deterministic.element @zone.provides()
45 | console.log @zone.root.name, 'mines', @mines if debug
46 | @produces[@mines] = @allocates[@mines] = min 100, @zone.availableFor @mines
47 | @shieldMax = @shield
48 | @armourMax = @armour
49 | @access = @access || []
50 | @hostile = []
51 | @weapon = new Weapon @, @weapon
52 | @weapon.slot = id:0, equip:@weapon
53 | @slots = # Mock items for now
54 | weapon: [ @weapon.slot ]
55 | structure: [
56 | {id:0, size:'large', equip:null}
57 | {id:0, size:'large', equip:null}
58 | ]
59 | @name += "-#{@id}"
60 | # @name += " (#{@constructor.name})" if isClient
61 | return if isClient
62 | do @weapon.blur = =>
63 | return if @destructing
64 | console.log @name, 'lost target' if debug
65 | @weapon.release()
66 | Weapon.Defensive.add @weapon
67 | destructor:->
68 | Weapon.Defensive.remove @weapon
69 | @weapon.destructor()
70 | @weapon = null
71 | super()
72 | toJSON: -> return {
73 | id: @id
74 | key: @key
75 | sprite: @sprite
76 | state: @state
77 | name: @name
78 | mines: @mines
79 | provides: @provides
80 | allocates: @allocates
81 | produces: @produces
82 | consumes: @consumes
83 | template: @template
84 | owner: @owner
85 | access: @access }
86 |
87 | Station.blueprint =
88 | name: 'Station'
89 | sprite: 'sprite@station-shipyard'
90 | weapon: 'LaserTurretMK'
91 | population: 10
92 | massMax: 100
93 | shield: 1000
94 | armour: 1000
95 | produces: {}
96 | provides: {}
97 | consumes: {}
98 | allocates: e:100
99 | mines: false
100 | upgrades: false
101 | requires: false
102 |
103 | Station.template =
104 | Outpost:
105 | sprite: 'station-sphere'
106 | population: 5
107 | shield: 10000
108 | armour: 10000
109 | provides: e:100
110 |
111 | Stronghold:
112 | upgrades: 'Outpost'
113 | sprite: 'station-sphere'
114 | population: 5
115 | shield: 50000
116 | armour: 10000
117 | provides: e:1000
118 |
119 | Fortress:
120 | upgrades: 'Stronghold'
121 | sprite: 'base'
122 | population: 10
123 | shield: 100000
124 | armour: 100000
125 | weapon: 'GraveBeam'
126 | allocates: e:1000
127 |
128 | Powerplant:
129 | sprite: 'station-powerplant'
130 | population: 0
131 | provides: e:5000
132 | scannerSymbol:'䷡'
133 |
134 | Farm:
135 | sprite: 'station-agriculture'
136 | population: 10
137 | allocates: e:100, Farmland:100, H20:10
138 | produces: Food:100
139 | scannerSymbol:'🍽'
140 |
141 | LargeFarm:
142 | upgrades: 'Farm'
143 | sprite: 'station-commerce3'
144 | population: 100
145 | allocates: e:10, Farmland:1000
146 | consumes: H20:20
147 | produces: Food:1000
148 | scannerSymbol:'🍽'
149 |
150 | Habitat:
151 | upgrades: 'Farm'
152 | sprite: 'station-commerce'
153 | population: 100000
154 | allocates: e:10, Executives:10, H20:10000, Food:100000
155 | provides: Executives:100, Workers:50000
156 | scannerSymbol:'🏘'
157 |
158 | Mine:
159 | sprite: 'station-commerce'
160 | population: 2000
161 | allocates: e:200, Food:100
162 | scannerSymbol:'⚒'
163 |
164 | Trade:
165 | sprite: 'station-commerce2'
166 | population: 10000
167 | massMax: 1000000
168 | allocates: e:100, Executives:30, Food:1000
169 | scannerSymbol:'💰'
170 |
171 | Factory:
172 | sprite: 'station-shipyard'
173 | population: 10
174 | allocates: e:100, Executives:10, Food:10
175 | consumes: Fe:10
176 | produces: Kestrel: 1
177 | scannerSymbol:'🏭'
178 |
--------------------------------------------------------------------------------
/mod/core/client/vt100.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | $public class VT100 extends Window
24 | line: []
25 | hist: []
26 | frame: null
27 | cursor: x: 0, y: 0
28 | inputBuffer: ''
29 | promptActive: no
30 |
31 | constructor: (opts={}) ->
32 | super name:'VT100'
33 | @$.addClass 'vt full'
34 | @body.append @input$ = $ """"""
35 | console.user = @write
36 | @write """\
37 | nuu console / v#{$version}\
38 | (c) 2007-2022 Sebastian Glaser <anx@ulzq.de> / (c) 2007-2022 flyc0r\
39 | GNU General Public License v3 / see license screen (alt-L)"""
40 | return
41 |
42 | draw: =>
43 | c = @cursor.x
44 | p = if @promptQuery then @promptQuery + ": " else ''
45 | b = @inputBuffer
46 | b = b.replace /./g, "*" if @stars
47 | b = b.substr(0,c) + '' + b.substr c
48 | @input$.html p + b
49 | @$[0].scroll top: @body.height()
50 |
51 | write: (lines) =>
52 | @input$.before $ """
#{lines}
"""
53 | do @draw
54 |
55 | status: (p,t)->
56 | do @show
57 | if @lastSP and ( @lastSP isnt p or @lastST isnt t )
58 | log @lastSP+':', @lastST
59 | @inputBuffer = t+'\n'
60 | @cursor.x = t.length + 1
61 | @draw @promptQuery = p
62 | @lastSP = p; @lastST = t
63 |
64 | prompt: (p,callback,override) =>
65 | log @lastSP+':', @lastST if @lastSP and not @stars; @lastST = @lastSP = null
66 | return false if @promptActive unless override or p.override
67 | if typeof p is 'object'
68 | @stars = p.stars || false
69 | @overrideKeys = p.key
70 | callback = p.then
71 | p = p.p
72 | @focus()
73 | @promptActive = yes
74 | @promptQuery = p
75 | @inputBuffer = ''
76 | @onReturn = (cmd)=>
77 | log @lastSP+':', @lastST if @lastSP and not @stars; @lastST = @lastSP = null
78 | return true if true is callback.call @, cmd
79 | @promptQuery = 'nuu#'
80 | @hide()
81 | false
82 | @cursor.x = 0
83 | @draw()
84 | true
85 |
86 | pasteHandler: (e)->
87 | c = @cursor.x
88 | d = e.clipboardData
89 | .getData('text')
90 | .replace(/\n/g,'; ')
91 | @inputBuffer = @inputBuffer.substr(0,c) + d + @inputBuffer.substr(c)
92 | @cursor.x += d.length
93 | do @draw
94 |
95 | keyHandler: (e,code)->
96 | return if true is @overrideKeys e if @overrideKeys
97 | c = @cursor.x
98 | if code is 'Enter'
99 | if (fnc = @onReturn)?
100 | i = @inputBuffer
101 | delete @onReturn
102 | @write @promptQuery + ': ' + if @stars then @inputBuffer.replace(/./g,'*') else @inputBuffer
103 | @hist.cursor = @hist.push(@inputBuffer) - 1
104 | @cursor.x = 0
105 | @promptActive = no
106 | @inputBuffer = ''
107 | res = fnc i unless e.shiftKey
108 | @hide() unless res is true
109 | else if code is 'Escape' or code is 'sEscape' or code is 'Backquote'
110 | res = fnc null if not e.shiftKey and ( fnc = @onReturn )?
111 | @hide() unless res is true
112 | else if code is 'ArrowLeft'
113 | @cursor.x = max(0,--@cursor.x)
114 | else if code is 'ArrowRight'
115 | @cursor.x = min(@inputBuffer.length,++@cursor.x)
116 | else if code is 'ArrowUp'
117 | @hist.cursor = max(0,--@hist.cursor)
118 | @inputBuffer = @hist[@hist.cursor]
119 | @cursor.x = @inputBuffer.length
120 | else if code is 'ArrowDown'
121 | @hist.cursor = min(@hist.length-1,++@hist.cursor)
122 | @inputBuffer = @hist[@hist.cursor]
123 | @cursor.x = @inputBuffer.length
124 | else if code is 'Delete'
125 | @inputBuffer = @inputBuffer.substr(0,c) + @inputBuffer.substr(c+1)
126 | else if code is 'aDelete'
127 | @inputBuffer = @inputBuffer.substr(0,c)
128 | @cursor.x = @inputBuffer.length
129 | else if code is 'Backspace'
130 | @inputBuffer = @inputBuffer.substr(0,c-1) + @inputBuffer.substr(c)
131 | @cursor.x = max(0,--@cursor.x)
132 | else if code is 'aBackspace'
133 | @inputBuffer = @inputBuffer.substr(c)
134 | @cursor.x = 0
135 | else
136 | k = e.char || e.key || String.fromCharCode e.charCode
137 | if k.length is 1
138 | @inputBuffer = @inputBuffer.substr(0,c) + k + @inputBuffer.substr(c)
139 | @cursor.x++
140 | @draw()
141 | false
142 |
143 | VT100.toggle = ->
144 | vt.show()
145 | vt.rootPrompt()
146 |
147 | VT100::rootPrompt = (force=no)->
148 | p = (text) ->
149 | return false unless text
150 | try
151 | v = eval(text)
152 | console.user v.toString() if v?
153 | catch e
154 | console.user ( if e and e.message then e.message else e )
155 | true
156 | setTimeout ( new Function 'VT100.toggle()' ), 0
157 | true
158 | vt.prompt 'nuu #', p, force
159 |
160 | Kbd.macro 'console', 'Backquote', 'Show / hide console', VT100.toggle
161 |
--------------------------------------------------------------------------------
/mod/nuu/common/asteroid.coffee:
--------------------------------------------------------------------------------
1 |
2 | # █████ ███████ ████████ ███████ ██████ ██████ ██ ██████
3 | # ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
4 | # ███████ ███████ ██ █████ ██████ ██ ██ ██ ██ ██
5 | # ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
6 | # ██ ██ ███████ ██ ███████ ██ ██ ██████ ██ ██████
7 |
8 | # belt = Array.random [5.0325e8,5.984e+9]
9 |
10 | $obj.register class Asteroid extends $obj
11 | @interfaces: [$obj,Shootable,Debris,Asteroid]
12 | constructor: (opts)->
13 | opts.sprite = 'asteroid-D' + ( opts.size - 10 ).toString().padStart 2,'0'
14 | super opts
15 | @armour = @armourMax = 10 * @size
16 | toJSON:-> undefined
17 |
18 | Asteroid::virtual = yes if isServer
19 |
20 | $obj.register class Asteroid.Belt extends $obj
21 | bigMass:yes
22 | @interfaces: [$obj,Stellar]
23 | constructor: (opts={})->
24 | opts = Object.assign {
25 | seed: 'asdasdas213'
26 | belt: 5.0325e8 # 5.984e+9
27 | width: 1e8
28 | count: 1000
29 | }, opts
30 | opts.parent = $obj.byId[opts.parent] if isClient
31 | opts.state = opts.state || State.orbit.relto opts.parent, opts.belt, 1
32 | super opts
33 | unless @base
34 | @ids = new IdPool name:@name, max:@count unless @ids?
35 | @base = @ids.s
36 | @noise = new Deterministic @seed
37 | @list = new Array @count
38 | for i in [0..@count]
39 | oo = @belt - @width/2 + @width*@noise.doubell()
40 | @list[i] = new Asteroid
41 | resource: ( Element.deterministic @noise for j in [0...5])
42 | state: State.orbit.relto @parent, oo, 1, TAU * @noise.double()
43 | size: max 10, floor @noise.double() * 73
44 | belt: @
45 | id: @base + i
46 | name: @name + '-' + ( @base + i )
47 | $obj.select @list if isClient
48 | loadAssets:->
49 | toJSON:-> id:@id,key:@key,state:@state,seed:@seed,base:@base,count:@count,belt:@belt,width:@width,name:@name,parent:@parent.id
50 |
51 | $obj::closestAsteroid = ->
52 | return no unless Asteroid.list.length > 0
53 | closest = null; dist = Infinity
54 | for p in Asteroid.list
55 | continue if p.destructing
56 | if dist > d = abs $dist @,p
57 | closest = p
58 | dist = d
59 | return [closest,dist]
60 |
61 | return if isClient
62 |
63 | Asteroid.autospawn = (opts={})->
64 | NUU.on 'start', ->
65 | center = $obj.byId[0]
66 | roids = [
67 | { name:"Asteroid Belt", parent:center, belt:4.0325e8, width:3e8, count:2000 }
68 | { name:"Kuyper Belt", parent:center, belt:5.984e9, width:2.992e9, count:5000 }
69 | { name:"D Ring", parent:$obj.byName.Saturn, belt:78315, width:7610, count:500 }
70 | { name:"C Ring", parent:$obj.byName.Saturn, belt:100671, width:17342, count:500 }
71 | { name:"B Ring", parent:$obj.byName.Saturn, belt:130370, width:25580, count:500 }
72 | { name:"Cassini Division", parent:$obj.byName.Saturn, belt:124465, width:4590, count:500 }
73 | { name:"A ring", parent:$obj.byName.Saturn, belt:144077, width:14605, count:500 }
74 | { name:"Roche Division", parent:$obj.byName.Saturn, belt:140682, width:2605, count:500 }
75 | { name:"F Ring", parent:$obj.byName.Saturn, belt:139930, width:500, count:500 }
76 | { name:"Janus/Epimetheus Ring", parent:$obj.byName.Saturn, belt:156500, width:5000, count:1000 }
77 | { name:"G Ring", parent:$obj.byName.Saturn, belt:179500, width:9000, count:1000 }
78 | { name:"Methone Ring Arc", parent:$obj.byName.Saturn, belt:194230, width:500, count:1000, arc:PI/2 }
79 | { name:"Anthe Ring Arc", parent:$obj.byName.Saturn, belt:197665, width:500, count:1000, arc:PI/2 }
80 | { name:"Pallene Ring", parent:$obj.byName.Saturn, belt:214750, width:2500, count:1000 }
81 | { name:"E Ring", parent:$obj.byName.Saturn, belt:630000, width:300000, count:1000 }
82 | { name:"Phoebe Ring", parent:$obj.byName.Saturn, belt:4500000, width:9000000, count:1000 }]
83 | new Asteroid.Belt o for o in roids
84 | return
85 | $worker.push =>
86 | # roids = @list.length
87 | # if roids < opts.max
88 | # dt = opts.max - roids
89 | # new Asteroid for i in [0...dt]
90 | 1000
91 | return
92 |
93 | $obj.register class Asteroid.Fragment extends $obj
94 | @interfaces: [$obj,Shootable,Debris,Asteroid]
95 | ttlFinal: true
96 | localObject: true
97 | constructor: (opts)->
98 | console.log 'fragment'
99 | opts.sprite = 'asteroid-D' + ( opts.size - 10 ).toString().padStart 2, '0'
100 | opts.ttl = NUU.time() + 60000
101 | super opts
102 | @armour = @armourMax = 10 * @size
103 | toJSON:-> id:@id, key:@key, hostile:@hostile, resource:@resource, state:@state, size:@size, ttl:@ttl
104 |
105 | Asteroid::hit = (perp,weapon)->
106 | return if @destructing
107 | return unless dmg = weapon.stats.physical
108 | @armour = max 0, @armour - dmg
109 | NET.mods.write @, ( if @armour is 0 then 'destroyed' else 'hit' ), 0, @armour
110 | return unless @armour is 0
111 | if @resource.length > 1 then for r in @resource
112 | @update()
113 | console.log @v
114 | fragment = new Asteroid
115 | toJSON:-> id:@id, key:@key, hostile:@hostile, resource:@resource, state:@state, size:@size, ttl:@ttl
116 | virtual: false
117 | hostile: []
118 | resource: [r]
119 | size: size = max 10, floor random() * @size / 2
120 | state: S:$moving, x:@x, y:@y, v: [ @v[0] - .02 + random() * .04, @v[1] - .02 + random() * .04 ]
121 | Weapon.hostility perp, fragment
122 | else
123 | NUU.emit 'asteroid:destroyed', perp, @resource
124 | console.log "mined fragment"
125 | @destructor()
126 | null
127 |
--------------------------------------------------------------------------------
/mod/core/common/worker.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | window.global = window if window?
24 | global.TIME = NUU.time()
25 |
26 | $static '$worker', new class TickWorker
27 | constructor:(interval=TICK)->
28 | @list = []
29 | @remove = @remove.bind @
30 | @tick = @tick.bind @
31 | @push = @push.bind @
32 | @timer = setInterval @tick, interval
33 | tick:->
34 | list = @list.filter @notStopped
35 | while f = list.shift()
36 | continue if f.notBefore > time = NUU.time()
37 | global.TIME = time
38 | global.ETIME = Math.floor(TIME/1000000)*1000000
39 | if typeof f isnt 'function'
40 | else if f.stop
41 | else if not isNaN r = f time
42 | f.notBefore = NUU.time() + r
43 | else f.stop = r isnt false
44 | @list = @list.filter @notStopped
45 | push:(f)-> f.stop = false; @list.push f; f
46 | remove:(f)-> f.stop = true
47 | notStopped:(f)-> f.stop isnt true
48 |
49 | class $worker.InlineThread
50 | constructor:(init=@init)->
51 | source = [ '( async function(self){' ]
52 | source.push @workerInit.toInlineCode() if @workerInit
53 | source.push @worker .toInlineCode() if @worker
54 | source.push '} ).call(this,self)'
55 | if source.length isnt 2
56 | @thread = new Worker URL.createObjectURL new Blob [source.join '\n'],type:'text/javascript'
57 | @thread.onmessage = (m)=>
58 | @[k]? v for k,v of m.data
59 | return
60 | @init?()
61 |
62 | $worker.List = (worker)->
63 | list = c = n = count = null
64 | listWorker = (time)->
65 | { count, list } = listWorker
66 | c = n = 0
67 | worker.call at, time while ( at = list[c++] )?
68 | null
69 | listWorker.worker = worker
70 | listWorker.list = []
71 | listWorker.count = 0
72 | listWorker.remove = (item)-> Array.remove @list, item
73 | listWorker.add = (item)->
74 | @list[@count++] = item
75 | @count
76 | listWorker.remove = (item)->
77 | return false unless @list.includes item
78 | Array.remove @list, item
79 | --@count
80 | listWorker.worker = worker
81 | $worker.push listWorker
82 |
83 | $worker.ReduceList = (worker)->
84 | swap = []; list = c = n = count = null
85 | listWorker = (time)->
86 | { count, list } = listWorker
87 | c = n = 0
88 | while c < count and ( at = list[c++] )?
89 | unless false is worker.call at, time
90 | swap[n++] = at
91 | listWorker.list = swap; swap = list
92 | listWorker.count = n
93 | null
94 | listWorker.worker = worker
95 | listWorker.list = []
96 | listWorker.count = 0
97 | listWorker.add = (item)->
98 | @list[@count++] = item
99 | @count
100 | listWorker.remove = (item)->
101 | return false unless @list.includes item
102 | Array.remove @list, item
103 | --@count
104 | $worker.push listWorker
105 |
106 | $worker.PauseList = (opts,worker)->
107 | unless worker
108 | opts = {}
109 | worker = opts
110 | listKey = opts.listKey || "pause" + $worker.PauseList.key++
111 | if debug then setInterval ( ->
112 | console.log '$PauseWorker', listKey, listWorker.count
113 | ), 5000
114 | listWorker = (time)->
115 | { count, list } = listWorker
116 | c = -1; n = 0
117 | while count > ++c
118 | item = list[c]
119 | delay = item[listKey]
120 | if -1 is delay
121 | delete item[listKey]
122 | continue
123 | if time < delay
124 | list[n++] = item
125 | continue
126 | res = worker.call item, time
127 | if -1 is res or -1 is item[listKey]
128 | delete item[listKey]
129 | continue
130 | item[listKey] = time + res
131 | list[n++] = item
132 | listWorker.count = n
133 | return true
134 | listWorker.worker = worker
135 | listWorker.list = []
136 | listWorker.count = 0
137 | listWorker.add = (item)->
138 | item[listKey] = 0
139 | @list[@count++] = item
140 | Object.assign item, opts
141 | listWorker.remove = (item)->
142 | item[listKey] = -1
143 | $worker.push listWorker
144 | $worker.PauseList.key = 0
145 |
146 | $worker.DeadLine = (waitFor,deadline,worker)->
147 | listKey = "deadline" + $worker.DeadLine.key
148 | listKeyLast = "deadlineLast" + $worker.DeadLine.key++
149 | listWorker = (time)->
150 | { count, list } = listWorker
151 | c = -1; n = 0
152 | while count > ++c
153 | item = list[c]
154 | delay = item[listKey]
155 | deadlay = item[listKeyLast]
156 | if delay is false
157 | delete item[listKey]
158 | delete item[listKeyLast]
159 | continue
160 | if time < delay and time < deadlay
161 | list[n++] = item
162 | continue
163 | worker.call item, time
164 | delete item[listKey]
165 | delete item[listKeyLast]
166 | listWorker.count = n
167 | return
168 | listWorker.worker = worker
169 | listWorker.list = []
170 | listWorker.count = 0
171 | listWorker.add = (item)->
172 | item[listKey] = waitFor + time = NUU.time()
173 | unless item[listKeyLast]
174 | item[listKeyLast] = deadline + time
175 | @list[@count++] = item
176 | listWorker.remove = (item)->
177 | item[listKey] = false
178 | $worker.push listWorker
179 | $worker.DeadLine.key = 0
180 |
--------------------------------------------------------------------------------
/mod/preql/common/unit.coffee:
--------------------------------------------------------------------------------
1 |
2 | $public class URPlayer
3 | @byId : {}
4 | @nextid : 0
5 | credits : 0
6 | constructor : ->
7 | URPlayer.byId[@id = URPlayer.nextid++] = @
8 |
9 | $public class URUnit
10 | @nextid : 0
11 | @byId : {}
12 | @byPos : {}
13 | @byPlayer : {}
14 |
15 | @tpl :
16 | byType : u : {}, b : {}
17 | bld: type: 'u', name:'Builder', icon: 'left', cost: 80, hp:5, size:1.5, range:1,dps:1,tps:1,color:'#FF0',actions:['move','attack','build','repair'],build:['brk','trt']
18 | wrk: type: 'u', name:'Worker', icon: 'left', cost: 150, hp:2, size:1.5, range:1,dps:1,tps:1,color:'#FF0',actions:['move','mine','repair']
19 | inf: type: 'u', name:'Infantry', icon: 'left', cost: 100, hp:20, size:1, range:5,dps:5,tps:2,color:'#0F0',actions:['attack','move']
20 | inf: type: 'u', name:'Tank', icon: 'left', cost: 1000, hp:300, size:2, range:15,dps:50,tps:4,color:'#0F0',actions:['attack','move']
21 | brk: type: 'b', name:'Barraks', icon: 'left', cost: 2000, hp:500, size:10, color:'#F22', actions:['build','setrelay'], build: ['inf','bld','wrk']
22 | trt: type: 'b', name:'Turret', icon: 'left', cost: 500, hp:100, size:8, color:'#F22', actions:['attack']
23 |
24 | selected : no
25 |
26 | constructor : (opts={}) ->
27 |
28 | @order = (o='wait',rec={}) =>
29 | # if rec.force
30 | @order.c.cancel() if @order.c and @order.c.cancel
31 | @order.q = []
32 | done = (success) =>
33 | console.log @id, o, 'done', rec, rec
34 | delete Control.worker.list[@id]
35 | rec.success() if rec.success
36 | @order.q.shift().start() if @order.q.length > 0
37 | name : 0
38 | target : target = rec.target
39 | rec.start = start = =>
40 | console.log @id, o, rec
41 | Control.worker.list[@id] = @
42 | @order.c = rec
43 | @[o](rec,done)
44 | if @order.q.length is 0 then start()
45 | else @order.q.push rec
46 |
47 | {x,y,tpl,@owner} = opts
48 | @owner = Control.player unless @owner
49 | URUnit.byId[@id = URUnit.nextid++] = @
50 | URUnit.byPlayer[@owner.id] = @
51 | @setpos(x,y)
52 | @tpl = URUnit.tpl[tpl||'bld']
53 | if @tpl.type is "u" then @unit()
54 | else @building()
55 |
56 | unit : ->
57 | @hp = @tpl.hp
58 | @order 'wait', force: yes
59 |
60 | building : ->
61 | @hp = 0
62 | @operational = 0
63 |
64 | setpos : (x,y) ->
65 | sector = x+"x"+y
66 | if URUnit.byPos[(oldsector = @x+'x'+@y)] and URUnit.byPos[oldsector][@id]
67 | delete URUnit.byPos[oldsector][@id]
68 | if Object.keys(URUnit.byPos[oldsector]) is 0
69 | delete URUnit.byPos[oldsector]
70 | @x = x; @y = y
71 | URUnit.byPos[sector] = {} unless URUnit.byPos[sector]
72 | URUnit.byPos[sector][@id] = @
73 |
74 |
75 | wait : (order,callback) -> callback true
76 |
77 | repair : (order,callback) ->
78 | target = order.target
79 | order.cancel = => delete @work
80 | @move order, (success) =>
81 | return callback false unless success
82 | @work = =>
83 | target.hp += 1
84 | if target.hp > target.tpl.hp
85 | target.hp = target.tpl.hp
86 | delete @work
87 | callback true
88 |
89 |
90 | attack : (order,callback) ->
91 | target = order.target
92 | inrange = =>
93 | order.cancel = => delete @work
94 | @work = =>
95 | target.hp -= @tpl.dps / 1000 * 33
96 | if target.hp < 0
97 | target.hp = 0
98 | callback true
99 | order.onwaypoint = =>
100 | if @distTo(target.x,target.y) <= @tpl.range
101 | order.cancel()
102 | inrange()
103 | @move order, (result) =>
104 | return callback false unless result
105 | inrange()
106 |
107 | move : (order,callback) ->
108 | return unless @tpl.tps
109 | done = =>
110 | @setpos(@moverec.x,@moverec.y)
111 | @moverec = null
112 | order.cancel = null
113 | callback true if callback
114 | waypoint = =>
115 | if @path.length > 0
116 | {x,y} = @path.shift()
117 | @setpos(@moverec.x,@moverec.y) if @moverec
118 | @moverec =
119 | x : x
120 | y : y
121 | eta : eta = 1 / @tpl.tps
122 | start : Date.now()
123 | timer : setTimeout waypoint, eta * 1000
124 | order.cancel = =>
125 | console.log 'cancel'
126 | @setpos(@moverec.x,@moverec.y)
127 | order.cancel = null
128 | clearTimeout @moverec.timer
129 | order.onwaypoint() if order.onwaypoint
130 | else done()
131 | {x,y} = order
132 | {target:{x,y}} = order unless x > 0 and y > 0
133 | try
134 | graph = new Graph(Map.walkmap(),diagonal:yes,heuristic:astar.heuristics.diagonal)
135 | start = graph.grid[@x][@y]
136 | end = graph.grid[x][y]
137 | @path = astar.search(graph,start,end)
138 | console.log @path
139 | waypoint()
140 | catch e
141 | callback false
142 |
143 | distTo : (x,y) -> Math.sqrt (dx=@x-x)*dx+(dy=@y-y)*dy
144 |
145 | render : (ctx,x,y,scale,sb2,now) ->
146 | if @moverec
147 | seconds_passed = (now - @moverec.start) / 1000
148 | completed = Math.min( seconds_passed / @moverec.eta, 1 )
149 | movex = completed * (@moverec.x - @x) * scale
150 | movey = completed * (@moverec.y - @y) * scale
151 | else movex = movey = 0
152 | sx = x * scale + movex + sb2
153 | sy = y * scale + movey + sb2
154 | ss = scale/10*@tpl.size
155 | s2 = ss * 2
156 | if @selected is true
157 | ctx.fillStyle = "#F00"
158 | else ctx.fillStyle = "#FF0"
159 | ctx.beginPath()
160 | ctx.arc sx,sy,ss,0,Math.PI*2
161 | ctx.fill()
162 | ctx.strokeStyle = "solid 1px #000"
163 | ctx.strokeRect sx-ss,sy-ss-5,s2,3
164 | ctx.fillStyle = "#F00"
165 | ctx.fillRect sx-ss,sy-ss-5,s2,3
166 | ctx.fillStyle = "#0F0"
167 | ctx.fillRect sx-ss,sy-ss-5,Math.round((@hp/@tpl.hp)*s2),3
168 | ctx.fillStyle = "#000"
169 | ctx.fillText sx-ss,sy-ss,@hp+'/'+@tpl.hp
170 |
171 | URUnit.tpl.byType[v.type][k] = v for k,v of URUnit.tpl when v.type?
172 |
--------------------------------------------------------------------------------
/mod/nuu-mbt/shader/gas_giant.frag:
--------------------------------------------------------------------------------
1 | uniform sampler2D tex0;
2 | uniform sampler2D star_spectrum;
3 | uniform float seed;
4 | uniform float time;
5 | uniform float temperature;
6 | uniform vec3 light;
7 | varying vec3 vNormal;
8 | varying vec2 vTex;
9 |
10 | #define PI 3.1415926535897932384626433832795
11 |
12 | vec3 random3(vec3 c) {
13 | float j = seed + 4096.0*sin(dot(c,vec3(17.0, 59.4, 15.0)));
14 | vec3 r;
15 | r.z = fract(512.0*j);
16 | j *= .125;
17 | r.x = fract(512.0*j);
18 | j *= .125;
19 | r.y = fract(512.0*j);
20 | return r-0.5;
21 | }
22 |
23 | const float F3 = 0.3333333;
24 | const float G3 = 0.1666667;
25 | float snoise(vec3 p) {
26 | vec3 s = floor(p + dot(p, vec3(F3)));
27 | vec3 x = p - s + dot(s, vec3(G3));
28 | vec3 e = step(vec3(0.0), x - x.yzx);
29 | vec3 i1 = e*(1.0 - e.zxy);
30 | vec3 i2 = 1.0 - e.zxy*(1.0 - e);
31 | vec3 x1 = x - i1 + G3;
32 | vec3 x2 = x - i2 + 2.0*G3;
33 | vec3 x3 = x - 1.0 + 3.0*G3;
34 | vec4 w, d;
35 | w.x = dot(x, x);
36 | w.y = dot(x1, x1);
37 | w.z = dot(x2, x2);
38 | w.w = dot(x3, x3);
39 | w = max(0.6 - w, 0.0);
40 | d.x = dot(random3(s), x);
41 | d.y = dot(random3(s + i1), x1);
42 | d.z = dot(random3(s + i2), x2);
43 | d.w = dot(random3(s + 1.0), x3);
44 | w *= w;
45 | w *= w;
46 | d *= w;
47 | return dot(d, vec4(52.0)); }
48 |
49 | float snoise(vec3 uv, float res){
50 | const vec3 s = vec3(1e0, 1e2, 1e4);
51 | uv *= res;
52 | vec3 uv0 = floor(mod(uv, res))*s;
53 | vec3 uv1 = floor(mod(uv+vec3(1.), res))*s;
54 | vec3 f = fract(uv); f = f*f*(3.0-2.0*f);
55 | vec4 v = vec4(uv0.x+uv0.y+uv0.z, uv1.x+uv0.y+uv0.z, uv0.x+uv1.y+uv0.z, uv1.x+uv1.y+uv0.z);
56 | vec4 r = fract(sin(v*1e-3)*1e5);
57 | float r0 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);
58 | r = fract(sin((v + uv1.z - uv0.z)*1e-3)*1e5);
59 | float r1 = mix(mix(r.x, r.y, f.x), mix(r.z, r.w, f.x), f.y);
60 | return mix(r0, r1, f.z)*2.-1.; }
61 |
62 | float snoiseFractal(vec3 m) {
63 | return 0.5333333 *
64 | snoise(m,2.) +
65 | 0.2666667 * snoise(2.0*m,2.) +
66 | 0.1333333 * snoise(4.0*m,2.) +
67 | 0.0666667 * snoise(8.0*m,2.); }
68 |
69 | float freqs[4];
70 |
71 | vec4 baseColor(){
72 | float u = (temperature - 800.0) / 29200.0;
73 | return texture2D(star_spectrum, vec2(u,0)); }
74 |
75 | float shading(vec3 position){
76 | vec3 l = normalize(light);
77 | return max(0.015, dot(position, l)); }
78 |
79 | vec4 fineNoise(vec2 p){
80 | float xx = snoise(vec3(p*4.,0.), 100.);
81 | float yy = snoise(vec3(p*4.,0.), 100.);
82 | float zz = snoise(vec3(p*4.,0.), 100.);
83 | float x = xx + snoise(vec3(p*20.,10.), 64.);
84 | float y = yy + snoise(vec3(p*20.,10.), 64.);
85 | float z = zz + snoise(vec3(p*20.,10.), 64.);
86 | return vec4(x,x,x,time); }
87 |
88 | float fnoise(vec3 position, const int octaves, float frequency, float persistence) {
89 | float total = 0.0;
90 | float maxAmplitude = 0.0;
91 | float amplitude = 1.0;
92 | total += snoise(position * frequency) * amplitude;
93 | frequency *= 2.0;
94 | maxAmplitude += amplitude;
95 | amplitude *= persistence;
96 | total += snoise(position * frequency) * amplitude;
97 | frequency *= 2.0;
98 | maxAmplitude += amplitude;
99 | amplitude *= persistence;
100 | total += snoise(position * frequency) * amplitude;
101 | frequency *= 2.0;
102 | maxAmplitude += amplitude;
103 | amplitude *= persistence;
104 | total += snoise(position * frequency) * amplitude;
105 | frequency *= 2.0;
106 | maxAmplitude += amplitude;
107 | amplitude *= persistence;
108 | total += snoise(position * frequency) * amplitude;
109 | frequency *= 2.0;
110 | maxAmplitude += amplitude;
111 | amplitude *= persistence;
112 | total += snoise(position * frequency) * amplitude;
113 | frequency *= 2.0;
114 | maxAmplitude += amplitude;
115 | amplitude *= persistence;
116 | return total / maxAmplitude; }
117 |
118 | float rnoise(vec3 position, const int octaves, float frequency, float persistence) {
119 | float total = 0.0;
120 | float maxAmplitude = 0.0;
121 | float amplitude = 1.0;
122 | total += ((1.0 - abs(snoise(position * frequency))) * 2.0 - 1.0) * amplitude;
123 | frequency *= 2.0;
124 | maxAmplitude += amplitude;
125 | amplitude *= persistence;
126 | total += ((1.0 - abs(snoise(position * frequency))) * 2.0 - 1.0) * amplitude;
127 | frequency *= 2.0;
128 | maxAmplitude += amplitude;
129 | amplitude *= persistence;
130 | total += ((1.0 - abs(snoise(position * frequency))) * 2.0 - 1.0) * amplitude;
131 | frequency *= 2.0;
132 | maxAmplitude += amplitude;
133 | amplitude *= persistence;
134 | total += ((1.0 - abs(snoise(position * frequency))) * 2.0 - 1.0) * amplitude;
135 | frequency *= 2.0;
136 | maxAmplitude += amplitude;
137 | amplitude *= persistence;
138 | total += ((1.0 - abs(snoise(position * frequency))) * 2.0 - 1.0) * amplitude;
139 | frequency *= 2.0;
140 | maxAmplitude += amplitude;
141 | amplitude *= persistence;
142 | return total / maxAmplitude; }
143 |
144 | float u_k = .1;
145 | vec4 plasma(vec2 p){
146 | float t = time / 10.;
147 | float v = 0.0;
148 | vec2 c = vec2(p.y,0.) * u_k - u_k/2.0;
149 | v += sin((c.x+t));
150 | v += sin((c.y+t)/2.0);
151 | v += sin((c.x+c.y+t)/2.0);
152 | c += u_k/2.0 * vec2(sin(t/3.0), cos(t/2.0));
153 | v += sin(sqrt(c.x*c.x+c.y*c.y+1.0)+t);
154 | v = v/2.0;
155 | vec3 col = vec3(1, sin(PI*v), cos(PI*v));
156 | return vec4(col, 1); }
157 |
158 | float computeDiffuse(vec3 normal) {
159 | return dot( normal, vec3(1.) ); }
160 |
161 | vec4 colorAt(vec2 p){
162 | return plasma(p * 100.) * .5 + plasma(p * 1000.); }
163 |
164 | void main() {
165 | vec2 p = vTex;
166 | float n1 = fnoise(vNormal.xyz, 6, 0.1, 0.8) * 0.01;
167 | float n2 = rnoise(vNormal.xyz, 5, 5.8, 0.75) * 0.015 - 0.01;
168 |
169 | // c = vec4(.1);
170 | float s = 0.6;
171 | float t1 = snoise( vNormal.xyz * 2.0 ) - s;
172 | float t2 = snoise((vNormal.xyz + 800.0 ) * 2.0) - s;
173 | float t3 = snoise((vNormal.xyz + 1600.0) * 2.0) - s;
174 | float threshold = max(t1 * t2 * t3, 0.0);
175 | float n3 = sin( snoise(vNormal.xyz * 0.2) ) * threshold;
176 | p.y += n1 * 3. + n2 * 1. + n3;
177 |
178 | vec4 c = colorAt(p);
179 | // c += vec4(threshold * 3.0, 0.0, 0.0, 0.0);
180 | gl_FragColor = vec4( c.xyz * shading(vNormal.xyz), 1.);}
181 |
--------------------------------------------------------------------------------
/mod/core/common/stellar.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▒▒▒
22 | ▒▒▒▓▓▓▓▓▓▓▓▓▓█████▓▓▓▓▓▓▒▒▒
23 | ▒▒▒▒▓▓▓▓▓▓▓▓▓▓███▓█▓▓▓▓▓▓▓▓▓▓▒▒▒▒
24 | ▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒
25 | ▒▒▒▒▒▒▓▓▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒ ▒▒
26 | ▓▓▓▒▒▒▒▓▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒
27 | ▒▓▓▓▓▓▓▒▒▒▒▒▒▒▓▓▒▒ ▒▒▓▓▓▒▒
28 | ▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒ ▒▓▓▒
29 | ▒▒▒▒▒ ▒▒▒▒▒ ▒ ▒▓▓▓ ▒▒ ▒
30 | ▒▒▒ ▒▒▓▓▓▒▒▒▓▓▓▓▒▒▒▒▒▒▒▒▒▒
31 | ▒▒▒▒▒▒ ▒▒▓▓▓▒▒▒▒▒▓▓▓▓▒▒▒▓▒▒▓▓▓▓▒▒▒▓▒▒
32 | ▒ ▒▒▓▓▒▒▒▒▒▓▓▓▓▒▒ ▒▒▒
33 | ▒▒▒ ▒▒▒▒▒▒▒▒▓▓▓▒▓▓▒▒▒▒ ▒▒▒▒▒▒▒▒
34 | ▒▓▓▓▒ ▒ ▒▒▒▒▒▒▒▒▒▓▒▒ ▒ ▒▒▒▒▒▒
35 | ▓▒▓▒ ▒▒▒▒▒▓▓▓▓▓▒▒▒ ▒ ▒▓▓▒▒▒
36 | ▒▓▓▒ ▒▒▒▒▒▒▒▒▓▓▓▒▒▒ ▒▒▒▒ ▒▒▒▒
37 | ▒▒▒▒▒▒▒▓▓▒▒▒▒ ▒▒▒▒▓▓▒
38 | ▒▒▒ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒
39 | ▒▒
40 | ▒
41 | ▒
42 | ▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒
43 |
44 | ██████ ██████ ███ ██ ██████ █████ ██████ ██ ██████ ██ ██
45 | ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
46 | ██ ██ ██ ██ ██ ██ ██ ██ ██ ███████ ██ ███ ██ ██ ██ ██ █ ██
47 | ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██
48 | ██████ ██████ ██ ████ ██████ ██ ██ ██████ ███████ ██████ ███ ███
49 | https://en.wikipedia.org/wiki/Don_Daglow ###
50 |
51 | # ███████ ████████ ███████ ██ ██ █████ ██████
52 | # ██ ██ ██ ██ ██ ██ ██ ██ ██
53 | # ███████ ██ █████ ██ ██ ███████ ██████
54 | # ██ ██ ██ ██ ██ ██ ██ ██ ██
55 | # ███████ ██ ███████ ███████ ███████ ██ ██ ██ ██
56 |
57 | $obj.byName = {}
58 | $obj.register class Stellar extends $obj
59 | interval: 1000
60 | @interfaces: [$obj,Stellar]
61 | constructor:(opts)->
62 | super opts
63 | unless @orbits then @orbits = (
64 | if @size > 500 then [500,1000,1500,2000]
65 | else if @size > 300 then [500,1000,1500]
66 | else if @size > 100 then [500,1000]
67 | else [500] )
68 | @lastCycle = @nextCyle = 0
69 | @name = "#{@constructor.name} [#{@id}]" unless @name
70 | $obj.byName[@name] = @
71 | console.log @constructor.name.yellow, @name, ( @buildRoot?.name || '' ).red,( @state?.relto?.name || '' ).bold if debug
72 | Economy.attach @ if isServer
73 | destructor:->
74 | @zone.detach @ if @zone
75 | super()
76 | toJSON: -> return {
77 | id: @id
78 | key: @key
79 | sprite: @sprite
80 | state: @state
81 | name: @name
82 | orbits: if @orbits[0] is 500 then undefined else @orbits
83 | produces: @produces
84 | consumes: @consumes }
85 | produce:-> e:@produces.e * @level
86 |
87 | Stellar::actions = ['travel','orbit','land','jump']
88 | Stellar::defaultAction = ->
89 | d = VEHICLE.dist TARGET
90 | mode = 'travel'
91 | mode = 'orbit' if d < 10e3 and TARGET.bigMass
92 | mode = 'land' if d < TARGET.size + VEHICLE.size
93 | mode
94 |
95 | Object.defineProperty Stellar::, 'zoneDomain', get:->
96 | "#{@name}.#{@zone.root.name}"
97 |
98 | Stellar.init = ->
99 | console.log ':nuu', 'init:stars' if debug
100 | orbits = {}
101 | now = Date.now()
102 | rules.lastId = 256
103 | for i in rules.stars
104 | continue unless o = i[7]
105 | continue unless o.occupiedBy
106 | rules.seedEconomy i, o
107 | for i in rules.stars
108 | [ id, Constructor, name, sprite, orbit, state, relto, args ] = i
109 | orbits[relto+'_'+orbit] = l = orbits[relto+'_'+orbit] || []
110 | l.push id
111 | for i in rules.stars
112 | [ id, Constructor, name, sprite, orbit, state, relto, args ] = i
113 | args = {} unless args
114 | odx = orbits[relto+'_'+orbit].indexOf id
115 | oct = ( orbits[relto+'_'+orbit] || [] ).length
116 | relto$ = $obj.byId[relto] || x:0,y:0,update:$void
117 | relto$.update()
118 | rand = if oct is 1 then TAU * random() else ( TAU / oct ) * odx
119 | vel = 0.05
120 | if 0 < hrs = args.t
121 | vel = ( TAU * orbit ) / ( hrs * 360000 ) # ten times faster
122 | stp = TAU / ( ( TAU * orbit ) / vel )
123 | state = S:state, relto:relto$, t:now, orb:orbit, vel:vel, stp:stp, off:rand
124 | opts = Object.assign args||{}, id:id, name:name, sprite:sprite, state:state
125 | new Constructor opts
126 | return
127 |
128 | Object.defineProperty Stellar::, 'buildRoot', get:->
129 | p = @; u = {}; u[p.id] = true
130 | console.log '-', @name, @state.relto.name if @state.relto if debug
131 | p.state.update time = NUU.time()
132 | while r = p.state.relto
133 | console.log '|', p.name if debug
134 | r.state.update time
135 | if 150000 < d = $dist(p,r)
136 | console.log 'x:dist', d if debug
137 | break
138 | if u[r.id]
139 | console.log 'x:uniq', r.name if debug
140 | break
141 | u[(p = r).id] = true
142 | console.log '->', p.name, p.constructor.name if debug
143 | switch p.constructor.name
144 | when 'Star','Planet','Moon' then p
145 | else null
146 |
147 | $obj.register class Star extends Stellar
148 | @interfaces: [$obj,Stellar]
149 | provides: e:1000
150 | bigMass:yes
151 |
152 | $obj.register class Planet extends Stellar
153 | @interfaces: [$obj,Stellar]
154 | bigMass:yes
155 |
156 | $obj.register class Moon extends Stellar
157 | @interfaces: [$obj,Stellar]
158 | bigMass:yes
159 |
--------------------------------------------------------------------------------
/mod/nuu/sprites/gov/hks.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
149 |
--------------------------------------------------------------------------------
/mod/core/client/keyboard.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | $static 'Kbd', new EventEmitter
24 |
25 | Kbd.init = ->
26 | Kbd = @
27 | @help = {}
28 | @state = {}
29 | @mmap = {}
30 | @rmap = {}
31 | @up = {}
32 | @dn = {}
33 | window.addEventListener 'keyup', @onKeyUp.bind @
34 | window.addEventListener 'keydown', @onKeyDown.bind @
35 | null
36 |
37 | # source: mdn these keycodes should be available on all major platforms
38 | Kbd.workingKeycodes2018 = ["AltLeft","AltRight","ArrowDown","ArrowLeft","ArrowRight","ArrowUp","Backquote","Backslash","Backspace","BracketLeft","BracketRight","CapsLock","Comma","ContextMenu","ControlLeft","ControlRight","Convert","Copy","Cut","Delete","Digit0","Digit1","Digit2","Digit3","Digit4","Digit5","Digit6","Digit7","Digit8","Digit9","End","Enter","Equal","Escape","F1","F10","F11","F12","F13","F14","F15","F16","F17","F18","F19","F2","F20","F3","F4","F5","F6","F7","F8","F9","Find","Help","Home","Insert","IntlBackslash","KeyA","KeyB","KeyC","KeyD","KeyE","KeyF","KeyG","KeyH","KeyI","KeyJ","KeyK","KeyL","KeyM","KeyN","KeyO","KeyP","KeyQ","KeyR","KeyS","KeyT","KeyU","KeyV","KeyW","KeyX","KeyY","KeyZ","Minus","NonConvert","NumLock","Numpad0","Numpad1","Numpad2","Numpad3","Numpad4","Numpad5","Numpad6","Numpad7","Numpad8","Numpad9","NumpadAdd","NumpadDecimal","NumpadDivide","NumpadEnter","NumpadEqual","NumpadMultiply","NumpadSubtract","Open","OSLeft","OSRight","PageDown","PageUp","Paste","Pause","Period","PrintScreen","Props","Quote","ScrollLock","Select","Semicolon","ShiftLeft","ShiftRight","Slash","Space","Tab","Undo"]
39 |
40 | Kbd.macro = (name,key,d10,func)->
41 | NUU.settings.bind = {} unless NUU.settings.bind?
42 | key = NUU.settings.bind[name] || key
43 | console.log key, name, NUU.settings.bind[name]? if debug
44 | @macro[name] = func
45 | @bind key, name if key
46 | @d10[name] = d10
47 |
48 | Kbd.bind = (combo,macro,opt) ->
49 | opt = @macro[macro] unless opt?
50 | delete @rmap[combo]
51 | delete @help[combo]
52 | return console.log ':kbd', 'bind:opt:undefined', macro, key, combo, opt unless opt?
53 | opt = up: opt if typeof opt is 'function'
54 | key = combo.replace /^[cas]+/,''
55 | return console.log ':kbd', 'bind:key:unknown', macro, key, combo, opt if -1 is @workingKeycodes2018.indexOf key
56 | console.log ':kbd', 'bind', combo, opt if debug
57 | @up[macro] = opt.up if opt.up?
58 | @dn[macro] = opt.dn if opt.dn?
59 | @mmap[macro] = combo
60 | @rmap[combo] = macro
61 | @help[combo] = macro
62 | @state[key] = off
63 |
64 | Kbd.onKeyDown = (e) ->
65 | # allow some browser-wide shortcuts that would otherwise not work
66 | return if e.ctrlKey and e.code is 'KeyC' if isClient
67 | return if e.ctrlKey and e.code is 'KeyV' if isClient
68 | return if e.ctrlKey and e.code is 'KeyR' if isClient
69 | return if e.ctrlKey and e.code is 'KeyL' if isClient
70 | # allow the inspector; but only in debug mode ;)
71 | return if e.ctrlKey and e.shiftKey and e.code is 'KeyI' if debug
72 | code = e.code
73 | code = 'c' + code if e.ctrlKey
74 | code = 'a' + code if e.altKey
75 | code = 's' + code if e.shiftKey
76 | if @onkeydown
77 | return @onkeydown e, code
78 | return true if @onkeyup
79 | e.preventDefault() unless e.allowDefault
80 | macro = @rmap[code]
81 | notice 500, "d[#{code}]:#{macro} #{e.code}" if debug
82 | return if @state[code] is true
83 | @state[code] = true
84 | @dn[macro](e) if @dn[macro]?
85 |
86 | Kbd.onKeyUp = (e) ->
87 | e.preventDefault()
88 | code = e.code
89 | code = 'c' + code if e.ctrlKey
90 | code = 'a' + code if e.altKey
91 | code = 's' + code if e.shiftKey
92 | return @onkeyup e, code if @onkeyup
93 | macro = @rmap[code]
94 | notice 500, "u[#{code}]:#{macro}" if debug
95 | return if @state[code] is false
96 | @state[code] = false
97 | @up[macro](e) if @up[macro]?
98 |
99 | Kbd.stack = []
100 |
101 | Kbd.clearHooks = (key)->
102 | @focus = null
103 | document.removeEventListener 'paste', @onpaste if @onpaste
104 | delete @onpaste
105 | delete @onkeyup
106 | delete @onkeydown
107 | true
108 |
109 | Kbd.grab = (focus,opts)->
110 | # console.log ':kbd', 'grab', focus.name if debug
111 | if @focus
112 | if @focus is focus and opts.onkeydown is @onkeydown and opts.onkeyup is @onkeyup and opts.onpaste is @onpaste
113 | console.log ':kbd', 'same', @focus.name if debug
114 | else
115 | console.log ':kbd', 'obscure', @focus.name # if debug
116 | @stack.push focus:@focus, onkeydown:@onkeydown, onkeyup:@onkeyup, onpaste:@onpaste
117 | do @clearHooks
118 | else
119 | console.log ':kbd', 'grab', focus.name if debug
120 | @focus = focus
121 | Object.assign @, opts
122 | @focus.raise() if @focus.raise?
123 | document.addEventListener 'paste', @onpaste if @onpaste
124 | Mouse.disableTemp()
125 | # console.log ':kbd', 'grabbed', @focus.name if debug
126 | true
127 |
128 | Kbd.release = (focus)->
129 | if @focus is focus
130 | console.log ':kbd', 'release_current', focus.name if debug
131 | do @clearHooks
132 | if @stack.length is 0
133 | console.log ':kbd', 'main-focus' if debug
134 | Mouse.enableIfWasEnabled()
135 | return true
136 | item = @stack.pop()
137 | @grab item.focus, item
138 | console.log ':kbd', 'main' unless @focus if debug
139 | true
140 | else if @stack.length > 0 and item = @stack.reverse().reduce( (v,c=no)-> if v.focus is focus then v else c )
141 | console.log ':kbd', 'release_obscured', focus.name if debug
142 | Array.remove @stack, item
143 | console.log ':kbd', 'main' unless @focus if debug
144 | true
145 | else false
146 |
147 | Kbd.defaultMap =
148 | boost: 'sArrowUp'
149 | accel: 'ArrowUp'
150 | retro: 'ArrowDown'
151 | left: 'ArrowLeft'
152 | right: 'ArrowRight'
153 |
154 | Kbd.d10 =
155 | execute: "Execute something"
156 | accel: "Accelerate"
157 | retro: "Decelerate"
158 | left: "Turn left"
159 | right: "Turn right"
160 | autopilot: "Turn to target"
161 | escape: "Exit something"
162 | boost: "Boost"
163 |
164 | do Kbd.init
165 |
--------------------------------------------------------------------------------
/mod/nuu/client/target.coffee:
--------------------------------------------------------------------------------
1 | ###
2 |
3 | * c) 2007-2022 Sebastian Glaser
4 | * c) 2007-2022 flyc0r
5 |
6 | This file is part of NUU.
7 |
8 | NUU is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU General Public License as published by
10 | the Free Software Foundation, either version 3 of the License, or
11 | (at your option) any later version.
12 |
13 | NUU is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU General Public License for more details.
17 |
18 | You should have received a copy of the GNU General Public License
19 | along with NUU. If not, see .
20 |
21 | ###
22 |
23 | NUU.on 'target:new', (opts)->
24 | Target.widget()
25 |
26 | NUU.on '$obj:destroyed', (v)->
27 | delete Target.hostile[k] for k,i of Target.hostile when i.id is v.id
28 | Target.enemy() if Target.hostile.length > 0
29 | return
30 |
31 | NET.on 'hostile', addHostile = (id)->
32 | count = 1
33 | doAdd = (i)->
34 | return console.log ':tgt', 'hostile:unknown', i unless v = $obj.byId[i]
35 | return if v.destructing
36 | Target.hostile[v.id] = v
37 | NUU.emit 'hostile', i
38 | if Array.isArray id
39 | Array.empty Target.hostile
40 | id.map doAdd
41 | count = id.length
42 | else doAdd id
43 | Target.types[0] = Target.hostile
44 | NUU.emit 'hostiles', Target.hostile
45 | if Target.hostile.length is 0
46 | do Target.nothing
47 | else do Target.enemy
48 | return
49 |
50 | window.TARGET = null
51 |
52 | $public class Target
53 | @id: 0
54 | @class: 0
55 | @mode: 'land'
56 | @hostile: h = []
57 | @typeNames : ['off','hostile','ship','stellar','roid','all']
58 | @types : [[],h,Ship.byId,Stellar.byId,Asteroid.byId,$obj.byId]
59 |
60 | Target.widget = ->
61 | HUD.widget 'target', "#{Target.mode}", yes
62 |
63 | Target.set = (target,callback)->
64 | old.unref @ if old = TARGET
65 | unless window.TARGET = target
66 | NUU.emit 'target:new', null, old
67 | return
68 | target.ref @, (v)->
69 | Target.enemy()
70 | NUU.emit 'target:lost', v
71 | return
72 | unless ( actions = TARGET.actions ).includes oldMode = Target.mode
73 | Target.mode = TARGET.defaultAction?() || actions[0] || ''
74 | return console.log ':tgt', 'nx:ty' unless ty = Target.types
75 | return console.log ':tgt', 'nx:cl' unless cl = ty[Target.class]
76 | return console.log ':tgt', 'nx:ks' unless ks = Object.keys cl
77 | Target.id = if -1 is id = ks.indexOf '' + target.id then 0 else id
78 | callback? TARGET
79 | NUU.emit 'target:new', target, old
80 | return TARGET
81 |
82 | Target.mutate = (fnc)-> again = (callback,skipSelf=false)->
83 | return console.log ':tgt', 'nx:ty' unless ty = Target.types
84 | return console.log ':tgt', 'nx:cl' unless cl = ty[Target.class]
85 | return console.log ':tgt', 'nx:ks' unless ks = Object.keys cl
86 | ct = ks.length
87 | ix = if ( cu = TARGET || cl[ks[0]] || null ) then ks.indexOf '' + cu.id else 0
88 | id = ks[fnc ix, ct, cl, ks, skipSelf]
89 | ta = cl[id]
90 | if ( skipSelf is false ) and ( ta? and VEHICLE? ) and ( ta.id is VEHICLE.id )
91 | ta = again callback, VEHICLE.id, window.TARGET = ta
92 | Target.set ta, callback
93 |
94 | Target.prev = Target.mutate (ix,ct)-> ( ct + --ix ) % ct
95 | Target.next = Target.mutate (ix,ct)-> ++ix % ct
96 |
97 | Target.closest = Target.mutate (ix,ct,cl,ks,skipSelf) ->
98 | return console.log ':tgt', 'nx:v' unless v = VEHICLE
99 | dist = Infinity; closest = null
100 | for k,t of cl when t and t.id isnt v.id and (d = $dist v, t) < dist
101 | continue if t.destructing
102 | continue if t.id is skipSelf
103 | dist = d; closest = t
104 | return if closest? then ks.indexOf '' + closest.id else 0
105 |
106 | Target.nothing = ->
107 | Target.class = 0
108 | Target.set null
109 |
110 | Target.enemy = ->
111 | return if TARGET and Target.hostile[TARGET.id]
112 | Target.class = 1 # hostile
113 | do Target.closest
114 |
115 | Target.nextClass = ->
116 | ct = ( list = Target.types ).length
117 | Target.class = ++Target.class % ct
118 | do Target.closest
119 | do Target.nextClass if Target.class is 1 and VEHICLE.hostile.length is 0
120 | return
121 |
122 | Target.prevClass = ->
123 | ct = ( list = Target.types ).length
124 | Target.class = ( ct + --Target.class ) % ct
125 | do Target.closest
126 | do Target.prevClass if Target.class is 1 and VEHICLE.hostile.length is 0
127 | return
128 |
129 | Target.toggleMode = ->
130 | li = TARGET.actions || ['n/a']
131 | le = li.length
132 | ci = li.indexOf Target.mode
133 | Target.mode = if ci is -1 then ( TARGET.defaultAction?() || li[0] ) else li[++ci%le]
134 | Target.widget()
135 | return
136 |
137 | Target.eva = ->
138 | NET.action.write 0, 'eva'
139 | return
140 |
141 | Target.launch = ->
142 | NET.action.write 0, 'launch'
143 | return
144 |
145 | Target.orbit = ->
146 | return unless t = TARGET
147 | NET.action.write t, Target.mode
148 | return
149 |
150 | Target.jump = ->
151 | return unless t = TARGET
152 | NET.json.write jump: t.id
153 | return
154 |
155 | Target.capture = capture = ->
156 | return unless t = TARGET
157 | NET.action.write t, 'capture'
158 | return
159 |
160 | Target.roid = ->
161 | Target.class = 4
162 | do Target.closest
163 |
164 | Target.captureClosest = ->
165 | Target.class = 5 # all
166 | Target.closest (t)-> capture t if t?
167 | return
168 |
169 | Target.prompt = ->
170 | vt.prompt "target#", (
171 | (seek)->
172 | return if seek.trim() is ''
173 | seek = seek.toLowerCase()
174 | t = Object
175 | .keys($obj.byName)
176 | .filter (i)-> null != i.toLowerCase().match seek
177 | .map (i)-> $obj.byName[i]
178 | Target.set t[0] if t[0]
179 | ), yes
180 |
181 | Kbd.macro 'targetSearch', 'KeyG', 'Target search', Target.prompt
182 | Kbd.macro 'targetNothing', 'sKeyW', 'Target nothing', Target.nothing
183 | Kbd.macro 'targetNext', 'KeyD', 'Target next', Target.next
184 | Kbd.macro 'targetClassNext', 'KeyW', 'Target next class', Target.nextClass
185 | Kbd.macro 'targetPrev', 'KeyA', 'Target prev', Target.prev
186 | Kbd.macro 'targetClassPrev', 'KeyS', 'Target prev class', Target.prevClass
187 | Kbd.macro 'targetClosest', 'KeyU', 'Target closest target', Target.closest
188 | Kbd.macro 'targetEnemy', 'KeyE', 'Target closest enemy', Target.enemy
189 | Kbd.macro 'targetRoid', 'KeyR', 'Target closest asteroid', Target.roid
190 | Kbd.macro 'targetMode', 'KeyQ', 'Toggle Land/Dock/Orbit', Target.toggleMode
191 | Kbd.macro 'orbit', 'Tab', 'Land / Dock / Enter Orbit', Target.orbit
192 | Kbd.macro 'launch', 'aKeyQ', 'Launch / Undock', Target.launch
193 | Kbd.macro 'eva', 'saKeyQ', 'EVA', Target.eva
194 | Kbd.macro 'jump', 'KeyJ', 'Jump to target', Target.jump
195 | Kbd.macro 'capture', 'sKeyC', 'Capture target', Target.capture
196 | Kbd.macro 'captureClosest', 'KeyC', 'Capture closest', Target.captureClosest
197 |
--------------------------------------------------------------------------------