Compare commits
8 Commits
master
...
graphql-ui
Author | SHA1 | Date | |
---|---|---|---|
5d71a1b19b | |||
c17bb904af | |||
f982af16c2 | |||
7568e38575 | |||
3aedc4a8ff | |||
bb5e57e8b2 | |||
6bc31f3079 | |||
4cae521faf |
2
Makefile
2
Makefile
|
@ -7,8 +7,6 @@ run: build
|
||||||
./$(NAME)
|
./$(NAME)
|
||||||
build:
|
build:
|
||||||
crystal build src/$(NAME).cr --stats --error-trace -Dentitas_enable_logging
|
crystal build src/$(NAME).cr --stats --error-trace -Dentitas_enable_logging
|
||||||
build_entitas_logging:
|
|
||||||
crystal build src/$(NAME).cr --stats --error-trace -Dentitas_enable_logging
|
|
||||||
debug:
|
debug:
|
||||||
crystal build src/$(NAME).cr --stats --error-trace -Dentitas_enable_logging Dentitas_debug_generator
|
crystal build src/$(NAME).cr --stats --error-trace -Dentitas_enable_logging Dentitas_debug_generator
|
||||||
# I did not enabled --debug because it crashes.
|
# I did not enabled --debug because it crashes.
|
||||||
|
|
|
@ -16,8 +16,8 @@ Install `git`, `sfml`, `crystal`, `make`, `imgui` (`imgui-sfml` with archlinux).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
export LD_LIBRARY_PATH="$(pwd)/lib/imgui-sfml"
|
|
||||||
./core # run the server (high performance for data)
|
./core # run the server (high performance for data)
|
||||||
|
xdg-open https://localhost:3000 # open the game UI (browser UI for simplicity)
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ Install `git`, `sfml`, `crystal`, `make`, `imgui` (`imgui-sfml` with archlinux).
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
- [Arthur Poulet](https://git.sceptique.eu/Sceptique) - creator and maintainer
|
- [Arthur Poulet](https://git.sceptique.eu/Sceptique) - creator and maintainer.
|
||||||
|
|
||||||
## Particular mentions
|
## Particular mentions
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ items:
|
||||||
c_plant:
|
c_plant:
|
||||||
<<: *template_default
|
<<: *template_default
|
||||||
title: Chemical plant
|
title: Chemical plant
|
||||||
description: Transformation machines that can produce almost any kind of material via chemistery, such as super-acides, rare gaz, etc. using lots of basic materials.
|
description: Series of chemical transformation machines.
|
||||||
consumes:
|
consumes:
|
||||||
mineral:
|
mineral:
|
||||||
function: linear
|
function: linear
|
||||||
|
@ -84,7 +84,7 @@ items:
|
||||||
l_plant:
|
l_plant:
|
||||||
<<: *template_default
|
<<: *template_default
|
||||||
title: Logistic assembly
|
title: Logistic assembly
|
||||||
description: An assembly center site that make trunks, trains, planes, and civilan space transports. It also provide a better transportation network.
|
description: An assembly center site that make trunks, trains, planes, and civilan space transports.
|
||||||
consumes:
|
consumes:
|
||||||
alloy:
|
alloy:
|
||||||
function: linear
|
function: linear
|
||||||
|
|
|
@ -36,7 +36,7 @@ items:
|
||||||
e_plant:
|
e_plant:
|
||||||
<<: *template_default
|
<<: *template_default
|
||||||
title: Energy plant
|
title: Energy plant
|
||||||
description: Energy grid that produce energy using nuclear isotops, sun, wind, and fossil fuel, and also can fill energy storage means with chemistery.
|
description: Energy grid that produce energy using nuclear isotops, sun, wind, and fossil fuel.
|
||||||
prods:
|
prods:
|
||||||
energy:
|
energy:
|
||||||
function: linear
|
function: linear
|
||||||
|
@ -51,7 +51,7 @@ items:
|
||||||
mine:
|
mine:
|
||||||
<<: *template_default
|
<<: *template_default
|
||||||
title: Mines
|
title: Mines
|
||||||
description: Mines are a polluting industry that can generate tons of basic ressources required for most of the industry. Network of mines generate it's own cheminals.
|
description: Mines are polluting industry to generate tons of basic ressources required for most of the industry.
|
||||||
consumes:
|
consumes:
|
||||||
energy:
|
energy:
|
||||||
function: linear
|
function: linear
|
||||||
|
@ -62,20 +62,16 @@ items:
|
||||||
function: linear
|
function: linear
|
||||||
coefs:
|
coefs:
|
||||||
a: 10
|
a: 10
|
||||||
chemical:
|
|
||||||
function: linear
|
|
||||||
coefs:
|
|
||||||
a: 0.01
|
|
||||||
wastes:
|
wastes:
|
||||||
pollution:
|
pollution:
|
||||||
function: linear
|
function: linear
|
||||||
coefs:
|
coefs:
|
||||||
a: 1
|
a: 1
|
||||||
|
|
||||||
agrifood:
|
farm:
|
||||||
<<: *template_default
|
<<: *template_default
|
||||||
title: Farmed lands
|
title: Farmed lands
|
||||||
description: Large portions of the land space is used to produce nutrient. It growth crops, animal farms, etc. using standard mechanized tools and vehicules. Food can then be distributed via a dense network of distributors.
|
description: Large portions of the land space is used to produce nutrient. It growth crops, animal farms, etc. using standard mechanized tools and vehicules.
|
||||||
consumes:
|
consumes:
|
||||||
mineral:
|
mineral:
|
||||||
function: linear
|
function: linear
|
||||||
|
|
|
@ -29,7 +29,7 @@ templates:
|
||||||
max:
|
max:
|
||||||
function: linear
|
function: linear
|
||||||
coefs:
|
coefs:
|
||||||
b: 40_000
|
b: 10_000
|
||||||
|
|
||||||
items:
|
items:
|
||||||
|
|
||||||
|
@ -51,12 +51,12 @@ items:
|
||||||
mineral:
|
mineral:
|
||||||
function: linear
|
function: linear
|
||||||
coefs:
|
coefs:
|
||||||
a: 10000
|
a: 10000000
|
||||||
|
|
||||||
f_store:
|
f_store:
|
||||||
<<: *template_default
|
<<: *template_default
|
||||||
title: Food storage
|
title: Food storage
|
||||||
description: Protective centers for nutrients and food supplies.
|
description: Protective centers for nutrients and food supplies
|
||||||
consumes:
|
consumes:
|
||||||
energy:
|
energy:
|
||||||
function: linear
|
function: linear
|
||||||
|
|
|
@ -1,35 +1,35 @@
|
||||||
energy:
|
energy:
|
||||||
name: Energy
|
name: Energy
|
||||||
description: Represents how many machines we can move at the same time. Energy unit represents both the energy immediate usage and storage by technological means.
|
description: Represents how many machines we can move at the same time.
|
||||||
food:
|
food:
|
||||||
name: Food
|
name: Food
|
||||||
description: Standard food with low technological ehancements, along with means and services to distribute it properly.
|
description: Standard food with low technological ehancements
|
||||||
food2:
|
food2:
|
||||||
name: Synthetic Food
|
name: Synthetic Food
|
||||||
description: Artificialy engineered very efficient food and distribution.
|
description: Artificialy engineered very efficient food
|
||||||
mineral:
|
mineral:
|
||||||
name: Simple Minerals in a proper concentration.
|
name: Simple Minerals
|
||||||
minerals2:
|
minerals2:
|
||||||
name: Rare Minerals.
|
name: Rare Minerals
|
||||||
chemical:
|
chemical:
|
||||||
name: Chemicals
|
name: Chemicals
|
||||||
description: Materials usually extracted from minerals with chemistery, with the infrastructure for safe storage and exploitation.
|
description: Usually extracted from minerals with chemistery
|
||||||
require: mineral
|
require: mineral
|
||||||
alloy:
|
alloy:
|
||||||
name: Alloys
|
name: Alloys
|
||||||
description: Standards alloys for most industrial and military usages.
|
description: Standard alloys
|
||||||
require: mineral
|
require: mineral
|
||||||
alloy2:
|
alloy2:
|
||||||
name: Super Materials
|
name: Super Materials
|
||||||
description: Super alloys with near-magical properties compared to modern age.
|
description: Super alloys with near-magical properties
|
||||||
require: mineral
|
require: mineral
|
||||||
weapon:
|
weapon:
|
||||||
name: Weaponery
|
name: Weaponery
|
||||||
description: Bombs, guns, bullets, armors, utilitary, tanks, fighters, artillery, all the things that are small enough to fit in a carrier.
|
description: Bombs, guns, bullets, armors, utilitary, tanks, fighters
|
||||||
require: alloy
|
require: alloy
|
||||||
logistic:
|
logistic:
|
||||||
name: Logistic
|
name: Logistic
|
||||||
description: Civilan ships and means of transportations for high volume fret and travellers.
|
description: Civilan ships for fret and travellers
|
||||||
pollution:
|
pollution:
|
||||||
name: Pollution
|
name: Pollution
|
||||||
description: Represent the wastes of industrial activities, that is stored on the planet more or less efficiently and have a negative impact on productivity and life.
|
description: Represent the wastes of industrial activities, that is stored on the planet more or less randomly.
|
||||||
|
|
|
@ -36,7 +36,7 @@ Lalande
|
||||||
Lampadas
|
Lampadas
|
||||||
Lankiveil
|
Lankiveil
|
||||||
Lernaeus
|
Lernaeus
|
||||||
Luyten
|
Luyten's Star
|
||||||
Mu Arae
|
Mu Arae
|
||||||
Muritan
|
Muritan
|
||||||
Naraj
|
Naraj
|
||||||
|
@ -53,11 +53,11 @@ Selusa
|
||||||
Sikun
|
Sikun
|
||||||
Sun
|
Sun
|
||||||
Synchrony
|
Synchrony
|
||||||
|
Trappist
|
||||||
Tau Ceti
|
Tau Ceti
|
||||||
Tauri
|
Tauri
|
||||||
Tegeuse
|
Tegeuse
|
||||||
Tleilax
|
Tleilax
|
||||||
Trappist
|
|
||||||
Tupile
|
Tupile
|
||||||
Upsilon Andromedae
|
Upsilon Andromedae
|
||||||
Ursae Majoris
|
Ursae Majoris
|
||||||
|
@ -65,3 +65,4 @@ Virginis
|
||||||
Wasp
|
Wasp
|
||||||
Xo
|
Xo
|
||||||
Yz Ceti
|
Yz Ceti
|
||||||
|
c
|
||||||
|
|
5
graphql/base.graphql
Normal file
5
graphql/base.graphql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
type Query {
|
||||||
|
empires(name: String!): String!
|
||||||
|
|
||||||
|
day(): Int!
|
||||||
|
}
|
26
shard.lock
26
shard.lock
|
@ -1,20 +1,28 @@
|
||||||
version: 2.0
|
version: 2.0
|
||||||
shards:
|
shards:
|
||||||
crsfml:
|
backtracer:
|
||||||
git: https://github.com/oprypin/crsfml.git
|
git: https://github.com/sija/backtracer.cr.git
|
||||||
version: 2.5.2
|
version: 1.2.1
|
||||||
|
|
||||||
entitas:
|
entitas:
|
||||||
git: https://github.com/spoved/entitas.cr.git
|
git: https://github.com/spoved/entitas.cr.git
|
||||||
version: 1.4.5
|
version: 1.4.5
|
||||||
|
|
||||||
imgui:
|
exception_page:
|
||||||
git: https://github.com/oprypin/crystal-imgui.git
|
git: https://github.com/crystal-loot/exception_page.git
|
||||||
version: 1.87
|
version: 0.2.2
|
||||||
|
|
||||||
imgui-sfml:
|
graphql:
|
||||||
git: https://github.com/oprypin/crystal-imgui-sfml.git
|
git: https://github.com/graphql-crystal/graphql.git
|
||||||
version: 1.87
|
version: 0.4.0
|
||||||
|
|
||||||
|
kemal:
|
||||||
|
git: https://github.com/kemalcr/kemal.git
|
||||||
|
version: 1.2.0
|
||||||
|
|
||||||
|
radix:
|
||||||
|
git: https://github.com/luislavena/radix.git
|
||||||
|
version: 0.4.1
|
||||||
|
|
||||||
spoved:
|
spoved:
|
||||||
git: https://github.com/spoved/spoved.cr.git
|
git: https://github.com/spoved/spoved.cr.git
|
||||||
|
|
10
shard.yml
10
shard.yml
|
@ -15,9 +15,7 @@ license: GPLv3
|
||||||
dependencies:
|
dependencies:
|
||||||
entitas:
|
entitas:
|
||||||
github: spoved/entitas.cr
|
github: spoved/entitas.cr
|
||||||
crsfml:
|
graphql:
|
||||||
github: oprypin/crsfml
|
github: graphql-crystal/graphql
|
||||||
imgui:
|
kemal:
|
||||||
github: oprypin/crystal-imgui
|
github: kemalcr/kemal
|
||||||
imgui-sfml:
|
|
||||||
github: oprypin/crystal-imgui-sfml
|
|
||||||
|
|
4
src/api.cr
Normal file
4
src/api.cr
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
module TETU::API
|
||||||
|
end
|
||||||
|
|
||||||
|
require "./api/*"
|
21
src/api/game_query.cr
Normal file
21
src/api/game_query.cr
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
require "./objects"
|
||||||
|
|
||||||
|
module TETU::API::Definitions
|
||||||
|
@[GraphQL::Object]
|
||||||
|
class GameQuery < GraphQL::BaseQuery
|
||||||
|
@[GraphQL::Field]
|
||||||
|
def hello(name : String) : String
|
||||||
|
"Hello, #{name}!"
|
||||||
|
end
|
||||||
|
|
||||||
|
@[GraphQL::Field]
|
||||||
|
def empire : Empire
|
||||||
|
Empire.new
|
||||||
|
end
|
||||||
|
|
||||||
|
@[GraphQL::Field]
|
||||||
|
def planet(id : ID? = nil) : Planet
|
||||||
|
Planet.new(id: id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
50
src/api/http_server.cr
Normal file
50
src/api/http_server.cr
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
class TETU::API::HttpServer
|
||||||
|
def self.start
|
||||||
|
spawn do
|
||||||
|
Kemal.run do |config|
|
||||||
|
server = config.server.not_nil!
|
||||||
|
server.bind_tcp(
|
||||||
|
host: ENV.fetch("HOST", "127.0.0.1"),
|
||||||
|
port: ENV.fetch("PORT", "3000").to_i,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require "./game_query"
|
||||||
|
|
||||||
|
schema = GraphQL::Schema.new(TETU::API::Definitions::GameQuery.new)
|
||||||
|
|
||||||
|
|
||||||
|
# class CustomHandler < Kemal::Handler
|
||||||
|
# only ["/graphql"], "POST"
|
||||||
|
|
||||||
|
# def call(context)
|
||||||
|
# puts "Doing some custom stuff here"
|
||||||
|
# spawn do
|
||||||
|
# puts "Async stuff finished now"
|
||||||
|
# call_next context
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# add_handler CustomHandler.new
|
||||||
|
|
||||||
|
module TETU::API
|
||||||
|
PLAYER_CHANNEL = Channel(Tuple(String, Channel(String))).new
|
||||||
|
RESPONSE_CHANNEL = Channel(String).new
|
||||||
|
end
|
||||||
|
|
||||||
|
post "/graphql" do |env|
|
||||||
|
env.response.content_type = "application/json"
|
||||||
|
|
||||||
|
query = env.params.json["query"].as(String)
|
||||||
|
variables = env.params.json["variables"]?.as(Hash(String, JSON::Any)?)
|
||||||
|
operation_name = env.params.json["operationName"]?.as(String?)
|
||||||
|
context = TETU::API::Definitions::PlayerContext.new(TETU::API::PLAYER_CHANNEL)
|
||||||
|
schema.execute(query, variables, operation_name, context)
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/" do |env|
|
||||||
|
"<html></html>"
|
||||||
|
end
|
3
src/api/objects.cr
Normal file
3
src/api/objects.cr
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
require "./objects/base"
|
||||||
|
require "./objects/planet"
|
||||||
|
require "./objects/all"
|
48
src/api/objects/all.cr
Normal file
48
src/api/objects/all.cr
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
require "./base"
|
||||||
|
require "./planet"
|
||||||
|
|
||||||
|
module TETU::API::Definitions
|
||||||
|
@[GraphQL::Object]
|
||||||
|
class Star < GraphQL::BaseObject
|
||||||
|
@@i : ID = 0
|
||||||
|
|
||||||
|
def initialize(id : Int32? = nil)
|
||||||
|
if id.nil?
|
||||||
|
@id = @@i
|
||||||
|
@@i += 1
|
||||||
|
else
|
||||||
|
@id = id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@[GraphQL::Field]
|
||||||
|
getter id : ID
|
||||||
|
|
||||||
|
@[GraphQL::Field]
|
||||||
|
def name : String
|
||||||
|
"my #{@id}th star name"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module TETU::API::Definitions
|
||||||
|
@[GraphQL::Object]
|
||||||
|
class Empire < GraphQL::BaseObject
|
||||||
|
@[GraphQL::Field]
|
||||||
|
def name : String
|
||||||
|
"Player"
|
||||||
|
end
|
||||||
|
|
||||||
|
@[GraphQL::Field]
|
||||||
|
def stars(context) : Array(Star)
|
||||||
|
context.player_channel.send({ "stars", TETU::API::RESPONSE_CHANNEL })
|
||||||
|
answer = TETU::API::RESPONSE_CHANNEL.receive
|
||||||
|
answer.split(",").map { |name| Star.new }
|
||||||
|
end
|
||||||
|
|
||||||
|
@[GraphQL::Field]
|
||||||
|
def planets : Array(Planet)
|
||||||
|
[] of Planet
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
src/api/objects/base.cr
Normal file
8
src/api/objects/base.cr
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module TETU::API::Definitions
|
||||||
|
alias ID = Int32
|
||||||
|
|
||||||
|
class PlayerContext < GraphQL::Context
|
||||||
|
def initialize(@player_channel : Channel(Tuple(String, Channel(String))))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
src/api/objects/planet.cr
Normal file
23
src/api/objects/planet.cr
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
module TETU::API::Definitions
|
||||||
|
@[GraphQL::Object]
|
||||||
|
class Planet < GraphQL::BaseObject
|
||||||
|
@@i : ID = 0
|
||||||
|
|
||||||
|
def initialize(id : Int32? = nil)
|
||||||
|
if id.nil?
|
||||||
|
@id = @@i
|
||||||
|
@@i += 1
|
||||||
|
else
|
||||||
|
@id = id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@[GraphQL::Field]
|
||||||
|
getter id : ID
|
||||||
|
|
||||||
|
@[GraphQL::Field]
|
||||||
|
def name : String
|
||||||
|
"my #{@id}th planet name"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -18,6 +18,79 @@ class TETU::Named < Entitas::Component
|
||||||
end
|
end
|
||||||
|
|
||||||
require "./game/resources"
|
require "./game/resources"
|
||||||
require "./game/infrastructure"
|
|
||||||
|
# TODO: why is it not a component ????? should fix that
|
||||||
|
class TETU::InfrastructureUpgrade
|
||||||
|
spoved_logger level: :info, io: STDOUT, bind: true
|
||||||
|
alias Costs = Hash(Resources::Name, Float64)
|
||||||
|
property id : String
|
||||||
|
property costs_by_tick : Costs
|
||||||
|
property costs_start : Costs
|
||||||
|
property end_tick : TETU::Tick
|
||||||
|
property current_tick : TETU::Tick
|
||||||
|
@finished = false
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"#{@id} (#{@current_tick}/#{@end_tick})"
|
||||||
|
end
|
||||||
|
|
||||||
|
def finished?
|
||||||
|
@finished
|
||||||
|
end
|
||||||
|
|
||||||
|
def finish!
|
||||||
|
@finished = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(@id, @costs_by_tick, @costs_start, @end_tick, @current_tick = 0i64)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_infrastructure(id : String, tier : Int32)
|
||||||
|
# TODO: must read the properties of the blueprints to define the costs
|
||||||
|
free_instant(id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.free_instant(id : String)
|
||||||
|
new(
|
||||||
|
id: id,
|
||||||
|
costs_by_tick: Costs.new,
|
||||||
|
costs_start: Costs.new,
|
||||||
|
end_tick: 0i64,
|
||||||
|
current_tick: 0i64,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
spoved_logger level: :info, io: STDOUT, bind: true
|
||||||
|
|
||||||
|
def self.from_blueprint(infra_id : String, tier : Number)
|
||||||
|
blueprint = Helpers::InfrastructuresFileLoader.all[infra_id]
|
||||||
|
total_costs = blueprint.build.costs.transform_values { |f| f.execute(tier) }
|
||||||
|
upfront_costs = total_costs.transform_values { |v| v * blueprint.build.upfront }
|
||||||
|
duration = blueprint.build.duration.execute(tier)
|
||||||
|
tick_costs = total_costs.transform_values { |v| v * (1.0 - blueprint.build.upfront) / duration }
|
||||||
|
logger.debug { "" }
|
||||||
|
logger.debug { "> Create from blueprint" }
|
||||||
|
upgrade = new(
|
||||||
|
id: infra_id,
|
||||||
|
costs_by_tick: tick_costs,
|
||||||
|
costs_start: upfront_costs,
|
||||||
|
end_tick: duration.to_i64,
|
||||||
|
current_tick: 0i64,
|
||||||
|
)
|
||||||
|
logger.debug { {blueprint: blueprint} }
|
||||||
|
logger.debug { {upgrade: upgrade} }
|
||||||
|
logger.debug { "" }
|
||||||
|
upgrade
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@[Context(Game)]
|
||||||
|
class TETU::InfrastructureUpgrades < Entitas::Component
|
||||||
|
prop :upgrades, Array(InfrastructureUpgrade), default: Array(InfrastructureUpgrade).new
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
"InfrastructureUpgrades: #{upgrades.map(&.to_s)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
require "./game/*"
|
require "./game/*"
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
# TODO: why is it not a component ????? should fix that
|
|
||||||
class TETU::InfrastructureUpgrade
|
|
||||||
spoved_logger level: :info, io: STDOUT, bind: true
|
|
||||||
alias Costs = Hash(Resources::Name, Float64)
|
|
||||||
property id : String
|
|
||||||
property costs_by_tick : Costs
|
|
||||||
property costs_start : Costs
|
|
||||||
property end_tick : TETU::Tick
|
|
||||||
property current_tick : TETU::Tick
|
|
||||||
@finished = false
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
"#{@id} (#{@current_tick}/#{@end_tick})"
|
|
||||||
end
|
|
||||||
|
|
||||||
def finished?
|
|
||||||
@finished
|
|
||||||
end
|
|
||||||
|
|
||||||
def finish!
|
|
||||||
@finished = true
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(@id, @costs_by_tick, @costs_start, @end_tick, @current_tick = 0i64)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.from_infrastructure(id : String, tier : Int32)
|
|
||||||
# TODO: must read the properties of the blueprints to define the costs
|
|
||||||
free_instant(id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.free_instant(id : String)
|
|
||||||
new(
|
|
||||||
id: id,
|
|
||||||
costs_by_tick: Costs.new,
|
|
||||||
costs_start: Costs.new,
|
|
||||||
end_tick: 0i64,
|
|
||||||
current_tick: 0i64,
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
spoved_logger level: :info, io: STDOUT, bind: true
|
|
||||||
|
|
||||||
def self.from_blueprint(infra_id : String, tier : Number)
|
|
||||||
blueprint = Helpers::InfrastructuresFileLoader.all[infra_id]
|
|
||||||
total_costs = blueprint.build.costs.transform_values { |f| f.execute(tier) }
|
|
||||||
upfront_costs = total_costs.transform_values { |v| v * blueprint.build.upfront }
|
|
||||||
duration = blueprint.build.duration.execute(tier)
|
|
||||||
tick_costs = total_costs.transform_values { |v| v * (1.0 - blueprint.build.upfront) / duration }
|
|
||||||
logger.debug { "" }
|
|
||||||
logger.debug { "> Create from blueprint" }
|
|
||||||
upgrade = new(
|
|
||||||
id: infra_id,
|
|
||||||
costs_by_tick: tick_costs,
|
|
||||||
costs_start: upfront_costs,
|
|
||||||
end_tick: duration.to_i64,
|
|
||||||
current_tick: 0i64,
|
|
||||||
)
|
|
||||||
logger.debug { {blueprint: blueprint} }
|
|
||||||
logger.debug { {upgrade: upgrade} }
|
|
||||||
logger.debug { "" }
|
|
||||||
upgrade
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@[Context(Game)]
|
|
||||||
class TETU::InfrastructureUpgrades < Entitas::Component
|
|
||||||
prop :upgrades, Array(InfrastructureUpgrade), default: Array(InfrastructureUpgrade).new
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
"InfrastructureUpgrades: #{upgrades.map(&.to_s)}"
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +1,15 @@
|
||||||
@[Context(Game)]
|
@[Context(Game)]
|
||||||
class TETU::Population < Entitas::Component
|
class TETU::Population < Entitas::Component
|
||||||
alias Food = Hash(String, Float64)
|
|
||||||
DEFAULT_FOOD = { "food" => 1.0/100.0.millions }
|
|
||||||
|
|
||||||
prop :amount, Float64, default: 0.0
|
prop :amount, Float64, default: 0.0
|
||||||
prop :foods, Food, default: DEFAULT_FOOD
|
|
||||||
|
|
||||||
MIN_RANDOM_POP = 10.0.millions
|
MIN_RANDOM_POP = 10_000.0
|
||||||
MAX_RANDOM_POP = 10.0.billions
|
MAX_RANDOM_POP = 10_000_000_000.0
|
||||||
|
|
||||||
def self.generate_for(entity)
|
def self.generate(entity)
|
||||||
entity.add_population amount: (MIN_RANDOM_POP..MAX_RANDOM_POP).sample.round
|
entity.add_population amount: (MIN_RANDOM_POP..MAX_RANDOM_POP).sample.round
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s(round : Int32 = 2)
|
def to_s
|
||||||
Helpers::Numbers.humanize(number: @amount, round: round)
|
Helpers::Numbers.humanize(number: @amount, round: 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,10 +27,6 @@ class TETU::Resources < Entitas::Component
|
||||||
def humanize(sep = "\n")
|
def humanize(sep = "\n")
|
||||||
map { |k, store| "#{k}: #{store.humanize}" }.join(sep)
|
map { |k, store| "#{k}: #{store.humanize}" }.join(sep)
|
||||||
end
|
end
|
||||||
|
|
||||||
def amount_hash
|
|
||||||
transform_values { |store| store.amount }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# alias Prod = Tuple(Name, Float64)
|
# alias Prod = Tuple(Name, Float64)
|
||||||
|
@ -97,7 +93,7 @@ class TETU::Resources < Entitas::Component
|
||||||
|
|
||||||
def self.default
|
def self.default
|
||||||
stores = Stores.new
|
stores = Stores.new
|
||||||
stores["pollution"] = Store.new(amount: 0.0, max: 1.0.millions)
|
stores["pollution"] = Store.new(amount: 0.0, max: 1_000_000.0)
|
||||||
|
|
||||||
infras = Infras.new
|
infras = Infras.new
|
||||||
|
|
||||||
|
|
33
src/core.cr
33
src/core.cr
|
@ -1,24 +1,20 @@
|
||||||
# require "log"
|
|
||||||
require "yaml"
|
require "yaml"
|
||||||
|
|
||||||
require "entitas"
|
require "entitas"
|
||||||
require "crsfml"
|
require "graphql"
|
||||||
require "imgui"
|
|
||||||
require "imgui-sfml"
|
|
||||||
require "spoved/logger"
|
require "spoved/logger"
|
||||||
|
require "kemal"
|
||||||
|
|
||||||
module TETU
|
module TETU
|
||||||
spoved_logger level: :info, io: STDOUT, bind: true
|
spoved_logger level: :info, io: STDOUT, bind: true
|
||||||
|
|
||||||
# TO BE USED
|
# TODO: TO BE USED
|
||||||
module Systems
|
module Systems
|
||||||
# Log = TETU::Log.for(self)
|
|
||||||
spoved_logger level: :info, io: STDOUT, bind: true
|
spoved_logger level: :info, io: STDOUT, bind: true
|
||||||
end
|
end
|
||||||
|
|
||||||
# TO BE USED
|
# TODO: TO BE USED
|
||||||
module Components
|
module Components
|
||||||
# Log = TETU::Log.for(self)
|
|
||||||
spoved_logger level: :info, io: STDOUT, bind: true
|
spoved_logger level: :info, io: STDOUT, bind: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,9 +24,9 @@ end
|
||||||
|
|
||||||
require "./helpers/*"
|
require "./helpers/*"
|
||||||
require "./core/*"
|
require "./core/*"
|
||||||
require "./ui_service"
|
|
||||||
require "./components"
|
require "./components"
|
||||||
require "./systems"
|
require "./systems"
|
||||||
|
require "./api"
|
||||||
|
|
||||||
class TETU::EconomicSystems < Entitas::Feature
|
class TETU::EconomicSystems < Entitas::Feature
|
||||||
def initialize(contexts : Contexts)
|
def initialize(contexts : Contexts)
|
||||||
|
@ -42,22 +38,16 @@ class TETU::EconomicSystems < Entitas::Feature
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TETU::UiSystems < Entitas::Feature
|
class TETU::PlayerSystems < Entitas::Feature
|
||||||
def initialize(contexts : Contexts)
|
def initialize(contexts : Contexts)
|
||||||
@name = "UI Systems"
|
@name = "Player Systems"
|
||||||
|
add RequestHandlerSystem.new(contexts, player_channel)
|
||||||
add UiInitSystem.new(contexts)
|
|
||||||
add UiBackgroundSystem.new(contexts)
|
|
||||||
add UiEmpireSystem.new(contexts)
|
|
||||||
add UiPlanetSystem.new(contexts)
|
|
||||||
add UiDrawSystem.new(contexts) # keep at the end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class TETU::TimeSystems < Entitas::Feature
|
class TETU::TimeSystems < Entitas::Feature
|
||||||
def initialize(contexts : Contexts)
|
def initialize(contexts : Contexts)
|
||||||
@name = "Time Systems"
|
@name = "Time Systems"
|
||||||
|
|
||||||
add TimeSystem.new(contexts)
|
add TimeSystem.new(contexts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -68,12 +58,12 @@ class TETU::MainWorld
|
||||||
def start
|
def start
|
||||||
# get a reference to the contexts
|
# get a reference to the contexts
|
||||||
contexts = Contexts.shared_instance
|
contexts = Contexts.shared_instance
|
||||||
|
|
||||||
# create the systems by creating individual features
|
# create the systems by creating individual features
|
||||||
@systems = Entitas::Feature.new("systems")
|
@systems = Entitas::Feature.new("systems")
|
||||||
.add(TimeSystems.new(contexts))
|
.add(TimeSystems.new(contexts))
|
||||||
.add(EconomicSystems.new(contexts))
|
.add(EconomicSystems.new(contexts))
|
||||||
.add(UiSystems.new(contexts))
|
.add(PlayerSystems.new(contexts))
|
||||||
|
# .add(UiSystems.new(contexts))
|
||||||
@systems.init
|
@systems.init
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -81,7 +71,6 @@ class TETU::MainWorld
|
||||||
# call execute on all the ExecuteSystems and
|
# call execute on all the ExecuteSystems and
|
||||||
# ReactiveSystems that were triggered last frame
|
# ReactiveSystems that were triggered last frame
|
||||||
@systems.execute
|
@systems.execute
|
||||||
|
|
||||||
# call cleanup on all the CleanupSystems
|
# call cleanup on all the CleanupSystems
|
||||||
@systems.cleanup
|
@systems.cleanup
|
||||||
end
|
end
|
||||||
|
@ -115,9 +104,11 @@ module TETU
|
||||||
t2 = Time.local
|
t2 = Time.local
|
||||||
logger.debug { "Duration: #{t2 - t1}" }
|
logger.debug { "Duration: #{t2 - t1}" }
|
||||||
logger.debug { "" }
|
logger.debug { "" }
|
||||||
|
sleep 0.1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
TETU::API::HttpServer.start
|
||||||
TETU.main_loop
|
TETU.main_loop
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
require "./configuration"
|
require "./configuration"
|
||||||
|
|
||||||
class TETU::Window
|
# class TETU::Window
|
||||||
@@instance = Window.new
|
# @@instance = Window.new
|
||||||
|
|
||||||
def self.instance
|
# def self.instance
|
||||||
@@instance
|
# @@instance
|
||||||
end
|
# end
|
||||||
|
|
||||||
GALAXY_WIDTH = TETU::MAX_X
|
# GALAXY_WIDTH = TETU::MAX_X
|
||||||
GALAXY_HEIGHT = TETU::MAX_Y
|
# GALAXY_HEIGHT = TETU::MAX_Y
|
||||||
UI_WIDTH = GALAXY_WIDTH + TETU::UI_CONF["right_sidebar"].as_i64
|
# UI_WIDTH = GALAXY_WIDTH + TETU::UI_CONF["right_sidebar"].as_i64
|
||||||
UI_HEIGHT = GALAXY_HEIGHT
|
# UI_HEIGHT = GALAXY_HEIGHT
|
||||||
SQUARE_SIZE = TETU::UI_CONF["square_size"].as_i64
|
# SQUARE_SIZE = TETU::UI_CONF["square_size"].as_i64
|
||||||
|
|
||||||
GALAXY = SF::Texture.from_file("assets/#{GALAXY_WIDTH}x#{GALAXY_HEIGHT}/galaxy.jpg")
|
# GALAXY = SF::Texture.from_file("assets/#{GALAXY_WIDTH}x#{GALAXY_HEIGHT}/galaxy.jpg")
|
||||||
|
|
||||||
getter window : SF::RenderWindow
|
# getter window : SF::RenderWindow
|
||||||
getter delta_clock : SF::Clock
|
# getter delta_clock : SF::Clock
|
||||||
property planet_menu_selected : GameEntity? = nil
|
# property planet_menu_selected : GameEntity? = nil
|
||||||
|
|
||||||
def initialize
|
# def initialize
|
||||||
@window = SF::RenderWindow.new(
|
# @window = SF::RenderWindow.new(
|
||||||
SF::VideoMode.new(UI_WIDTH, UI_HEIGHT),
|
# SF::VideoMode.new(UI_WIDTH, UI_HEIGHT),
|
||||||
"To the End of The Universe",
|
# "To the End of The Universe",
|
||||||
)
|
# )
|
||||||
@delta_clock = SF::Clock.new
|
# @delta_clock = SF::Clock.new
|
||||||
end
|
# end
|
||||||
|
|
||||||
def [](k)
|
# def [](k)
|
||||||
@data[k]
|
# @data[k]
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
|
|
|
@ -15,17 +15,3 @@ module TETU::Helpers::Numbers
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
struct Number
|
|
||||||
def billions
|
|
||||||
self * TETU::Helpers::Numbers::BILLION
|
|
||||||
end
|
|
||||||
|
|
||||||
def millions
|
|
||||||
self * TETU::Helpers::Numbers::MILLION
|
|
||||||
end
|
|
||||||
|
|
||||||
def thousands
|
|
||||||
self * TETU::Helpers::Numbers::THOUSAND
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
module Ratio
|
|
||||||
alias IdValue = Hash(String, Number)
|
|
||||||
|
|
||||||
# minimum ratio of right/left, or nil if missing keys in right
|
|
||||||
def self.minimum_reverse(left : IdValue, right : IdValue)
|
|
||||||
return nil if left.keys & right.keys != left.keys
|
|
||||||
|
|
||||||
left.map do |id, left_value|
|
|
||||||
right_value = right[id]
|
|
||||||
right_value / left_value
|
|
||||||
end.min
|
|
||||||
end
|
|
||||||
|
|
||||||
# minimum ratio of left/right, or nil if missing keys in right
|
|
||||||
def self.minimum(left : IdValue, right : IdValue)
|
|
||||||
return nil if left.keys & right.keys != left.keys
|
|
||||||
|
|
||||||
left.map do |id, left_value|
|
|
||||||
right_value = right[id]
|
|
||||||
left_value / right_value
|
|
||||||
end.min
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,2 +1,2 @@
|
||||||
require "./systems/*"
|
require "./systems/*"
|
||||||
require "./systems/ui/*"
|
require "./systems/player/*"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
require "../components"
|
require "../components"
|
||||||
|
|
||||||
class TETU::EconomicProductionSystem < Entitas::ReactiveSystem
|
class TETU::EconomicProductionSystem < Entitas::ReactiveSystem
|
||||||
spoved_logger level: :debug, io: STDOUT, bind: true
|
spoved_logger level: :info, io: STDOUT, bind: true
|
||||||
|
|
||||||
def initialize(@contexts : Contexts)
|
def initialize(@contexts : Contexts)
|
||||||
@time_context = @contexts.time
|
@time_context = @contexts.time
|
||||||
|
@ -21,14 +21,9 @@ class TETU::EconomicProductionSystem < Entitas::ReactiveSystem
|
||||||
|
|
||||||
producer_group = @contexts.game.get_group Entitas::Matcher.all_of(Resources, Population, ManpowerAllocation)
|
producer_group = @contexts.game.get_group Entitas::Matcher.all_of(Resources, Population, ManpowerAllocation)
|
||||||
producer_group.entities.each do |e|
|
producer_group.entities.each do |e|
|
||||||
if !e.resources.can_produce?
|
next if !e.resources.can_produce?
|
||||||
logger.debug { "#{e.named} cannot produces" }
|
|
||||||
next
|
|
||||||
end
|
|
||||||
logger.debug { "#{e.named} produces now" }
|
|
||||||
|
|
||||||
e.resources.infras.each do |infra_id, infra|
|
e.resources.infras.each do |infra_id, infra|
|
||||||
logger.debug { "#{e.named.to_s} produces now via #{infra.id}" }
|
|
||||||
rate = prod_rate(infra, e)
|
rate = prod_rate(infra, e)
|
||||||
prod_rates = infra.prods.map { |res, prod| apply_prod(infra: infra, res: res, rate: rate, prod: prod) }
|
prod_rates = infra.prods.map { |res, prod| apply_prod(infra: infra, res: res, rate: rate, prod: prod) }
|
||||||
real_prod_rate = prod_rates.empty? ? rate : prod_rates.max
|
real_prod_rate = prod_rates.empty? ? rate : prod_rates.max
|
||||||
|
@ -47,16 +42,8 @@ class TETU::EconomicProductionSystem < Entitas::ReactiveSystem
|
||||||
end
|
end
|
||||||
|
|
||||||
def prod_rate(infra : Resources::Infra, producer : GameEntity) : Float64
|
def prod_rate(infra : Resources::Infra, producer : GameEntity) : Float64
|
||||||
if infra.consumes.empty?
|
return 1.0 if infra.consumes.empty?
|
||||||
logger.debug { "no consumption, rate 1.0" }
|
return 0.0 if infra.consumes.any? { |res, _value| infra.stores[res]?.nil? }
|
||||||
return 1.0
|
|
||||||
end
|
|
||||||
|
|
||||||
if infra.consumes.any? { |res, _value| infra.stores[res]?.nil? }
|
|
||||||
logger.debug { "missing consumable, 0.0" }
|
|
||||||
return 0.0
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: another function for pop.amount < manpower.optimal
|
# TODO: another function for pop.amount < manpower.optimal
|
||||||
allocated_manpower = producer.manpower_allocation.absolute[infra.id]
|
allocated_manpower = producer.manpower_allocation.absolute[infra.id]
|
||||||
maximal_rate =
|
maximal_rate =
|
||||||
|
@ -67,15 +54,14 @@ class TETU::EconomicProductionSystem < Entitas::ReactiveSystem
|
||||||
end
|
end
|
||||||
limited_rate = (infra.consumes.map { |res, value| infra.stores[res].amount / value } + [maximal_rate]).min
|
limited_rate = (infra.consumes.map { |res, value| infra.stores[res].amount / value } + [maximal_rate]).min
|
||||||
# if infra.id == "mine" || true
|
# if infra.id == "mine" || true
|
||||||
logger.debug { "" }
|
# logger.debug { "producer.named.name=#{producer.named.name}" }
|
||||||
logger.debug { "producer.named.name=#{producer.named.name}" }
|
# logger.debug { "infra.id=#{infra.id}" }
|
||||||
logger.debug { "infra.id=#{infra.id}" }
|
# logger.debug { "allocated_manpower=#{allocated_manpower}" }
|
||||||
logger.debug { "allocated_manpower=#{allocated_manpower}" }
|
# logger.debug { "infra.manpower.optimal=#{infra.manpower.optimal}" }
|
||||||
logger.debug { "infra.manpower.optimal=#{infra.manpower.optimal}" }
|
# logger.debug { "infra.manpower.min=#{infra.manpower.min} " }
|
||||||
logger.debug { "infra.manpower.min=#{infra.manpower.min} " }
|
# logger.debug { "maximal_rate=#{maximal_rate} " }
|
||||||
logger.debug { "maximal_rate=#{maximal_rate} " }
|
# logger.debug { "limited_rate=#{limited_rate}" }
|
||||||
logger.debug { "limited_rate=#{limited_rate}" }
|
# logger.debug { "" }
|
||||||
logger.debug { "" }
|
|
||||||
# end
|
# end
|
||||||
limited_rate
|
limited_rate
|
||||||
end
|
end
|
||||||
|
@ -83,10 +69,10 @@ class TETU::EconomicProductionSystem < Entitas::ReactiveSystem
|
||||||
# returns the real production rate, limited by the storage
|
# returns the real production rate, limited by the storage
|
||||||
# @param rate : the maximum production we should use
|
# @param rate : the maximum production we should use
|
||||||
def apply_prod(infra : Resources::Infra, rate : Float64, res : Resources::Name, prod : Float64) : Float64
|
def apply_prod(infra : Resources::Infra, rate : Float64, res : Resources::Name, prod : Float64) : Float64
|
||||||
logger.debug { "apply_prod wants rate=#{rate} res=#{res} prod=#{prod}" }
|
# logger.debug { "apply_prod wants rate=#{rate} res=#{res} prod=#{prod}" }
|
||||||
store = infra.stores[res]?
|
store = infra.stores[res]?
|
||||||
if store.nil? || rate > 0 && store.amount == store.max
|
if store.nil? || rate > 0 && store.amount == store.max
|
||||||
logger.debug { "max storage: apply_prod applied rate=0.0 res=#{res} prod=#{prod}" }
|
# logger.debug { "apply_prod applied rate=0.0 res=#{res} prod=#{prod}" }
|
||||||
return 0.0
|
return 0.0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -102,7 +88,7 @@ class TETU::EconomicProductionSystem < Entitas::ReactiveSystem
|
||||||
|
|
||||||
store.amount = new_amount
|
store.amount = new_amount
|
||||||
|
|
||||||
logger.debug { "ok: apply_prod applied rate=#{rate} res=#{res} prod=#{prod}, store=#{new_amount}" }
|
# logger.debug { "apply_prod applied rate=#{rate} res=#{res} prod=#{prod}" }
|
||||||
|
|
||||||
return rate
|
return rate
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
class TETU::GalaxyInitializerSystem
|
class TETU::GalaxyInitializerSystem
|
||||||
include Entitas::Systems::InitializeSystem
|
include Entitas::Systems::InitializeSystem
|
||||||
spoved_logger level: :debug, io: STDOUT, bind: true
|
spoved_logger level: :info, io: STDOUT, bind: true
|
||||||
|
|
||||||
def initialize(@contexts : Contexts); end
|
def initialize(@contexts : Contexts); end
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ class TETU::GalaxyInitializerSystem
|
||||||
EMPIRE_AMOUNT = AI_AMOUNT + 1 # add the player
|
EMPIRE_AMOUNT = AI_AMOUNT + 1 # add the player
|
||||||
AI_MIN_PLANETS = GALAXY_CONF["ai_start_populated_bodies_amount"].as_i
|
AI_MIN_PLANETS = GALAXY_CONF["ai_start_populated_bodies_amount"].as_i
|
||||||
PLANET_POP_PROBA = TETU::GALAXY_CONF["populated_planets_proba"].as_f
|
PLANET_POP_PROBA = TETU::GALAXY_CONF["populated_planets_proba"].as_f
|
||||||
AI_DEBUG_0_IS_PLANET = GALAXY_CONF["ai_debug_0_is_planet"].as_bool?
|
|
||||||
# NO_SPACE_EMPIRE_ID = 100001
|
# NO_SPACE_EMPIRE_ID = 100001
|
||||||
|
|
||||||
def init
|
def init
|
||||||
|
@ -45,11 +44,9 @@ class TETU::GalaxyInitializerSystem
|
||||||
|
|
||||||
bodies_amount = Helpers::Planet::BODIES_STATISTICS.sample
|
bodies_amount = Helpers::Planet::BODIES_STATISTICS.sample
|
||||||
bodies_amount = AI_MIN_PLANETS if !empire_id.nil? && bodies_amount < AI_MIN_PLANETS
|
bodies_amount = AI_MIN_PLANETS if !empire_id.nil? && bodies_amount < AI_MIN_PLANETS
|
||||||
logger.debug { "generate bodies_amount=#{bodies_amount}" }
|
|
||||||
|
|
||||||
bodies_amount.times.map do |index|
|
bodies_amount.times.map do |index|
|
||||||
body_type = Helpers::Planet::TYPES_STATISTICS.sample
|
body_type = Helpers::Planet::TYPES_STATISTICS.sample
|
||||||
body_type = :planet if AI_DEBUG_0_IS_PLANET && index == 0
|
|
||||||
ids_trash[body_type] += 1
|
ids_trash[body_type] += 1
|
||||||
body = generate_body(star: star, index: index, body_type: body_type, ids_trash: ids_trash)
|
body = generate_body(star: star, index: index, body_type: body_type, ids_trash: ids_trash)
|
||||||
if body_type == :asteroid_belt
|
if body_type == :asteroid_belt
|
||||||
|
@ -107,32 +104,19 @@ class TETU::GalaxyInitializerSystem
|
||||||
body
|
body
|
||||||
end
|
end
|
||||||
|
|
||||||
DEFAULT_INFRASTRUCTURES = {
|
DEFAULT_INFRASTRUCTURES = %w[e_store m_store f_store e_plant mine farm a_store l_store a_plant l_plant]
|
||||||
"e_store" => 2,
|
|
||||||
"m_store" => 2,
|
|
||||||
"f_store" => 5,
|
|
||||||
"e_plant" => 2,
|
|
||||||
"mine" => 2,
|
|
||||||
"agrifood" => 5,
|
|
||||||
"a_store" => 1,
|
|
||||||
"l_store" => 1,
|
|
||||||
"a_plant" => 1,
|
|
||||||
"l_plant" => 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
def populate(body)
|
def populate(body)
|
||||||
# logger.debug { "populate: #{body.named.name}..." }
|
# logger.debug { "populate: #{body.named.name}..." }
|
||||||
pop_amount = ((10_000.0)..(10.0.billions)).sample
|
pop_amount = ((10_000.0)..(10_000_000_000.0)).sample
|
||||||
Population.generate_for(body)
|
body.add_population amount: pop_amount
|
||||||
body.replace_component(Resources.default_populated)
|
body.replace_component(Resources.default_populated)
|
||||||
body.add_infrastructure_upgrades
|
body.add_infrastructure_upgrades
|
||||||
body.add_manpower_allocation
|
body.add_manpower_allocation
|
||||||
body.manpower_allocation.available = body.population.amount
|
body.manpower_allocation.available = body.population.amount
|
||||||
DEFAULT_INFRASTRUCTURES.each do |infra_id, infra_level|
|
DEFAULT_INFRASTRUCTURES.each do |infra_id|
|
||||||
infra_level.times do
|
upgrade = InfrastructureUpgrade.free_instant(id: infra_id)
|
||||||
upgrade = InfrastructureUpgrade.free_instant(id: infra_id)
|
body.infrastructure_upgrades.upgrades << upgrade
|
||||||
body.infrastructure_upgrades.upgrades << upgrade
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
# logger.debug { "populated: #{body.named.name}, now #{body.resources.to_s}, with #{body.infrastructure_upgrades.upgrades.size} upgrade to do..." }
|
# logger.debug { "populated: #{body.named.name}, now #{body.resources.to_s}, with #{body.infrastructure_upgrades.upgrades.size} upgrade to do..." }
|
||||||
body
|
body
|
||||||
|
|
|
@ -81,14 +81,14 @@ class TETU::InfrastructureUpgradesSystem < Entitas::ReactiveSystem
|
||||||
end
|
end
|
||||||
|
|
||||||
def pay_upgrade_tick(resources : Resources, upgrade : InfrastructureUpgrade, costs : InfrastructureUpgrade::Costs)
|
def pay_upgrade_tick(resources : Resources, upgrade : InfrastructureUpgrade, costs : InfrastructureUpgrade::Costs)
|
||||||
if !(missing_resource = costs.find { |res, amount| resources.stores[res].amount < amount })
|
if costs.all? { |res, amount| resources.stores[res].amount >= amount }
|
||||||
# pay the upgrade with local store
|
# pay the upgrade with local store
|
||||||
costs.all? { |res, amount| resources.stores[res].amount -= amount }
|
costs.all? { |res, amount| resources.stores[res].amount -= amount }
|
||||||
upgrade.current_tick += 1
|
upgrade.current_tick += 1
|
||||||
logger.debug { "paid tick upgrade" }
|
logger.debug { "paid tick upgrade" }
|
||||||
else
|
else
|
||||||
# if we can't pay the upgrade, we will "loose" one tick due to maintenance
|
# if we can't pay the upgrade, we will "loose" one tick due to maintenance
|
||||||
logger.debug { "cannot pay upgrade because #{missing_resource}" }
|
logger.debug { "cannot pay upgrade" }
|
||||||
upgrade.end_tick += 1
|
upgrade.end_tick += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
20
src/systems/player/request_handler.cr
Normal file
20
src/systems/player/request_handler.cr
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
class TETU::RequestHandlerSystem
|
||||||
|
include Entitas::Systems::ExecuteSystem
|
||||||
|
spoved_logger level: :info, io: STDOUT, bind: true
|
||||||
|
|
||||||
|
getter player_channel : Channel(Tuple(String, Channel(String)))
|
||||||
|
@contexts : Contexts
|
||||||
|
|
||||||
|
def initialize(@contexts, @player_channel)
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute
|
||||||
|
message, responder = player_channel.receive
|
||||||
|
if message == "stars"
|
||||||
|
stars = @contexts.game.get_group Entitas::Matcher.all_of(Named, Position, CelestialBody, PlayerOwned).none_of(StellarPosition)
|
||||||
|
responder.send stars.entities.map { |entity| entity.named.name }.join(",")
|
||||||
|
else
|
||||||
|
responder.send ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,7 @@
|
||||||
require "../components"
|
require "../components"
|
||||||
|
|
||||||
class TETU::PopulationGrowthSystem < Entitas::ReactiveSystem
|
class TETU::PopulationGrowthSystem < Entitas::ReactiveSystem
|
||||||
spoved_logger level: :debug, io: STDOUT, bind: true
|
spoved_logger level: :info, io: STDOUT, bind: true
|
||||||
|
|
||||||
def initialize(@contexts : Contexts)
|
def initialize(@contexts : Contexts)
|
||||||
@time_context = @contexts.time
|
@time_context = @contexts.time
|
||||||
|
@ -13,62 +13,15 @@ class TETU::PopulationGrowthSystem < Entitas::ReactiveSystem
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute(time_entities : Array(Entitas::IEntity))
|
def execute(time_entities : Array(Entitas::IEntity))
|
||||||
populateds = @contexts.game.get_group Entitas::Matcher.all_of(Population, Resources)
|
populateds = @contexts.game.get_group Entitas::Matcher.all_of(Population)
|
||||||
populateds.entities.each do |e|
|
populateds.entities.each do |e|
|
||||||
pop_amount = e.population.amount
|
pop_amount = e.population.amount
|
||||||
foods = e.population.foods
|
# let's say every adult make 1.5 child average in its average lifespan (80 years)
|
||||||
pop_foods_needs = foods.transform_values do |food_amount_per_pop|
|
# and one tick is one day
|
||||||
food_amount_per_pop * pop_amount
|
reproduction_rate = 1.5 * (1.0/80.0) * (1.0/365)
|
||||||
end
|
new_pop_amount = pop_amount + pop_amount * reproduction_rate
|
||||||
logger.debug { "pop_foods_needs=#{pop_foods_needs}" }
|
# logger.debug { "population growth: {reproduction_rate:#{reproduction_rate}} {population:#{e.population.to_s}} {bonus:#{pop_amount * reproduction_rate}}" }
|
||||||
logger.debug { "resouces=#{e.resources.stores}" }
|
e.replace_population(amount: new_pop_amount)
|
||||||
|
|
||||||
minimum_food_ratio = Ratio.minimum_reverse(pop_foods_needs, e.resources.stores.amount_hash) || 0.0
|
|
||||||
logger.debug { "minimum_food_ratio=#{minimum_food_ratio}" }
|
|
||||||
total_food_modifier = food_modifier(minimum_food_ratio)
|
|
||||||
logger.debug { "total_food_modifier=#{total_food_modifier}" }
|
|
||||||
|
|
||||||
new_pop_amount = pop_amount + pop_amount * (reproduction_rate(total_food_modifier))
|
|
||||||
logger.debug { "population growth: #{pop_amount} * #{new_pop_amount / pop_amount} => #{new_pop_amount}" }
|
|
||||||
e.replace_population(amount: new_pop_amount, foods: foods)
|
|
||||||
pop_foods_needs.each do |food_id, food_need|
|
|
||||||
# only consumes the need, only "theorical availability" counts, like a richness factor
|
|
||||||
food_consumption = minimum_food_ratio < 1.0 ? food_need * minimum_food_ratio : food_need
|
|
||||||
logger.debug { "population food consumption: #{food_id}:#{food_need} -> consumes #{food_consumption}" }
|
|
||||||
e.resources.stores[food_id].amount -= food_consumption
|
|
||||||
end if minimum_food_ratio > 0.0
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# we also add a modifier based on food availability
|
|
||||||
# that can be between -5 for famine with 0 food
|
|
||||||
# +0.0 if food required is reach no less no more
|
|
||||||
# up to 1.0 for post-scarcity inifity food (5x food required = +0.8)
|
|
||||||
def food_modifier(available_food_ratio : Float64)
|
|
||||||
return -5.0 if available_food_ratio <= 0.0
|
|
||||||
modifier = 1.0 / -available_food_ratio + 1.0
|
|
||||||
if modifier < -5.0
|
|
||||||
-5.0 # not worse than -5
|
|
||||||
else
|
|
||||||
modifier
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# let's say every adult make 1.5 child average in its average lifespan (80 years)
|
|
||||||
# and one tick is one day (3 children per couple).
|
|
||||||
def reproduction_rate(food_modifier) : Float64
|
|
||||||
(children_per_pop_average + food_modifier) * (1.0/pop_lifespan_average) * (1.0/365.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
def pop_per_food_per_tick : Float64
|
|
||||||
100.0.millions
|
|
||||||
end
|
|
||||||
|
|
||||||
def children_per_pop_average : Float64
|
|
||||||
1.5
|
|
||||||
end
|
|
||||||
|
|
||||||
def pop_lifespan_average : Float64
|
|
||||||
80.0
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,23 +8,14 @@ class TETU::UiService::PlanetInfrastructure < TETU::UiService
|
||||||
|
|
||||||
def draw
|
def draw
|
||||||
if ImGui.tree_node_ex("resources panel", ImGui::ImGuiTreeNodeFlags.new(ImGui::ImGuiTreeNodeFlags::DefaultOpen))
|
if ImGui.tree_node_ex("resources panel", ImGui::ImGuiTreeNodeFlags.new(ImGui::ImGuiTreeNodeFlags::DefaultOpen))
|
||||||
ImGui.text @planet.named.name
|
|
||||||
|
|
||||||
draw_population
|
|
||||||
draw_resources
|
draw_resources
|
||||||
ImGui.tree_pop
|
ImGui.tree_pop
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def draw_population
|
|
||||||
ImGui.text "Population:" + if @planet.has_population?
|
|
||||||
@planet.population.to_s(round: 4)
|
|
||||||
else
|
|
||||||
"None"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private def draw_resources
|
private def draw_resources
|
||||||
|
ImGui.text @planet.named.name
|
||||||
|
|
||||||
if @planet.has_resources?
|
if @planet.has_resources?
|
||||||
draw_storage
|
draw_storage
|
||||||
ImGui.text ""
|
ImGui.text ""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user