Error, component can't get the location of its entity [solved]

I have this very short component which basically should make the entity who has it float at the water surface.

local Point3 = _radiant.csg.Point3
local ArchipelagoFloat = class()

function ArchipelagoFloat:post_activate()
	local location = radiant.entities.get_world_location(self._entity)
	local intersected_entities = radiant.terrain.get_entities_at_point(location)

	for id, entity in pairs(intersected_entities) do
		local water_component = entity:get_component('stonehearth:water')
		if water_component then
			local water_level = water_component:get_water_level()
			radiant.terrain.place_entity_at_exact_location(self._entity,location+Point3(0,water_level,0))
			break
		end
	end
end

return ArchipelagoFloat

But it is giving error because for some reason it can’t get a location. The location variable is always nil…

When the object is placed the game throw this error message:


2017-05-13 13:25:33.382504 | server |  0 |                         lua.code | -- Script Error (lua) Begin ------------------------------- 
2017-05-13 13:25:33.383001 | server |  0 |                         lua.code |    No matching overload found, candidates:
2017-05-13 13:25:33.383001 | server |  0 |                         lua.code |    void __init(luabind::argument const&,Point3 const&,Point3 const&,int)
2017-05-13 13:25:33.383001 | server |  0 |                         lua.code |    void __init(luabind::argument const&,Point3 const&,Point3 const&)
2017-05-13 13:25:33.383001 | server |  0 |                         lua.code |    void __init(luabind::argument const&,Point3 const&,int)
2017-05-13 13:25:33.383001 | server |  0 |                         lua.code |    void __init(luabind::argument const&,Point3 const&)
2017-05-13 13:25:33.383001 | server |  0 |                         lua.code |    void __init(luabind::argument const&,Cube3 const&)
2017-05-13 13:25:33.383001 | server |  0 |                         lua.code |    void __init(luabind::argument const&)
2017-05-13 13:25:33.383001 | server |  0 |                         lua.code |    stack traceback:
2017-05-13 13:25:33.383504 | server |  0 |                         lua.code |    	[C]: ?
2017-05-13 13:25:33.383504 | server |  0 |                         lua.code |    	[C]: ?
2017-05-13 13:25:33.383504 | server |  0 |                         lua.code |    	[C]: in function 'Cube3'
2017-05-13 13:25:33.383504 | server |  0 |                         lua.code |    	radiant/modules/terrain.lua:258: in function 'get_entities_at_point'
2017-05-13 13:25:33.383504 | server |  0 |                         lua.code |    	archipelago_biome/components/float/float.lua:6: in function <archipelago_biome/components/float/float.lua:4>
2017-05-13 13:25:33.383504 | server |  0 |                         lua.code | -- Lua Error End   ------------------------------- 

Ok, it was obvious that the component was live before the entity position was defined.
So I changed my code.
I’m now trying to listen for when the entity is placed in the water. My component is now like this:

local Point3 = _radiant.csg.Point3
local Cube3 = _radiant.csg.Cube3
local ArchipelagoFloat = class()
local log = radiant.log.create_logger('meu_log')

function ArchipelagoFloat:activate()
	if not self._added_to_world_listener then
		log:error('_added_to_world_listener created')
		self._added_to_world_listener = radiant.events.listen(self._entity, 'stonehearth:on_added_to_world', self, self._on_added_to_world)
		self._item_placed_listener = radiant.events.listen(self._entity, 'stonehearth:item_placed', self, self._item_placed)
	end
end

function ArchipelagoFloat:_item_placed()
	log:error('item_placed')
end

function ArchipelagoFloat:_on_added_to_world()
	log:error('on_added_to_world')
	local location = radiant.entities.get_world_grid_location(self._entity)
	local cube = Cube3(location):inflated(Point3(2,2,2))
	local intersected_entities = radiant.terrain.get_entities_in_cube(cube)

	for id, entity in pairs(intersected_entities) do
		local water_component = entity:get_component('stonehearth:water')
		if water_component then
			log:error('is in the water')

			local water_level = water_component:get_water_level()
			location.y = math.floor(water_level)
			log:error('float location %s', location.y)
			radiant.terrain.place_entity_at_exact_location(self._entity, location, {force_iconic = false})
			break
		end
	end
end

function ArchipelagoFloat:destroy()
	if self._added_to_world_listener then
		self._added_to_world_listener:destroy()
		self._added_to_world_listener = nil
	end
end

return ArchipelagoFloat

