Skip to content

Commit

Permalink
added models component and renderer an integrated them into RRN compo…
Browse files Browse the repository at this point in the history
…nent with berry bush as example
  • Loading branch information
paulthegreat committed Jul 14, 2019
1 parent 4827fa4 commit 792bd32
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 3 deletions.
39 changes: 39 additions & 0 deletions components/models/models_component.lua
@@ -0,0 +1,39 @@
--[[
this component is used in conjunction with the models component renderer to render additional models for an entity
for example, to easily add/remove berries to/from a berry bush when it ripens or is harvested
]]

local ModelsComponent = class()

function ModelsComponent:initialize()
self._sv.models = {}
end

function ModelsComponent:add_model(name, options)
self:set_model_options(name, options)
end

function ModelsComponent:remove_model(name)
self._sv.models[name] = nil
self.__saved_variables:mark_changed()
end

function ModelsComponent:set_model_options(name, options)
local model = self._sv.models[name]
options = radiant.shallow_copy(options)
if options.visible == nil then
options.visible = (not model or model.visible == nil) or model.visible
end
self._sv.models[name] = options
self.__saved_variables:mark_changed()
end

function ModelsComponent:set_model_visibility(name, visible)
local model = self._sv.models[name]
if model then
model.visible = visible
self.__saved_variables:mark_changed()
end
end

