forked from theFox6/working_villages
-
Notifications
You must be signed in to change notification settings - Fork 0
/
api.lua
864 lines (745 loc) · 27.2 KB
/
api.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
smart_villages.animation_frames = {
STAND = { x= 0, y= 79, },
LAY = { x=162, y=166, },
WALK = { x=168, y=187, },
MINE = { x=189, y=198, },
WALK_MINE = { x=200, y=219, },
SIT = { x= 81, y=160, },
}
smart_villages.registered_villagers = {}
smart_villages.registered_jobs = {}
smart_villages.registered_eggs = {}
-- smart_villages.is_job reports whether a item is a job item by the name.
function smart_villages.is_job(item_name)
if smart_villages.registered_jobs[item_name] then
return true
end
return false
end
-- smart_villages.is_villager reports whether a name is villager's name.
function smart_villages.is_villager(name)
if smart_villages.registered_villagers[name] then
return true
end
return false
end
---------------------------------------------------------------------
-- smart_villages.villager represents a table that contains common methods
-- for villager object.
-- this table must be contains by a metatable.__index of villager self tables.
-- minetest.register_entity set initial properties as a metatable.__index, so
-- this table's methods must be put there.
smart_villages.villager = {}
-- smart_villages.villager.get_inventory returns a inventory of a villager.
function smart_villages.villager:get_inventory()
return minetest.get_inventory {
type = "detached",
name = self.inventory_name,
}
end
-- smart_villages.villager.get_job_name returns a name of a villager's current job.
function smart_villages.villager:get_job_name()
local inv = self:get_inventory()
return inv:get_stack("job", 1):get_name()
end
-- smart_villages.villager.get_job returns a villager's current job definition.
function smart_villages.villager:get_job()
local name = self:get_job_name()
if name ~= "" then
return smart_villages.registered_jobs[name]
end
return nil
end
-- smart_villages.villager.get_nearest_player returns a player object who
-- is the nearest to the villager.
function smart_villages.villager:get_nearest_player(range_distance)
local player, min_distance = nil, range_distance
local position = self.object:getpos()
local all_objects = minetest.get_objects_inside_radius(position, range_distance)
for _, object in pairs(all_objects) do
if object:is_player() then
local player_position = object:getpos()
local distance = vector.distance(position, player_position)
if distance < min_distance then
min_distance = distance
player = object
end
end
end
return player
end
-- woriking_villages.villager.get_nearest_item_by_condition returns the position of
-- an item that returns true for the condition
function smart_villages.villager:get_nearest_item_by_condition(cond, range_distance)
local max_distance=range_distance
if type(range_distance) == "table" then
max_distance=math.max(math.max(range_distance.x,range_distance.y),range_distance.z)
end
local item = nil
local min_distance = max_distance
local position = self.object:getpos()
local all_objects = minetest.get_objects_inside_radius(position, max_distance)
for _, object in pairs(all_objects) do
if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == "__builtin:item" then
local found_item = ItemStack(object:get_luaentity().itemstring):to_table()
if found_item then
if cond(found_item) then
local item_position = object:getpos()
local distance = vector.distance(position, item_position)
if distance < min_distance then
min_distance = distance
item = object
end
end
end
end
end
return item;
end
-- smart_villages.villager.get_front returns a position in front of the villager.
function smart_villages.villager:get_front()
local direction = self:get_look_direction()
if math.abs(direction.x) >= 0.5 then
if direction.x > 0 then direction.x = 1 else direction.x = -1 end
else
direction.x = 0
end
if math.abs(direction.z) >= 0.5 then
if direction.z > 0 then direction.z = 1 else direction.z = -1 end
else
direction.z = 0
end
--direction.y = direction.y - 1
return vector.add(vector.round(self.object:getpos()), direction)
end
-- smart_villages.villager.get_front_node returns a node that exists in front of the villager.
function smart_villages.villager:get_front_node()
local front = self:get_front()
return minetest.get_node(front)
end
-- smart_villages.villager.get_back returns a position behind the villager.
function smart_villages.villager:get_back()
local direction = self:get_look_direction()
if math.abs(direction.x) >= 0.5 then
if direction.x > 0 then direction.x = -1
else direction.x = 1 end
else
direction.x = 0
end
if math.abs(direction.z) >= 0.5 then
if direction.z > 0 then direction.z = -1
else direction.z = 1 end
else
direction.z = 0
end
--direction.y = direction.y - 1
return vector.add(vector.round(self.object:getpos()), direction)
end
-- smart_villages.villager.get_back_node returns a node that exists behind the villager.
function smart_villages.villager:get_back_node()
local back = self:get_back()
return minetest.get_node(back)
end
-- smart_villages.villager.get_look_direction returns a normalized vector that is
-- the villagers's looking direction.
function smart_villages.villager:get_look_direction()
local yaw = self.object:getyaw()
return vector.normalize{x = -math.sin(yaw), y = 0.0, z = math.cos(yaw)}
end
-- smart_villages.villager.set_animation sets the villager's animation.
-- this method is wrapper for self.object:set_animation.
function smart_villages.villager:set_animation(frame)
self.object:set_animation(frame, 15, 0)
if frame == smart_villages.animation_frames.LAY then
local dir = self:get_look_direction()
local dirx = math.abs(dir.x)*0.5
local dirz = math.abs(dir.z)*0.5
self.object:set_properties({collisionbox={-0.5-dirx, 0, -0.5-dirz, 0.5+dirx, 0.5, 0.5+dirz}})
else
self.object:set_properties({collisionbox={-0.25, 0, -0.25, 0.25, 1.75, 0.25}})
end
end
-- smart_villages.villager.set_yaw_by_direction sets the villager's yaw
-- by a direction vector.
function smart_villages.villager:set_yaw_by_direction(direction)
self.object:setyaw(math.atan2(direction.z, direction.x) - math.pi / 2)
end
-- smart_villages.villager.get_wield_item_stack returns the villager's wield item's stack.
function smart_villages.villager:get_wield_item_stack()
local inv = self:get_inventory()
return inv:get_stack("wield_item", 1)
end
-- smart_villages.villager.set_wield_item_stack sets villager's wield item stack.
function smart_villages.villager:set_wield_item_stack(stack)
local inv = self:get_inventory()
inv:set_stack("wield_item", 1, stack)
end
-- smart_villages.villager.add_item_to_main add item to main slot.
-- and returns leftover.
function smart_villages.villager:add_item_to_main(stack)
local inv = self:get_inventory()
return inv:add_item("main", stack)
end
-- smart_villages.villager.move_main_to_wield moves itemstack from main to wield.
-- if this function fails then returns false, else returns true.
function smart_villages.villager:move_main_to_wield(pred)
local inv = self:get_inventory()
local main_size = inv:get_size("main")
for i = 1, main_size do
local stack = inv:get_stack("main", i)
if pred(stack:get_name()) then
local wield_stack = inv:get_stack("wield_item", 1)
inv:set_stack("wield_item", 1, stack)
inv:set_stack("main", i, wield_stack)
return true
end
end
return false
end
-- smart_villages.villager.is_named reports the villager is still named.
function smart_villages.villager:is_named()
return self.nametag ~= ""
end
-- smart_villages.villager.has_item_in_main reports whether the villager has item.
function smart_villages.villager:has_item_in_main(pred)
local inv = self:get_inventory()
local stacks = inv:get_list("main")
for _, stack in ipairs(stacks) do
local itemname = stack:get_name()
if pred(itemname) then
return true
end
end
end
-- smart_villages.villager.change_direction change direction to destination and velocity vector.
function smart_villages.villager:change_direction(destination)
local position = self.object:getpos()
local direction = vector.subtract(destination, position)
direction.y = 0
local velocity = vector.multiply(vector.normalize(direction), 1.5)
self.object:setvelocity(velocity)
self:set_yaw_by_direction(direction)
end
-- smart_villages.villager.change_direction_randomly change direction randonly.
function smart_villages.villager:change_direction_randomly()
local direction = {
x = math.random(0, 5) * 2 - 5,
y = 0,
z = math.random(0, 5) * 2 - 5,
}
local velocity = vector.multiply(vector.normalize(direction), 1.5)
self.object:setvelocity(velocity)
self:set_yaw_by_direction(direction)
self:set_animation(smart_villages.animation_frames.WALK)
end
-- smart_villages.villager.get_timer get the value of a counter.
function smart_villages.villager:get_timer(timerId)
return self.time_counters[timerId]
end
-- smart_villages.villager.set_timer set the value of a counter.
function smart_villages.villager:set_timer(timerId,value)
assert(type(value)=="number","timers need to be countable")
self.time_counters[timerId]=value
end
-- smart_villages.villager.clear_timers set all counters to 0.
function smart_villages.villager:clear_timers()
for timerId,_ in pairs(self.time_counters) do
self.time_counters[timerId] = 0
end
end
-- smart_villages.villager.count_timer count a counter up by 1.
function smart_villages.villager:count_timer(timerId)
if not self.time_counters[timerId] then
minetest.log("info","timer \""..timerId.."\" was not initialized")
self.time_counters[timerId] = 0
end
self.time_counters[timerId] = self.time_counters[timerId] + 1
end
-- smart_villages.villager.count_timers count all counters up by 1.
function smart_villages.villager:count_timers()
for id, counter in pairs(self.time_counters) do
self.time_counters[id] = counter + 1
end
end
-- smart_villages.villager.timer_exceeded if a timer exceeds the limit it will be reset and true is returned
function smart_villages.villager:timer_exceeded(timerId,limit)
if self:get_timer(timerId)>=limit then
self:set_timer(timerId,0)
return true
else
return false
end
end
-- smart_villages.villager.update_infotext updates the infotext of the villager.
function smart_villages.villager:update_infotext()
local infotext = ""
local job_name = self:get_job()
if job_name ~= nil then
job_name = job_name.description
infotext = infotext .. job_name .. "\n"
else
infotext = infotext .. "this villager is inactive\nNo job\n"
end
infotext = infotext .. "[Owner] : " .. self.owner_name
if self.pause=="resting" then
infotext = infotext .. "\nthis villager is resting"
elseif self.pause=="sleeping" then
infotext = infotext .. "\nthis villager is sleeping"
elseif self.pause=="active" then
infotext = infotext .. "\nthis villager is active"
end
self.object:set_properties{infotext = infotext}
end
-- smart_villages.villager.is_near checks if the villager is withing the radius of a position
function smart_villages.villager:is_near(pos, distance)
local p = self.object:getpos()
p.y = p.y + 0.5
return vector.distance(p, pos) < distance
end
--smart_villages.villager.handle_obstacles(ignore_fence,ignore_doors)
--if the villager hits a walkable he wil jump
--if ignore_fence is false and the villager hits a door he opens it
--if ignore_fence is false the villager will not jump over fences
function smart_villages.villager:handle_obstacles(ignore_fence,ignore_doors)
local velocity = self.object:getvelocity()
--local inside_node = minetest.get_node(self.object:getpos())
--if string.find(inside_node.name,"doors:door") and not ignore_doors then
-- self:change_direction(vector.round(self.object:getpos()))
--end
if velocity.y == 0 then
local front_node = self:get_front_node()
local above_node = self:get_front()
above_node = vector.add(above_node,{x=0,y=1,z=0})
above_node = minetest.get_node(above_node)
if minetest.get_item_group(front_node.name, "fence") > 0 and not(ignore_fence) then
self:change_direction_randomly()
elseif string.find(front_node.name,"doors:door") and not(ignore_doors) then
local door = doors.get(self:get_front())
door:open()
elseif minetest.registered_nodes[front_node.name].walkable
and not(minetest.registered_nodes[above_node.name].walkable) then
self.object:setvelocity{x = velocity.x, y = 6, z = velocity.z}
end
if not ignore_doors then
local back_pos = self:get_back()
if string.find(minetest.get_node(back_pos).name,"doors:door") then
local door = doors.get(back_pos)
door:close()
end
end
end
end
-- smart_villages.villager.pickup_item pickup items placed and put it to main slot.
function smart_villages.villager:pickup_item()
local pos = self.object:getpos()
local radius = 1.0
local all_objects = minetest.get_objects_inside_radius(pos, radius)
for _, obj in ipairs(all_objects) do
if not obj:is_player() and obj:get_luaentity() and obj:get_luaentity().itemstring then
local itemstring = obj:get_luaentity().itemstring
local stack = ItemStack(itemstring)
if stack and stack:to_table() then
local name = stack:to_table().name
if minetest.registered_items[name] ~= nil then
local inv = self:get_inventory()
local leftover = inv:add_item("main", stack)
minetest.add_item(obj:getpos(), leftover)
obj:get_luaentity().itemstring = ""
obj:remove()
end
end
end
end
end
-- smart_villages.villager.is_active check if the villager is paused.
function smart_villages.villager:is_active()
return self.pause == "active"
end
dofile(smart_villages.modpath.."/async_actions.lua") --load states
function smart_villages.villager:set_state(id) --deprecated
if id == "idle" then
print("the idle state is deprecated")
elseif id == "goto_dest" then
print("use self:go_to(pos) instead of self:set_state(\"goto\")")
self:go_to(self.destination)
elseif id == "job" then
print("the job state is not nessecary anymore")
elseif id == "dig_target" then
print("use self:dig(pos) instead of self:set_state(\"dig_target\")")
self:dig(self.target)
elseif id == "place_wield" then
print("use self:place(itemname,pos) instead of self:set_state(\"place_wield\")")
local wield_stack = self:get_wield_item_stack()
self:place(wield_stack:get_name(),self.target)
end
end
---------------------------------------------------------------------
-- smart_villages.manufacturing_data represents a table that contains manufacturing data.
-- this table's keys are product names, and values are manufacturing numbers
-- that has been already manufactured.
smart_villages.manufacturing_data = (function()
local file_name = minetest.get_worldpath() .. "/smart_villages_data"
minetest.register_on_shutdown(function()
local file = io.open(file_name, "w")
file:write(minetest.serialize(smart_villages.manufacturing_data))
file:close()
end)
local file = io.open(file_name, "r")
if file ~= nil then
local data = file:read("*a")
file:close()
return minetest.deserialize(data)
end
return {}
end) ()
--------------------------------------------------------------------
-- register empty item entity definition.
-- this entity may be hold by villager's hands.
do
minetest.register_craftitem("smart_villages:dummy_empty_craftitem", {
wield_image = "smart_villages_dummy_empty_craftitem.png",
})
local function on_activate(self)
-- attach to the nearest villager.
local all_objects = minetest.get_objects_inside_radius(self.object:getpos(), 0.1)
for _, obj in ipairs(all_objects) do
local luaentity = obj:get_luaentity()
if smart_villages.is_villager(luaentity.name) then
self.object:set_attach(obj, "Arm_R", {x = 0.065, y = 0.50, z = -0.15}, {x = -45, y = 0, z = 0})
self.object:set_properties{textures={"smart_villages:dummy_empty_craftitem"}}
return
end
end
end
local function on_step(self)
local all_objects = minetest.get_objects_inside_radius(self.object:getpos(), 0.1)
for _, obj in ipairs(all_objects) do
local luaentity = obj:get_luaentity()
if smart_villages.is_villager(luaentity.name) then
local stack = luaentity:get_wield_item_stack()
if stack:get_name() ~= self.itemname then
if stack:is_empty() then
self.itemname = ""
self.object:set_properties{textures={"smart_villages:dummy_empty_craftitem"}}
else
self.itemname = stack:get_name()
self.object:set_properties{textures={self.itemname}}
end
end
return
end
end
-- if cannot find villager, delete empty item.
self.object:remove()
return
end
minetest.register_entity("smart_villages:dummy_item", {
hp_max = 1,
visual = "wielditem",
visual_size = {x = 0.025, y = 0.025},
collisionbox = {0, 0, 0, 0, 0, 0},
physical = false,
textures = {"air"},
on_activate = on_activate,
on_step = on_step,
itemname = "",
})
end
---------------------------------------------------------------------
-- smart_villages.register_job registers a definition of a new job.
function smart_villages.register_job(job_name, def, recipe)
smart_villages.registered_jobs[job_name] = def
minetest.register_tool(job_name, {
stack_max = 1,
description = def.description,
inventory_image = def.inventory_image,
})
if recipe ~= nil then
minetest.register_craft({
output = job_name,
recipe = recipe
})
end
end
-- smart_villages.register_egg registers a definition of a new egg.
function smart_villages.register_egg(egg_name, def)
smart_villages.registered_eggs[egg_name] = def
minetest.register_tool(egg_name, {
description = def.description,
inventory_image = def.inventory_image,
stack_max = 1,
on_use = function(itemstack, user, pointed_thing)
if pointed_thing.above ~= nil and def.product_name ~= nil then
-- set villager's direction.
local new_villager = minetest.add_entity(pointed_thing.above, def.product_name)
new_villager:get_luaentity():set_yaw_by_direction(
vector.subtract(user:getpos(), new_villager:getpos())
)
new_villager:get_luaentity().owner_name = user:get_player_name()
new_villager:get_luaentity():update_infotext()
itemstack:take_item()
return itemstack
end
return nil
end,
})
end
-- smart_villages.register_villager registers a definition of a new villager.
function smart_villages.register_villager(product_name, def)
smart_villages.registered_villagers[product_name] = def
-- initialize manufacturing number of a new villager.
if smart_villages.manufacturing_data[product_name] == nil then
smart_villages.manufacturing_data[product_name] = 0
end
-- create_inventory creates a new inventory, and returns it.
local function create_inventory(self)
self.inventory_name = self.product_name .. "_" .. tostring(self.manufacturing_number)
local inventory = minetest.create_detached_inventory(self.inventory_name, {
on_put = function(_, listname, _, stack) --inv, listname, index, stack, player
if listname == "job" then
local job_name = stack:get_name()
local job = smart_villages.registered_jobs[job_name]
if type(job.on_start)=="function" then
job.on_start(self)
self.job_thread = coroutine.create(job.on_step)
elseif type(job.jobfunc)=="function" then
self.job_thread = coroutine.create(job.jobfunc)
end
self:update_infotext()
end
end,
allow_put = function(_, listname, _, stack) --inv, listname, index, stack, player
-- only jobs can put to a job inventory.
if listname == "main" then
return stack:get_count()
elseif listname == "job" and smart_villages.is_job(stack:get_name()) then
return stack:get_count()
elseif listname == "wield_item" then
return 0
end
return 0
end,
on_take = function(_, listname, _, stack) --inv, listname, index, stack, player
if listname == "job" then
local job_name = stack:get_name()
local job = smart_villages.registered_jobs[job_name]
self.time_counters = {}
if job then
if type(job.on_stop)=="function" then
job.on_stop(self)
elseif type(job.jobfunc)=="function" then
self.job_thread = false
end
end
self:update_infotext()
end
end,
allow_take = function(_, listname, _, stack) --inv, listname, index, stack, player
if listname == "wield_item" then
return 0
end
return stack:get_count()
end,
on_move = function(inv, from_list, _, to_list, to_index)
--inv, from_list, from_index, to_list, to_index, count, player
if to_list == "job" or from_list == "job" then
local job_name = inv:get_stack(to_list, to_index):get_name()
local job = smart_villages.registered_jobs[job_name]
if to_list == "job" then
if type(job.on_start)=="function" then
job.on_start(self)
self.job_thread = coroutine.create(job.on_step)
elseif type(job.jobfunc)=="function" then
self.job_thread = coroutine.create(job.jobfunc)
end
elseif from_list == "job" then
if type(job.on_stop)=="function" then
job.on_stop(self)
elseif type(job.jobfunc)=="function" then
self.job_thread = false
end
end
self:update_infotext()
end
end,
allow_move = function(inv, from_list, from_index, to_list, _, count)
--inv, from_list, from_index, to_list, to_index, count, player
if to_list == "wield_item" then
return 0
end
if to_list == "main" then
return count
elseif to_list == "job" and smart_villages.is_job(inv:get_stack(from_list, from_index):get_name()) then
return count
end
return 0
end,
})
inventory:set_size("main", 16)
inventory:set_size("job", 1)
inventory:set_size("wield_item", 1)
return inventory
end
-- on_activate is a callback function that is called when the object is created or recreated.
local function on_activate(self, staticdata)
-- parse the staticdata, and compose a inventory.
if staticdata == "" then
self.product_name = product_name
self.manufacturing_number = smart_villages.manufacturing_data[product_name]
smart_villages.manufacturing_data[product_name] = smart_villages.manufacturing_data[product_name] + 1
create_inventory(self)
-- attach dummy item to new villager.
minetest.add_entity(self.object:getpos(), "smart_villages:dummy_item")
else
-- if static data is not empty string, this object has beed already created.
local data = minetest.deserialize(staticdata)
self.product_name = data["product_name"]
self.manufacturing_number = data["manufacturing_number"]
self.nametag = data["nametag"]
self.owner_name = data["owner_name"]
self.pause = data["pause"]
local inventory = create_inventory(self)
for list_name, list in pairs(data["inventory"]) do
inventory:set_list(list_name, list)
end
end
self:update_infotext()
self.object:set_nametag_attributes{
text = self.nametag
}
self.object:setvelocity{x = 0, y = 0, z = 0}
self.object:setacceleration{x = 0, y = -10, z = 0}
local job = self:get_job()
if job ~= nil then
if type(job.on_start)=="function" then
job.on_start(self)
self.job_thread = coroutine.create(job.on_step)
elseif type(job.jobfunc)=="function" then
self.job_thread = coroutine.create(job.jobfunc)
end
if self.pause == "resting" then
if type(job.on_pause)=="function" then
job.on_pause(self)
end
end
end
end
-- get_staticdata is a callback function that is called when the object is destroyed.
local function get_staticdata(self)
local inventory = self:get_inventory()
local data = {
["product_name"] = self.product_name,
["manufacturing_number"] = self.manufacturing_number,
["nametag"] = self.nametag,
["owner_name"] = self.owner_name,
["inventory"] = {},
["pause"] = self.pause,
}
-- set lists.
for list_name, list in pairs(inventory:get_lists()) do
data["inventory"][list_name] = {}
for i, item in ipairs(list) do
data["inventory"][list_name][i] = item:to_string()
end
end
return minetest.serialize(data)
end
-- on_step is a callback function that is called every delta times.
local function on_step(self, dtime)
--upate old pause state
if self.pause==true then
self.pause="resting"
elseif self.pause == false then
self.pause="active"
end
--[[ if owner didn't login, the villager does nothing.
if not minetest.get_player_by_name(self.owner_name) then
return
end--]]
-- pickup surrounding item.
self:pickup_item()
if self.pause ~= "active" and self.pause ~= "sleeping" then
return
end
local job = self:get_job()
if not job then return end
if not self.job_thread and job.on_step then
job.on_start(self)
self.job_thread = coroutine.create(job.on_step)
end
if coroutine.status(self.job_thread) == "dead" then
if job.jobfunc then
self.job_thread = coroutine.create(job.jobfunc)
else
self.job_thread = coroutine.create(job.on_step)
end
end
if coroutine.status(self.job_thread) == "suspended" then
local state, err = coroutine.resume(self.job_thread, self, dtime)
if state == false then
error("error in job_thread " .. err)
end
end
end
-- on_rightclick is a callback function that is called when a player right-click them.
local function on_rightclick(self, clicker)
local wielded_stack = clicker:get_wielded_item()
if wielded_stack:get_name() == "smart_villages:commanding_sceptre"
and clicker:get_player_name() == self.owner_name then
smart_villages.forms.show_inv_formspec(self, clicker:get_player_name())
else
smart_villages.forms.show_talking_formspec(self, clicker:get_player_name())
end
self:update_infotext()
end
-- on_punch is a callback function that is called when a player punch then.
local function on_punch()--self, puncher, time_from_last_punch, tool_capabilities, dir
--TODO: aggression
end
-- register a definition of a new villager.
local villager_def = table.copy(smart_villages.villager)
-- basic initial properties
villager_def.hp_max = def.hp_max
villager_def.weight = def.weight
villager_def.mesh = def.mesh
villager_def.textures = def.textures
villager_def.physical = true
villager_def.visual = "mesh"
villager_def.visual_size = {x = 1, y = 1}
villager_def.collisionbox = {-0.25, 0, -0.25, 0.25, 1.75, 0.25}
villager_def.is_visible = true
villager_def.makes_footstep_sound = true
villager_def.infotext = ""
villager_def.nametag = ""
-- extra initial properties
villager_def.pause = "active"
villager_def.state = "job"
villager_def.job_thread = false
villager_def.product_name = ""
villager_def.manufacturing_number = -1
villager_def.owner_name = ""
villager_def.time_counters = {}
villager_def.destination = vector.new(0,0,0)
-- callback methods
villager_def.on_activate = on_activate
villager_def.on_step = on_step
villager_def.on_rightclick = on_rightclick
villager_def.on_punch = on_punch
villager_def.get_staticdata = get_staticdata
-- home methods
villager_def.get_home = smart_villages.get_home
villager_def.has_home = smart_villages.is_valid_home
minetest.register_entity(product_name, villager_def)
-- register villager egg.
smart_villages.register_egg(product_name .. "_egg", {
description = product_name .. " egg",
inventory_image = def.egg_image,
product_name = product_name,
})
end
function smart_villages.random_texture(...)
math.randomseed(os.time())
local args = { ... }
return args[math.random(1, #args)]
-- body
end