You will notice that I have two listeners. but both are not working.
The stonehearth:on_added_to_world only works when I spawn the item using debug tools, or use commands like “reset” or “teleport”. Only this will activate that listener. When a hearthling puts the items themselves it will not trigger it!

The stonehearth:item_placed should work and should be the correct option! But… It requires an specific hearthling in the listener to trigger it.
I can’t just listen for the item being placed by anyone. I would need to inform the listener to trigger for the correct hearthling carrying it… Which is impossible to know, as anyone could place the item…

How can I listen for an item being placed, without knowing who placed it? I just need to know when my item was placed, doesn’t matter who.

Do you want them to sail of the island? or do we have to wait, with patience, for you to figure this problem out, before you unveil what you are making? :merry:
I wish i could help, but i am just curious on the sideline.

I’m just trying to make things float for now. For example, when you place the raft at the ocean, it will be placed at the bottom. I’m trying to make it go up to above water level after it is placed.

1 Like

I discovered the problem. Gravity was always on, even though I turned it off in the json using:

      "navgrid": {
         "has_physics": false
      },

There is multiple occurrences in the code where the gravity was set back to “on”. Like in the PickupPlacedItemAdjacent class, with this piece:

-- gravity may have been turned off when placed.  turn it back on
item:add_component('mob'):set_ignore_gravity(false)

That was making the boat fall back into the bottom of the ocean. I had then explicitly call in lua to make it off and is now working fine!

1 Like

Yay! And the hearthlings are now seafarers :jubilant: Next step: how to get the rafts to move?
You are like the hearthling that invents the wheel dear Bruno :wink:

This was just to make it look better. But I have some ideas to make boat travels. Nothing fancy though.

1 Like

cool for you,
can you share you working listen event ? previously you say it is not working
but now ?

it is a good example for how i can use a event for my butcher event :slight_smile:

The “stonehearth:on_added_to_world” was working. It just happened to fire twice. The first time as expected, but then a second time after the lua code moved it, ending in an infinity loop.
The code changed just a little compared to what I shown above. It now listen for the event only once, and also added a timer to delay the movement to the surface, this all to avoid the loop.

[details=The actual code]

local Point3 = _radiant.csg.Point3
local ArchipelagoFloat = class()
-- local log = radiant.log.create_logger('meu_log')

function ArchipelagoFloat:activate()
	if not self._added_to_world_listener then
		-- log:error('_added_to_world_listener created')
		self._added_to_world_listener = radiant.events.listen_once(self._entity, 'stonehearth:on_added_to_world', function()
			-- log:error('_added_to_world_listener triggered')
			self._added_to_world_listener = nil
			self:on_added_to_world()
			end)
	end
end

function ArchipelagoFloat:on_added_to_world()
	local float_callback = function ()
		--bla
		-- log:error('timer activated')
		local location = radiant.entities.get_world_grid_location(self._entity)
		local intersected_entities = radiant.terrain.get_entities_at_point(location)

		for id, entity in pairs(intersected_entities) do
			local water_component = entity:get_component('stonehearth:water')
			if water_component then
				-- log:error('object is in the water')

				local water_level = water_component:get_water_level()
				location.y = math.floor(water_level)
				-- log:error('float location %s', location.y)
				radiant.terrain.place_entity_at_exact_location(self._entity, location, {force_iconic = false})
				self._entity:add_component('mob'):set_ignore_gravity(true)

				break
			end
		end
		self.float_timer = nil
		-- log:error('setting listener again')
		self._added_to_world_listener = radiant.events.listen_once(self._entity, 'stonehearth:on_added_to_world', function()
			-- log:error('listener triggered again')
			self._added_to_world_listener = nil
			self:on_added_to_world()
			end)
	end
	-- log:error('timer set')
	self.float_timer = stonehearth.calendar:set_persistent_timer("FloatComponent float_callback", 0, float_callback)
end

function ArchipelagoFloat:destroy()
	if self._added_to_world_listener then
		self._added_to_world_listener:destroy()
		self._added_to_world_listener = nil
	end
end