return ModelsComponent
20 changes: 20 additions & 0 deletions entities/plants/berry_bush/berry_bush.json
@@ -0,0 +1,20 @@
{
"components": {
"stonehearth:renewable_resource_node": {
"half_renewed_model": {
"model": "/stonehearth/entities/plants/berry_bush/berry_bush.qb",
"matrix": "Matrix_2",
"scale": 0.15,
"offset": {"x": 5, "y": 0, "z": 5}
},
"renewed_model": {
"model": "/stonehearth/entities/plants/berry_bush/berry_bush.qb",
"matrix": "Matrix_2",
"material": "materials/glowy_voxel.material.json",
"scale": 0.16,
"rotation": 180,
"offset": {"x": 5.5, "y": 0, "z": 5.5}
}
}
}
}
13 changes: 12 additions & 1 deletion entities/plants/berry_bush/berry_bush_ghost.json
@@ -1,5 +1,16 @@
{
"components": {},
"components": {
"model_variants": {
"default": {
"mixintypes": {
"models": "override"
},
"models": [
"/stonehearth/entities/plants/berry_bush/berry_bush_empty.qb"
]
}
}
},
"entity_data": {
"stonehearth:catalog": {
"material_tags": ["decoration"]
Expand Down
3 changes: 3 additions & 0 deletions manifest.json
Expand Up @@ -2306,6 +2306,7 @@
"stonehearth/entities/resources/earth_essence/earth_essence.json": "file(entities/resources/earth_essence/earth_essence.json)",
"stonehearth/entities/plants/silkweed/silkweed/silkweed.json": "file(entities/plants/silkweed/silkweed.json)",
"stonehearth/entities/plants/silkweed/silkweed/silkweed_ghost.json": "file(entities/plants/silkweed/silkweed_ghost.json)",
"stonehearth/entities/plants/berry_bush/berry_bush.json": "file(entities/plants/berry_bush/berry_bush.json)",
"stonehearth/entities/plants/berry_bush/berry_bush_ghost.json": "file(entities/plants/berry_bush/berry_bush_ghost.json)",
"stonehearth/entities/plants/cactus_tulip_plant/blue/cactus_tulip_blue_plant_ghost.json": "file(entities/plants/cactus_tulip_plant/blue/cactus_tulip_blue_plant_ghost.json)",
"stonehearth/entities/plants/cactus_tulip_plant/yellow/cactus_tulip_yellow_plant_ghost.json": "file(entities/plants/cactus_tulip_plant/yellow/cactus_tulip_yellow_plant_ghost.json)",
Expand Down Expand Up @@ -2928,6 +2929,7 @@
"entity_modification": "file(components/entity_modification/entity_modification_component.lua)",
"fence": "file(components/fence/fence_component.lua)",
"mechanical": "file(components/mechanical/mechanical_component.lua)",
"models": "file(components/models/models_component.lua)",
"pasture_item": "file(components/pasture_item/pasture_item_component.lua)",
"patrol_banner": "file(components/patrol_banner/patrol_banner_component.lua)",
"pile": "file(components/pile/pile_component.lua)",
Expand Down Expand Up @@ -2955,6 +2957,7 @@
"connection": "file(renderers/connection/connection_renderer.lua)",
"tile": "file(renderers/tile/tile_renderer.lua)",
"fence": "file(renderers/fence/fence_renderer.lua)",
"models": "file(renderers/models/models_renderer.lua)",
"patrol_banner": "file(renderers/patrol_banner/patrol_banner_renderer.lua)",
"pasture_item": "file(renderers/pasture_item/pasture_item_renderer.lua)",
"vine": "file(renderers/vine/vine_renderer.lua)"
Expand Down
163 changes: 161 additions & 2 deletions monkey_patches/ace_renewable_resource_node_component.lua
Expand Up @@ -4,10 +4,27 @@ local log = radiant.log.create_logger('renewable_resource_node')
local RenewableResourceNodeComponent = radiant.mods.require('stonehearth.components.renewable_resource_node.renewable_resource_node_component')
local AceRenewableResourceNodeComponent = class()

local RENEWED_MODEL_NAME = 'stonehearth:renewable_resource_node:renewed'
local HALF_RENEWED_MODEL_NAME = 'stonehearth:renewable_resource_node:half_renewed'

function AceRenewableResourceNodeComponent:create()
self._is_create = true
end

AceRenewableResourceNodeComponent._ace_old_activate = RenewableResourceNodeComponent.activate
function AceRenewableResourceNodeComponent:activate()
self:_ace_old_activate()
if self._sv.half_renew_timer then
self._sv.half_renew_timer:bind(function()
self:_set_model_half_renewed()
end)
end

if self._sv.harvestable then
self:_reset_model()
end
end

AceRenewableResourceNodeComponent._ace_old_post_activate = RenewableResourceNodeComponent.post_activate
function AceRenewableResourceNodeComponent:post_activate()
if not self._json then
Expand Down Expand Up @@ -62,10 +79,98 @@ function AceRenewableResourceNodeComponent:get_auto_harvest_enabled(player_id)
end
end

AceRenewableResourceNodeComponent._ace_old_spawn_resource = RenewableResourceNodeComponent.spawn_resource
function AceRenewableResourceNodeComponent:spawn_resource(harvester_entity, location, owner_player_id)
if not self._json.spawn_resource_immediately or self:_can_pasture_animal_renewably_harvest() ~= false then
self:_ace_old_spawn_resource(harvester_entity, location, owner_player_id)
self:_cancel_harvest_request()
local json = self._json

local will_destroy_entity = false
-- if we have a durability and we've run out, destroy the entity
if self._sv.durability then
self._sv.durability = self._sv.durability - 1
if self._sv.durability <= 0 then
self._sv.harvestable = false
will_destroy_entity = true
end
end

local player_id = owner_player_id or radiant.entities.get_player_id(harvester_entity)
local spawned_resources, singular_item = self:_place_spawned_items(json, player_id, location, will_destroy_entity)

for id, item in pairs(spawned_resources) do
if not json.skip_owner_inventory then
-- add it to the inventory of the owner
local inventory = stonehearth.inventory:get_inventory(player_id)
if inventory then
inventory:add_item_if_not_full(item)
end
end
end

if json.resource_spawn_effect then
local proxy = radiant.entities.create_entity('stonehearth:object:transient', { debug_text = 'spawn effect effect anchor' })
local location = radiant.entities.get_world_grid_location(self._entity)
radiant.terrain.place_entity_at_exact_location(proxy, location)
local effect = radiant.effects.run_effect(proxy, json.resource_spawn_effect)
effect:set_finished_cb(function()
radiant.entities.destroy_entity(proxy)
end)
end

-- if we have a durability and we've run out, destroy the entity
if will_destroy_entity then
radiant.entities.destroy_entity(self._entity)
return singular_item
end

--start the countdown to respawn.
self:_set_model_depleted()

self:_update_renew_timer()

--Change the description
if json.unripe_description then
radiant.entities.set_description(self._entity, json.unripe_description)
end

self._sv.harvestable = false
self.__saved_variables:mark_changed()

return singular_item
end
end

function AceRenewableResourceNodeComponent:_set_model_depleted()
if self._json.renewed_model or self._json.half_renewed_model then
self._entity:add_component('stonehearth_ace:models'):remove_model(RENEWED_MODEL_NAME)
self._entity:add_component('stonehearth_ace:models'):remove_model(HALF_RENEWED_MODEL_NAME)
else
local render_info = self._entity:add_component('render_info')
render_info:set_model_variant('depleted')
end
end

function AceRenewableResourceNodeComponent:_set_model_half_renewed()
if self._json.half_renewed_model then
self._entity:add_component('stonehearth_ace:models'):add_model(HALF_RENEWED_MODEL_NAME, self._json.half_renewed_model)
end
end

--- Reset the model to the default. Also, stop listening for effects
function AceRenewableResourceNodeComponent:_reset_model()
if self._json.renewed_model then
self._entity:add_component('stonehearth_ace:models'):add_model(RENEWED_MODEL_NAME, self._json.renewed_model)
self._entity:add_component('stonehearth_ace:models'):remove_model(HALF_RENEWED_MODEL_NAME)
else
local render_info = self._entity:add_component('render_info')
render_info:set_model_variant('')
end

if self._renew_effect then
self._renew_effect:set_finished_cb(nil)
:set_trigger_cb(nil)
:stop()
self._renew_effect = nil
end
end

Expand Down Expand Up @@ -115,6 +220,60 @@ function AceRenewableResourceNodeComponent:renew()
self:_auto_request_harvest()
end

function AceRenewableResourceNodeComponent:_update_renew_timer()
if self._sv.paused then
-- Do not update the timer if paused.
return
end

local renewal_time = self._renewal_time
if self._sv.renew_timer then
renewal_time = stonehearth.calendar:get_remaining_time(self._sv.renew_timer)
end

--Calculate renewal time based on stats
local attributes = self._entity:get_component('stonehearth:attributes')
if attributes then
local modifier = attributes:get_attribute('renewable_resource_rate_multiplier', 1)
renewal_time = radiant.math.round(renewal_time * modifier)
end

self:_stop_renew_timer()

if renewal_time < 1 then
-- It is possible for renewal time to be negative if the timer tracker falls behind actual game clock
-- This will sometimes happen on load when we get really large game ticks
renewal_time = 1
end

self._sv.renew_timer = stonehearth.calendar:set_persistent_timer("RenewableResourceNodeComponent renew", renewal_time,
function ()
self:renew()
end
)

if self._json.half_renewed_model then
renewal_time = renewal_time / 2
if renewal_time >= 1 then
self._sv.half_renew_timer = stonehearth.calendar:set_persistent_timer("RenewableResourceNodeComponent half-renew", renewal_time,
function ()
self:_set_model_half_renewed()
end
)
end
end
end

AceRenewableResourceNodeComponent._ace_old__stop_renew_timer = RenewableResourceNodeComponent._stop_renew_timer
function AceRenewableResourceNodeComponent:_stop_renew_timer()
if self._sv.half_renew_timer then
self._sv.half_renew_timer:destroy()
self._sv.half_renew_timer = nil
end

self:_ace_old__stop_renew_timer()
end

AceRenewableResourceNodeComponent._ace_old__place_spawned_items = RenewableResourceNodeComponent._place_spawned_items
function AceRenewableResourceNodeComponent:_place_spawned_items(json, owner, location, will_destroy_entity)
local spawned_items, item = self:_ace_old__place_spawned_items(json, owner, location, will_destroy_entity)
Expand Down
70 changes: 70 additions & 0 deletions renderers/models/models_renderer.lua
@@ -0,0 +1,70 @@
local Point3 = _radiant.csg.Point3

local ModelsRenderer = class()
local log = radiant.log.create_logger('models.renderer')

function ModelsRenderer:initialize(render_entity, datastore)
self._entity = render_entity:get_entity()
self._entity_node = render_entity:get_node()
self._node = self._entity_node:add_group_node('model node')

self._datastore = datastore
self._model_nodes = {}

self._datastore_trace = self._datastore:trace('drawing models')
:on_changed(function ()
self:_update()
end)
:push_object_state()
end

function ModelsRenderer:destroy()
self:_destroy_model_nodes()
if self._node then
self._node:destroy()
self._node = nil
end
if self._datastore_trace and self._datastore_trace.destroy then
self._datastore_trace:destroy()
self._datastore_trace = nil
end
end

function ModelsRenderer:_destroy_model_nodes()
for _, node in ipairs(self._model_nodes) do
node:destroy()
end
self._model_nodes = {}
end

function ModelsRenderer:_update()
self:_destroy_model_nodes()

local data = self._datastore:get_data()
local models = data.models

for _, model in pairs(models) do
self:_create_node(model)
end
end

function ModelsRenderer:_create_node(options)
if options and options.model and options.visible then
local rotation = options.rotation or 0
local offset = radiant.util.to_point3(options.offset) or Point3.zero
local scale = options.scale or 0.1
if options.scale_with_entity then
scale = scale * self._entity:get_component('render_info'):get_scale()
end
local node = _radiant.client.create_qubicle_matrix_node(self._node, options.model, options.matrix or 'background', offset)
if node then
node:set_transform(0, 0, 0, 0, rotation, 0, scale, scale, scale)
node:set_material(options.material or 'materials/voxel.material.json')
table.insert(self._model_nodes, node)
else
--log:error('nil result from create_qubicle_matrix_node "%s" with rotation %s', model, rotation)
end
end
end

return ModelsRenderer

0 comments on commit 792bd32

Please sign in to comment.