return ArchipelagoFloat
```[/details]

thanks, i try to modify my lua code.
so one question : i see the use of log:
but you dont need to close the log ?
if you have : local log = radiant.log.create_logger(‘meu_log’)
you dont need to close it ? it is automatly closed with the destroy function ?

i am new in lua code. i am a monkey :stuck_out_tongue: i mine and i try to understand :stuck_out_tongue:

so i get this :stuck_out_tongue: :
release-707 (x64)[M]
stonehearth/components/ai/ai_component.lua:242: assertion failed!
stack traceback:
radiant/modules/common.lua:245: in function 'report_traceback’
radiant/modules/common.lua:256: in function <radiant/modules/common.lua:250>
[C]: in function 'assert’
stonehearth/components/ai/ai_component.lua:242: in function '_add_action_to_index’
stonehearth/components/ai/ai_component.lua:293: in function '_add_action_internal’
stonehearth/components/ai/ai_component.lua:237: in function 'add_action’
stonehearth/services/server/ai/ai_injector.lua:69: in function '_inject_actions’
stonehearth/services/server/ai/ai_injector.lua:50: in function '_inject_ai’
stonehearth/services/server/ai/ai_injector.lua:15: in function '__user_init’
radiant/lib/unclasslib.lua:270: in function '__init’
radiant/lib/unclasslib.lua:201: in function ‘AiInjector’
…
…s/server/town/orchestrators/promote_orchestrator.lua:71: in function ‘_change_job’
…s/server/town/orchestrators/promote_orchestrator.lua:22: in function '_trigger_cb’
radiant/modules/effects/trigger_effect.lua:14: in function 'fn’
radiant/controllers/nonpersistent_timer.lua:56: in function 'fire’
radiant/controllers/time_tracker_controller.lua:84: in function <radiant/controllers/time_tracker_controller.lua:84>
[C]: in function 'xpcall’
radiant/modules/common.lua:265: in function 'xpcall’
radiant/controllers/time_tracker_controller.lua:84: in function 'set_now’
radiant/modules/gamestate.lua:9: in function 'set_now’
radiant/server.lua:60: in function <radiant/server.lua:58>

here the code. (dont know how to past code)…
local Entity = _radiant.om.Entity

local WaitForPastureVacancyCE = class()
local log = radiant.log.create_logger(‘ce_log’)

– Does the pasture need animals? If so, WAIT so the other dude gets to run.
– If not, listen for the pasture’s “need animals” event, and then wait, and then run
function WaitForPastureVacancyCE:activate()

if not self._added_to_world_listener then
	log:error('_added_to_world_listener createdCECE')
	self._added_to_world_listener = radiant.events.listen_once(self._entity, 'stonehearth:on_pasture_animals_changed', function()
		log:error('_added_to_world_listener triggeredCECE')
		self._added_to_world_listener = nil
		self:pasture_check()
		end)
end

end

function WaitForPastureVacancyCE:pasture_check()
local float_callback = function ()
log:error(‘timer activated’)
– local pasture_component = self._entity.pasture:get_component(‘stonehearth:shepherd_pasture’)
–
– if pasture_component and
– pasture_component:get_num_animals() == pasture_component:get_max_animals() then
– log:error(‘CECEok cest egal !!!’)
–here add the killling event .
–check the animal to kill
–local listanimal = pasture_component:get_animals()
– for _, critter_tracker in pairs(listanimal) do
– local locanimal = critter_tracker.entity
— if locanimal and locanimal.entity then
– local animal = locanimal.entity
– local age = radiant.entities.get_entity_data(animal, ‘stonehearth:evolve_data’)
– local stage = (age and age.current_stage) or “adult”
– if stage == “adult” then
---- send excute order to kill --> commands:slaughter" ?
– radiant.events.trigger_async(animal, ‘stonehearth:commands:slaughter’)
– end
– end
– end
self.float_timer = nil
log:error(‘setting listener againCECE’)
self._added_to_world_listener = radiant.events.listen_once(self._entity, ‘stonehearth:on_pasture_animals_changed’, function()
log:error(‘listener triggered againCECE’)
self._added_to_world_listener = nil
self:pasture_check()
end)
end
log:error(‘timer setCECE’)
self.float_timer = stonehearth.calendar:set_persistent_timer(“FloatComponent float_callback”, 0, float_callback)
end

function WaitForPastureVacancyCE:destroy()
if self._added_to_world_listener then
self._added_to_world_listener:destroy()
self._added_to_world_listener = nil
end
end

return WaitForPastureVacancyCE

I actually don’t know. I would guess you does need to destroy it later, better in the destroy() function.

But I only use those while testing, then I comment it out.
You can see that they are all prefixed with –, which is the comment symbol in lua. So those lines are all ignored.

Edit: I see your code, I don’t think you need any of my code in your component. Why you need to check when something was added to world? And why you need a timer? Keep in mind my code is very “hard coded” meaning that it was constructed for that task alone and will probably not be usable in anything else.