mirror of
https://gitlab.com/niansa/storybuilder.git
synced 2025-03-06 20:48:28 +01:00
Compare commits
21 commits
Author | SHA1 | Date | |
---|---|---|---|
|
69f1e614e3 | ||
|
543618251d | ||
|
71dc47ea1f | ||
|
a1bb16aec8 | ||
|
69ee57c20b | ||
|
fa9f7d1e9f | ||
|
5ea9576505 | ||
|
69df8fbdb7 | ||
|
761fa881c6 | ||
|
e7e69d44fe | ||
|
47ba33a418 | ||
|
a04e2396cc | ||
|
9eb48525d4 | ||
|
1c3f1042a2 | ||
|
e7f813f168 | ||
|
017e4f9bb7 | ||
|
5681fa4cc2 | ||
|
785dd8d75f | ||
|
ba28918201 | ||
|
91b77d4716 | ||
|
7c736e9f41 |
23 changed files with 2413 additions and 141 deletions
|
@ -1,7 +1,7 @@
|
|||
Storybuilder is a game written to create entire stories in Minetest.
|
||||
|
||||
Notes:
|
||||
* This game is in early deveopment, don't expect too much yet
|
||||
* This game is in stable state, but still expect many changes in the future
|
||||
* This game is very WIP, so breaking changes could suddenly be made!
|
||||
* The mod "minimal" included in this game is a renamed version of "Minetest Minimal Development Test"
|
||||
* Some thirdparty mods are included into `mods/thirdparty`
|
||||
* Some thirdparty mods are included into `mods/thirdparty`
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 392 B After Width: | Height: | Size: 1.6 MiB |
BIN
menu/icon.png
BIN
menu/icon.png
Binary file not shown.
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 568 KiB |
|
@ -21,7 +21,7 @@ minetest.register_node("astrid:rainbow", {
|
|||
})
|
||||
|
||||
minetest.register_node("astrid:bluekitty", {
|
||||
description = "bluekitty",
|
||||
description = "Bluekitty",
|
||||
tiles = {"astrid_bluekitty.png"},
|
||||
is_ground_content = false,
|
||||
groups = {cracky=2},
|
||||
|
|
|
@ -15,6 +15,8 @@ end
|
|||
function bgmusic.stop(name)
|
||||
if pmusic.id[name] then
|
||||
minetest.sound_stop(pmusic.id[name])
|
||||
pmusic.title[name] = nil
|
||||
pmusic.id[name] = nil
|
||||
return true
|
||||
end
|
||||
return false
|
||||
|
|
|
@ -100,24 +100,10 @@ minetest.register_chatcommand("setscore", {
|
|||
minetest.register_chatcommand("setstage", {
|
||||
privs = "cheat",
|
||||
func = function(name, param)
|
||||
storybase.set_stage(name, tonumber(param))
|
||||
storybase.set_stage(name, param)
|
||||
return true, "Your new stage: " .. param
|
||||
end
|
||||
})
|
||||
minetest.register_chatcommand("setextrastage", {
|
||||
privs = "cheat",
|
||||
func = function(name, param)
|
||||
storybase.set_stage(name, tonumber(param), true)
|
||||
return true, "Your new extra stage: " .. param
|
||||
end
|
||||
})
|
||||
minetest.register_chatcommand("unsetextrastage", {
|
||||
privs = "cheat",
|
||||
func = function(name, param)
|
||||
storybase.set_stage(name, nil, true)
|
||||
return true, "Your new extra stage: none"
|
||||
end
|
||||
})
|
||||
minetest.register_chatcommand("incscore", {
|
||||
privs = "cheat",
|
||||
func = function(name, param)
|
||||
|
|
100
mods/entities/init.lua
Normal file
100
mods/entities/init.lua
Normal file
|
@ -0,0 +1,100 @@
|
|||
-- Partitially taken from: https://github.com/TheTermos/wildlife/blob/1c66c50392637d5bb05e4742a707e704c4d79719/init.lua
|
||||
local function lava_dmg(self,dmg)
|
||||
node_lava = node_lava or minetest.registered_nodes[minetest.registered_aliases.mapgen_lava_source]
|
||||
if node_lava then
|
||||
local pos=self.object:get_pos()
|
||||
local box = self.object:get_properties().collisionbox
|
||||
local pos1={x=pos.x+box[1],y=pos.y+box[2],z=pos.z+box[3]}
|
||||
local pos2={x=pos.x+box[4],y=pos.y+box[5],z=pos.z+box[6]}
|
||||
local nodes=mobkit.get_nodes_in_area(pos1,pos2)
|
||||
if nodes[node_lava] then mobkit.hurt(self,dmg) end
|
||||
end
|
||||
end
|
||||
|
||||
local function predator_brain(self)
|
||||
-- vitals should be checked every step
|
||||
if mobkit.timer(self,1) then lava_dmg(self,6) end
|
||||
mobkit.vitals(self)
|
||||
-- if self.object:get_hp() <=100 then
|
||||
if self.hp <= 0 then
|
||||
-- cease all activity
|
||||
mobkit.clear_queue_high(self)
|
||||
-- Let the entity go to heaven or hell
|
||||
mobkit.hq_die(self)
|
||||
return
|
||||
end
|
||||
|
||||
-- decision making needn't happen every engine step
|
||||
if mobkit.timer(self,1) then
|
||||
local prty = mobkit.get_queue_priority(self)
|
||||
|
||||
if prty < 20 and self.isinliquid then
|
||||
mobkit.hq_liquid_recovery(self,20)
|
||||
return
|
||||
end
|
||||
|
||||
local pos=self.object:get_pos()
|
||||
|
||||
-- hunt if not busy with anything important
|
||||
if prty < 10 then
|
||||
-- look for prey
|
||||
local prey = mobkit.get_nearby_player(self)
|
||||
if prey then
|
||||
-- and chase it
|
||||
mobkit.hq_hunt(self,10,prey)
|
||||
end
|
||||
end
|
||||
|
||||
-- fool around
|
||||
if mobkit.is_queue_empty_high(self) then
|
||||
mobkit.hq_roam(self,0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("entities:test",{
|
||||
-- common props
|
||||
physical = true,
|
||||
stepheight = 0.1, --EVIL!
|
||||
collide_with_objects = true,
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 0.7, 0.3},
|
||||
visual = "mesh",
|
||||
mesh = "wolf.b3d",
|
||||
textures = {"kit_wolf.png"},
|
||||
visual_size = {x = 1.3, y = 1.3},
|
||||
static_save = true,
|
||||
makes_footstep_sound = true,
|
||||
on_step = mobkit.stepfunc, -- required
|
||||
on_activate = mobkit.actfunc, -- required
|
||||
get_staticdata = mobkit.statfunc,
|
||||
-- api props
|
||||
springiness=0,
|
||||
buoyancy = 0.75, -- portion of hitbox submerged
|
||||
max_speed = 5,
|
||||
jump_height = 1.26,
|
||||
view_range = 24,
|
||||
lung_capacity = 10, -- seconds
|
||||
max_hp = 14,
|
||||
timeout=600,
|
||||
attack={range=0.5,damage_groups={fleshy=7}},
|
||||
sounds = {
|
||||
attack='dogbite',
|
||||
warn = 'angrydog',
|
||||
},
|
||||
brainfunc = predator_brain,
|
||||
|
||||
on_punch=function(self, puncher, time_from_last_punch, tool_capabilities, dir)
|
||||
if mobkit.is_alive(self) then
|
||||
local hvel = vector.multiply(vector.normalize({x=dir.x,y=0,z=dir.z}),4)
|
||||
self.object:set_velocity({x=hvel.x,y=2,z=hvel.z})
|
||||
|
||||
mobkit.hurt(self,tool_capabilities.damage_groups.fleshy or 1)
|
||||
|
||||
if type(puncher)=='userdata' and puncher:is_player() then -- if hit by a player
|
||||
mobkit.clear_queue_high(self) -- abandon whatever they've been doing
|
||||
mobkit.hq_hunt(self,10,puncher) -- get revenge
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
})
|
BIN
mods/entities/models/wolf.b3d
Normal file
BIN
mods/entities/models/wolf.b3d
Normal file
Binary file not shown.
BIN
mods/entities/textures/kit_wolf.png
Normal file
BIN
mods/entities/textures/kit_wolf.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
21
mods/mobkit/LICENSE
Normal file
21
mods/mobkit/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 TheTermos
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
1588
mods/mobkit/init.lua
Normal file
1588
mods/mobkit/init.lua
Normal file
File diff suppressed because it is too large
Load diff
545
mods/mobkit/mobkit_api.txt
Normal file
545
mods/mobkit/mobkit_api.txt
Normal file
|
@ -0,0 +1,545 @@
|
|||
Contents
|
||||
|
||||
1 Concepts
|
||||
1.1 Behavior functions
|
||||
1.1.1 Low level functions
|
||||
1.1.2 High level functions
|
||||
1.1.2.1 Priority
|
||||
1.2 Logic function
|
||||
1.3 Processing diagram
|
||||
1.4 Entity definition
|
||||
1.5 Exposed luaentity members
|
||||
|
||||
2 Reference
|
||||
2.1 Utility functions
|
||||
2.2 Built in behaviors
|
||||
2.2.1 High level behaviors
|
||||
2.2.2 Low level behaviors
|
||||
2.3 Constants and member variables
|
||||
|
||||
-----------
|
||||
1. Concepts
|
||||
-----------
|
||||
|
||||
1.1 Behavior functions
|
||||
|
||||
These are the most fundamental units of code, every action entities can perform is a separate function.
|
||||
There are two types of behaviors:
|
||||
- low level, these govern physical actions and interactions (think moves)
|
||||
- high level, these are logical structures governing low level behaviors in order to perform more complex tasks
|
||||
|
||||
Behaviors run for considerable amount of time, this means the functions are being called repeatedly on consecutive engine steps.
|
||||
Therefore a need for preserving state between calls, this is why they are implemented as closures, see defining conventions for details.
|
||||
|
||||
Behavior functions are active until they finish the job, are removed from the queue or superseded by a higher priority behavior.
|
||||
They signal finished state by returning true, therefore it's very important to carefully design the completion conditions
|
||||
|
||||
For a behavior to begin executing it has to be put on a queue. There are two separate queues, one for low and one for high level behaviors.
|
||||
Queuing is covered by behavour defining conventions
|
||||
|
||||
!!! In simplest scenarios there's no need to code behaviors, much can be achieved using only built-in stuff !!!
|
||||
!!! To start using the api it's enough to learn defining mobs and writing brain functions !!!
|
||||
|
||||
|
||||
1.1.1 Low level behavior functions
|
||||
|
||||
These are physical actions and interactions: steps, jumps, turns etc. here you'll set velocity, yaw, kick off animations and sounds.
|
||||
|
||||
Low level behavior definition:
|
||||
|
||||
function mobkit.lq_bhv1(self,[optional additional persistent parameters]) -- enclosing function
|
||||
... -- optional definitions of additional persistent variables
|
||||
local func=function(self) -- enclosed function, self is mandatory and the only allowed parameter
|
||||
... -- actual function definition, remember to return true eventually
|
||||
end
|
||||
mobkit.queue_low(self,func) -- this will queue the behavior at the time of lq_bhv1 call
|
||||
end
|
||||
|
||||
|
||||
1.1.2 High level behavior functions
|
||||
|
||||
These are complex tasks like getting to a position, following other objects, hiding, patrolling an area etc.
|
||||
Their job is tracking changes in the environment and managing low level behavior queue accordingly.
|
||||
|
||||
High level behavior definition:
|
||||
|
||||
function mobkit.hq_bhv1(self,priority,[optional additional persistent parameters]) -- enclosing function
|
||||
... -- optional definitions of additional persistent variables
|
||||
local func=function(self) -- enclosed function, self is mandatory and the only allowed parameter
|
||||
... -- actual function definition, remember to return true eventually
|
||||
end
|
||||
mobkit.queue_high(self,func,priority) -- this will queue the behavior at the time of hq_bhv1 call
|
||||
end
|
||||
|
||||
|
||||
1.1.2.1 Priority
|
||||
|
||||
Unlike low level behaviors which are executed in FIFO order, high level behaviors support prioritization.
|
||||
This concept is essential for making sure the right behavior is active at the right time.
|
||||
Prioritization is what makes it possible to interrupt a task in order to perform a more important one
|
||||
|
||||
The currently executing behavior is always the first in the queue.
|
||||
When a new behavior is placed onto the queue:
|
||||
If the queue is not empty a new behavior is inserted before the first behavior of lower priority if such exists, or last.
|
||||
If the new behavior supersedes the one currently executing, low level queue is purged immediately.
|
||||
|
||||
Common idioms:
|
||||
|
||||
hq_bhv1(self,prty):
|
||||
...
|
||||
hq_bhv2(self,prty) -- bhv1 kicks off bhv2 with equal priority
|
||||
return true -- and ends,
|
||||
-- bhv2 becomes active on the next engine step.
|
||||
|
||||
hq_bhv1(self,prty):
|
||||
...
|
||||
hq_bhv2(self,prty+1) -- bhv1 kicks off bhv2 with higher priority
|
||||
-- bhv2 takes over and when it ends, bhv1 resumes.
|
||||
|
||||
|
||||
Particular prioritization scheme is to be designed by the user according to specific mod requirements.
|
||||
|
||||
|
||||
1.2 Logic function
|
||||
------------------
|
||||
Every mob must have one.
|
||||
Its job is managing high level behavior queue in response to events which are not intercepted by callbacks.
|
||||
Contrary to what the name suggests, these functions needn't necessarily be too complex thanks to their limited responsibilities.
|
||||
|
||||
Typical flow might look like this:
|
||||
|
||||
if mobkit.timer(self,1) then -- returns true approx every second
|
||||
local prty = mobkit.get_queue_priority(self)
|
||||
|
||||
if prty < 20
|
||||
if ... then
|
||||
hq_do_important_stuff(self,20)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if prty < 10 then
|
||||
if ... then
|
||||
hq_do_something_else(self,10)
|
||||
return
|
||||
elseif ... then
|
||||
hq_do_this_instead(self,10)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if mobkit.is_queue_empty_high(self) then
|
||||
hq_fool_around(self,0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
1.3 Processing diagram
|
||||
----------------------
|
||||
|
||||
---------------------------------------
|
||||
| PHYSICS |
|
||||
| |
|
||||
| ----------------------- |
|
||||
| | Logic Function | |
|
||||
| ----------------------- |
|
||||
| | |
|
||||
| -----|----------------- |
|
||||
| | V HL Queue | |
|
||||
| | | 1 | 2 | 3 |... | |
|
||||
| ----------------------- |
|
||||
| | |
|
||||
| -----|----------------- |
|
||||
| | V LL Queue | |
|
||||
| | | 1 | 2 | 3 |... | |
|
||||
| ----------------------- |
|
||||
| |
|
||||
---------------------------------------
|
||||
|
||||
Order of execution during an engine step:
|
||||
First comes physics: gravity, buoyancy, friction etc., then the logic function is called.
|
||||
After that, the first behavior on the high level queue, if exists,
|
||||
and the last, the first low level behavior if present.
|
||||
|
||||
1.4 Entity definition
|
||||
---------------------
|
||||
|
||||
minetest.register_entity("mod:name",{
|
||||
|
||||
-- required minetest api props
|
||||
|
||||
physical = true,
|
||||
collide_with_objects = true,
|
||||
collisionbox = {...},
|
||||
visual = "mesh",
|
||||
mesh = "...",
|
||||
textures = {...},
|
||||
|
||||
|
||||
-- required mobkit props
|
||||
|
||||
timeout = [num], -- entities are removed after this many seconds inactive
|
||||
-- 0 is never
|
||||
-- mobs having memory entries are not affected
|
||||
|
||||
buoyancy = [num], -- (0,1) - portion of collisionbox submerged
|
||||
-- = 1 - controlled buoyancy (fish, submarine)
|
||||
-- > 1 - drowns
|
||||
-- < 0 - MC like water trampolining
|
||||
|
||||
lung_capacity = [num], -- seconds
|
||||
max_hp = [num],
|
||||
on_step = mobkit.stepfunc,
|
||||
on_activate = mobkit.actfunc,
|
||||
get_staticdata = mobkit.statfunc,
|
||||
logic = [function user defined], -- older 'brainfunc' name works as well.
|
||||
|
||||
-- optional mobkit props
|
||||
-- or used by built in behaviors
|
||||
physics = [function user defined] -- optional, overrides built in physics
|
||||
animation = {
|
||||
[name]={range={x=[num],y=[num]},speed=[num],loop=[bool]}, -- single
|
||||
|
||||
[name]={ -- variant, animations are chosen randomly.
|
||||
{range={x=[num],y=[num]},speed=[num],loop=[bool]},
|
||||
{range={x=[num],y=[num]},speed=[num],loop=[bool]},
|
||||
...
|
||||
}
|
||||
...
|
||||
}
|
||||
sounds = {
|
||||
[name] = [string filename], --single, simple,
|
||||
|
||||
[name] = { --single, more powerful. All fields but 'name' are optional
|
||||
name = [string filename],
|
||||
gain=[num or range], --range is a table of the format {left_bound, right_bound}
|
||||
fade=[num or range],
|
||||
pitch=[num or range],
|
||||
},
|
||||
|
||||
[name] = { --variant, sound is chosen randomly
|
||||
{
|
||||
name = [string filename],
|
||||
gain=[num or range],
|
||||
fade=[num or range],
|
||||
pitch=[num or range],
|
||||
},
|
||||
{
|
||||
name = [string filename],
|
||||
gain=[num or range],
|
||||
fade=[num or range],
|
||||
pitch=[num or range],
|
||||
},
|
||||
...
|
||||
},
|
||||
...
|
||||
},
|
||||
max_speed = [num], -- m/s
|
||||
jump_height = [num], -- nodes/meters
|
||||
view_range = [num], -- nodes/meters
|
||||
attack={range=[num], -- range is distance between attacker's collision box center
|
||||
damage_groups={fleshy=[num]}}, -- and the tip of the murder weapon in nodes/meters
|
||||
armor_groups = {fleshy=[num]}
|
||||
})
|
||||
|
||||
1.5 Exposed luaentity members
|
||||
|
||||
Some frequently used entity fields to be accessed directly for convenience
|
||||
|
||||
self.dtime -- max(dtime as passed to on_step,0.5) - limit of 0.05 to prevent jerkines on long steps.
|
||||
self.hp -- hitpoints
|
||||
self.isonground -- true if pos.y remains unchanged for 2 consecutive steps
|
||||
self.isinliquid -- true if the node at foot level is drawtype=='liquid'
|
||||
|
||||
------------
|
||||
2. Reference
|
||||
------------
|
||||
|
||||
2.1 Utility Functions
|
||||
|
||||
function mobkit.minmax(v,m)
|
||||
-- v,n: numbers
|
||||
-- returns v trimmed to <-m,m> range
|
||||
|
||||
function mobkit.get_terrain_height(pos,steps)
|
||||
-- recursively search for walkable surface at pos.
|
||||
-- steps (optional) is how far from pos it gives up, expressed in nodes, default 3
|
||||
-- Returns:
|
||||
-- surface height at pos, or nil if not found
|
||||
-- liquid flag: true if found surface is covered with liquid
|
||||
|
||||
function mobkit.turn2yaw(self,tyaw,rate)
|
||||
-- gradually turns towards yaw
|
||||
-- self: luaentity
|
||||
-- tyaw: target yaw in radians
|
||||
-- rate: turn rate in rads/s
|
||||
--returns: true if facing tyaw; current yaw
|
||||
|
||||
function mobkit.timer(self,s)
|
||||
-- returns true approx every s seconds
|
||||
-- used to reduce execution of code that needn't necessarily be done on every engine step
|
||||
|
||||
function mobkit.pos_shift(pos,vec)
|
||||
-- convenience function
|
||||
-- returns pos shifted by vec
|
||||
-- vec needn't have all three components given, absent components are assumed zero.
|
||||
-- e.g pos_shift(pos,{y=1}) is valid
|
||||
|
||||
function mobkit.pos_translate2d(pos,yaw,dist)
|
||||
-- returns pos translated in the yaw direction by dist
|
||||
|
||||
function mobkit.get_stand_pos(thing)
|
||||
-- returns object pos projected onto the bottom collisionbox face
|
||||
-- thing can be luaentity or objectref.
|
||||
|
||||
function mobkit.nodeatpos(pos)
|
||||
-- convenience function
|
||||
-- returns nodedef or nil if it's an ignore node
|
||||
|
||||
function mobkit.get_node_pos(pos)
|
||||
-- returns center of the node that pos is inside
|
||||
|
||||
function mobkit.get_nodes_in_area(pos1,pos2,[full])
|
||||
-- in basic mode returns a table of unique nodes within area indexed by node
|
||||
-- in full=true mode returns a table of nodes indexed by pos
|
||||
-- works for up to 125 nodes.
|
||||
|
||||
function mobkit.isnear2d(p1,p2,thresh)
|
||||
-- returns true if pos p2 is within a square with center at pos p1 and radius thresh
|
||||
-- y components are ignored
|
||||
|
||||
function mobkit.is_there_yet2d(pos,dir,dest) -- obj positon; facing vector; destination position
|
||||
-- returns true if a position dest is behind position pos according to facing vector dir
|
||||
-- (checks if dest is in the rear half plane as defined by pos and dir)
|
||||
-- y components are ignored
|
||||
|
||||
function mobkit.isnear3d(p1,p2,thresh)
|
||||
-- returns true if pos p2 is within a cube with center at pos p1 and radius thresh
|
||||
|
||||
function mobkit.dir_to_rot(v,rot)
|
||||
-- converts a 3d vector v to rotation like in set_rotation() object method
|
||||
-- rot (optional) is current object rotation
|
||||
|
||||
function mobkit.rot_to_dir(rot)
|
||||
-- converts minetest rotation vector (pitch,yaw,roll) to direction unit vector
|
||||
|
||||
function mobkit.is_alive(thing)
|
||||
-- non essential, checks if thing exists in the world and is alive
|
||||
-- makes an assumption that luaentities are considered dead when their hp < 100
|
||||
-- thing can be luaentity or objectref.
|
||||
-- used for stored luaentities and objectrefs
|
||||
|
||||
function mobkit.exists(thing)
|
||||
-- checks if thing exists in the world
|
||||
-- thing can be luaentity or objectref.
|
||||
-- used for stored luaentities and objectrefs
|
||||
|
||||
function mobkit.hurt(luaent,dmg)
|
||||
-- decrease luaent.hp by dmg
|
||||
|
||||
function mobkit.heal(luaent,dmg)
|
||||
-- increase luaent.hp by dmg
|
||||
|
||||
function mobkit.get_spawn_pos_abr(dtime,intrvl,radius,chance,reduction)
|
||||
-- returns a potential spawn position at random intervals
|
||||
-- intrvl: avg spawn attempt interval for every player
|
||||
-- radius: spawn distance in nodes, active_block_range*16 is recommended
|
||||
-- chance: (0,1) chance to spawn a mob if there are no other objects in area
|
||||
-- reduction: (0,1) spawn chance is reduced by this factor for every object in range.
|
||||
--usage:
|
||||
minetest.register_globalstep(function(dtime)
|
||||
local spawnpos = mobkit.get_spawn_pos_abr(...)
|
||||
if spawnpos then
|
||||
... -- mod/game specific logic
|
||||
end
|
||||
end)
|
||||
|
||||
function mobkit.animate(self,anim)
|
||||
-- makes an entity play an animation of name anim, or does nothing if not defined
|
||||
-- anim is string, see entity definition
|
||||
-- does nothing if the same animation is already running
|
||||
|
||||
function mobkit.make_sound(self,sound)
|
||||
-- sound is string, see entity definition
|
||||
-- makes an entity play sound, or does nothing if not defined
|
||||
--returns sound handle
|
||||
|
||||
function mobkit.go_forward_horizontal(self,speed)
|
||||
-- sets an entity's horizontal velocity in yaw direction. Vertical velocity unaffected.
|
||||
|
||||
function mobkit.drive_to_pos(self,tpos,speed,turn_rate,dist)
|
||||
-- moves in facing direction while gradually turning towards tpos, returns true if in dist distance from tpos
|
||||
-- tpos: target position
|
||||
-- speed: in m/s
|
||||
-- turn_rate: in rad/s
|
||||
-- dist: in m.
|
||||
|
||||
-- Memory functions.
|
||||
|
||||
This represents mob long term memory
|
||||
Warning: Stuff in memory is serialized, never try to remember objectrefs or tables referencing them
|
||||
or the engine will crash.
|
||||
|
||||
function mobkit.remember(self,key,val)
|
||||
-- premanently store a key, value pair
|
||||
function mobkit.forget(self,key)
|
||||
-- clears a memory entry
|
||||
function mobkit.recall(self,key)
|
||||
-- returns val associated with key
|
||||
|
||||
-- Queue functions
|
||||
|
||||
function mobkit.queue_high(self,func,priority)
|
||||
-- only for use in behavior definitions, see 1.1.2
|
||||
|
||||
function mobkit.queue_low(self,func)
|
||||
-- only for use in behavior definitions, see 1.1.1
|
||||
|
||||
|
||||
function mobkit.clear_queue_high(self)
|
||||
function mobkit.clear_queue_low(self)
|
||||
|
||||
function mobkit.is_queue_empty_high(self)
|
||||
function mobkit.is_queue_empty_low(self)
|
||||
|
||||
function mobkit.get_queue_priority(self)
|
||||
-- returns the priority of currently running behavior
|
||||
-- this is also the highest of all queued behaviors
|
||||
|
||||
|
||||
-- Use these inside logic functions --
|
||||
|
||||
function mobkit.vitals(self)
|
||||
-- default drowning and fall damage, call it before hp check
|
||||
function mobkit.get_nearby_player(self)
|
||||
-- returns random player if nearby or nil
|
||||
function mobkit.get_nearby_entity(self,name)
|
||||
-- returns random nearby entity of name or nil
|
||||
function mobkit.get_closest_entity(self,name)
|
||||
-- returns closest entity of name or nil
|
||||
|
||||
|
||||
-- Misc
|
||||
|
||||
Neighbors structure represents a node's horizontal neighbors
|
||||
Not essential, used by some built in behaviors
|
||||
Custom behaviors may not need it.
|
||||
|
||||
Neighbor #1 is offset {x=1,z=0}, subsequent numbers go clockwise
|
||||
|
||||
function mobkit.dir2neighbor(dir)
|
||||
-- converts a 3d vector to neighbor number, y component ignored
|
||||
|
||||
function mobkit.neighbor_shift(neighbor,shift)
|
||||
-- get another neighbor number relative to the given, shift: plus is clockwise, minus the opposite
|
||||
-- 1,1 = 2; 1,-2 = 7
|
||||
|
||||
|
||||
2.2 Built in behaviors
|
||||
|
||||
function mobkit.goto_next_waypoint(self,tpos)
|
||||
-- this functions groups common operations making mobs move in a specific direction
|
||||
-- not a behavior itself, but is used by some built in HL behaviors
|
||||
-- which use node by node movement algorithm
|
||||
|
||||
2.2.1 High Level Behaviors --
|
||||
|
||||
function mobkit.hq_roam(self,prty)
|
||||
-- slow random roaming
|
||||
-- never returns
|
||||
|
||||
function mobkit.hq_follow(self,prty,tgtobj)
|
||||
-- follow the tgtobj
|
||||
-- returns if tgtobj becomes inactive
|
||||
|
||||
function mobkit.hq_goto(self,prty,tpos)
|
||||
-- go to tpos position
|
||||
-- returns on arrival
|
||||
|
||||
function mobkit.hq_runfrom(self,prty,tgtobj)
|
||||
-- run away from tgtobj object
|
||||
-- returns when tgtobj far enough
|
||||
|
||||
function mobkit.hq_hunt(self,prty,tgtobj)
|
||||
-- follow tgtobj and when close enough, kick off hq_attack
|
||||
-- returns when tgtobj too far
|
||||
|
||||
function mobkit.hq_warn(self,prty,tgtobj)
|
||||
-- when a tgtobj close by, turn towards them and make the 'warn' sound
|
||||
-- kick off hq_hunt if tgtobj too close or timer expired
|
||||
-- returns when tgtobj moves away
|
||||
|
||||
function mobkit.hq_die(self)
|
||||
-- default death, rotate and remove() after set time
|
||||
|
||||
function mobkit.hq_attack(self,prty,tgtobj)
|
||||
-- default attack, turns towards tgtobj and leaps
|
||||
-- returns when tgtobj out of range
|
||||
|
||||
function mobkit.hq_liquid_recovery(self,prty)
|
||||
-- use when submerged in liquid, scan for nearest land
|
||||
-- if land is found within view_range, kick off hq_swimto
|
||||
-- otherwise die
|
||||
|
||||
function mobkit.hq_swimto(self,prty,tpos)
|
||||
-- swim towards the position tpos, jump if necessary
|
||||
-- returns if standing firmly on dry land
|
||||
|
||||
Aquatic behaviors:
|
||||
|
||||
Macros:
|
||||
function aqua_radar_dumb(pos,yaw,range,reverse)
|
||||
-- assumes a mob will avoid shallows
|
||||
-- checks if a pos in front of a moving entity swimmable
|
||||
-- otherwise returns new position
|
||||
|
||||
function mobkit.is_in_deep(target)
|
||||
-- checks if an object is in water at least 2 nodes deep
|
||||
|
||||
Hq Behaviors:
|
||||
function mobkit.hq_aqua_roam(self,prty,speed)
|
||||
function mobkit.hq_aqua_attack(self,prty,tgtobj,speed)
|
||||
function mobkit.hq_aqua_turn(self,prty,tyaw,speed)
|
||||
-- used by both previous bhv
|
||||
|
||||
2.2.2 Low Level Behaviors --
|
||||
|
||||
function mobkit.lq_turn2pos(self,tpos)
|
||||
-- gradually turn towards tpos position
|
||||
-- returns when facing tpos
|
||||
|
||||
function mobkit.lq_idle(self,duration)
|
||||
-- do nothing for duration seconds
|
||||
-- set 'stand' animation
|
||||
|
||||
function mobkit.lq_dumbwalk(self,dest,speed_factor)
|
||||
-- simply move towards dest
|
||||
-- set 'walk' animation
|
||||
|
||||
function mobkit.lq_dumbjump(self,height)
|
||||
-- if standing on the ground, jump in the facing direction
|
||||
-- height is relative to feet level
|
||||
-- set 'stand' animation
|
||||
|
||||
function mobkit.lq_freejump(self)
|
||||
-- unconditional jump in the facing direction
|
||||
-- useful e.g for getting out of water
|
||||
-- returns when the apex has been reached
|
||||
|
||||
function mobkit.lq_jumpattack(self,height,target)
|
||||
-- jump towards the target, punch if a hit
|
||||
-- returns after punch or on the ground
|
||||
|
||||
function mobkit.lq_fallover(self)
|
||||
-- gradually rotates Z = 0 to pi/2
|
||||
|
||||
|
||||
2.3 Constants and member variables --
|
||||
|
||||
mobkit.gravity = -9.8
|
||||
mobkit.friction = 0.4 -- inert entities will slow down when in contact with the ground
|
||||
-- the smaller the number, the greater the effect
|
||||
|
||||
self.dtime -- for convenience, dtime as passed to currently executing on_step()
|
||||
self.isonground -- true if y velocity is 0 for at least two succesive steps
|
||||
self.isinliquid -- true if feet submerged in liquid type=source
|
2
mods/mobkit/mod.conf
Normal file
2
mods/mobkit/mod.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
name = mobkit
|
||||
description = Entity API
|
BIN
mods/mobkit/screenshot.png
Normal file
BIN
mods/mobkit/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
|
@ -2,10 +2,8 @@
|
|||
-- Normal drawtype if buildmode is enabled otherwise airlike
|
||||
if buildmode == true then
|
||||
borderdrawtype = "glasslike"
|
||||
borderwalkable = false
|
||||
else
|
||||
borderdrawtype = "airlike"
|
||||
borderwalkable = true
|
||||
end
|
||||
-- Register border nodes
|
||||
minetest.register_node("story:border", {
|
||||
|
@ -15,7 +13,7 @@ minetest.register_node("story:border", {
|
|||
drawtype = borderdrawtype,
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
walkable = borderwalkable,
|
||||
walkable = not buildmode,
|
||||
pointable = buildmode,
|
||||
diggable = buildmode,
|
||||
buildable_to = false,
|
||||
|
|
|
@ -1,23 +1 @@
|
|||
minetest.register_chatcommand("goto_extrastage", {
|
||||
privs = "interact",
|
||||
func = function(name, param)
|
||||
if param == "" then
|
||||
return false, "You have to pass a valid extrastage!"
|
||||
end
|
||||
if not story.get_stage(param) then
|
||||
return false, param.." does not exist... :("
|
||||
end
|
||||
storybase.set_stage(name, param, true)
|
||||
story.reload_stage(name)
|
||||
return true
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("leave_extrastage", {
|
||||
privs = "interact",
|
||||
func = function(name, param)
|
||||
storybase.set_stage(name, nil, true)
|
||||
story.reload_stage(name)
|
||||
return true
|
||||
end
|
||||
})
|
||||
-- Nothing (yet)
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
-- Normal drawtype if buildmode is enabled otherwise airlike
|
||||
if buildmode == true then
|
||||
nodedrawtype = "glasslike"
|
||||
else
|
||||
nodedrawtype = "airlike"
|
||||
end
|
||||
|
||||
-- Checkpoint
|
||||
minetest.register_node("story:checkpoint", {
|
||||
description = "Checkpoint",
|
||||
tiles ={"story_arrowpoint.png"},
|
||||
groups = {},
|
||||
on_punch = function(pos, node, player)
|
||||
if buildmode == true then
|
||||
minetest.node_dig(pos, node, player)
|
||||
return
|
||||
end
|
||||
local name = player:get_player_name()
|
||||
local rel_pos = story.get_player_relative_pos(name)
|
||||
story.set_checkpoint(name, true, rel_pos["x"], rel_pos["y"], rel_pos["z"])
|
||||
return true
|
||||
end
|
||||
groups = {crumbly=3, soil=1},
|
||||
|
||||
drawtype = nodedrawtype,
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
air_equivalent = true,
|
||||
pointable = buildmode,
|
||||
diggable = buildmode
|
||||
})
|
||||
|
||||
-- Ice
|
||||
|
|
|
@ -16,6 +16,12 @@ function story.get_stage(stage)
|
|||
return false
|
||||
end
|
||||
end
|
||||
function story.clean_stagevars()
|
||||
-- spos = nil (TODO: find out why this is causing weird bugs)
|
||||
we_file = nil
|
||||
music = nil
|
||||
minstage = nil
|
||||
end
|
||||
function story.get_player_relative_pos(name)
|
||||
local object = minetest.get_player_by_name(name)
|
||||
positions = object:get_pos()
|
||||
|
@ -33,29 +39,28 @@ function story.reload_stage(name, continuation)
|
|||
local object = minetest.get_player_by_name(name)
|
||||
-- Load informations about the stage
|
||||
local stage = storybase.get_stage(name)
|
||||
-- Stop playing music (if old music ~= new music)
|
||||
if music ~= bgmusic.playing("title", name) then
|
||||
bgmusic.stop(name)
|
||||
end
|
||||
-- Get stage positions
|
||||
storybase.debugmsg(name, "Loading stage " .. stage .. "...")
|
||||
if not story.get_stage(stage) then
|
||||
storybase.crashmsg(name, "reload_stage", "No such stage", stage)
|
||||
return false
|
||||
end
|
||||
-- Check if player is allowed to enter that stage
|
||||
if minstage and storybase.get_stage(name, true) < minstage then
|
||||
minetest.chat_send_player(name, 'You are not yet allowed to enter this extra stage!')
|
||||
return
|
||||
-- Stop playing music (if old music ~= new music)
|
||||
if music ~= bgmusic.playing("title", name) then
|
||||
bgmusic.stop(name)
|
||||
end
|
||||
-- Show loading texhud and teleport player to random position
|
||||
local loadinghud = playertools.texhud_add(object, "stage_loading.png", true)
|
||||
object:set_pos({ x = story.get_random_pos(), y = story.get_random_pos(), z = story.get_random_pos()})
|
||||
-- Place matching schematic
|
||||
storybase.debugmsg(name, "Loading schematic, please be patient...")
|
||||
schem.load("", spos, "stage_"..stage)
|
||||
if we_file then
|
||||
schem.load("", spos, we_file)
|
||||
else
|
||||
schem.load("", spos, "stage_"..stage)
|
||||
end
|
||||
-- Check if player has checkpoint, if yes, use it
|
||||
local checkpoint = story.get_checkpoint("singleplayer")
|
||||
local checkpoint = story.get_checkpoint(name)
|
||||
if checkpoint and continuation then
|
||||
spos.x_off = checkpoint["x"]
|
||||
spos.y_off = checkpoint["y"]
|
||||
|
@ -72,13 +77,10 @@ function story.reload_stage(name, continuation)
|
|||
if music ~= bgmusic.playing("title", name) then
|
||||
bgmusic.play(name, music)
|
||||
end
|
||||
-- Remove checkpoint if not continuation
|
||||
-- Remove checkpoint if not continuation
|
||||
if not continuation then
|
||||
story.set_checkpoint(name, false)
|
||||
end
|
||||
-- Clean up variables
|
||||
-- spos = nil (TODO: find out why this is causing weird bugs)
|
||||
music = nil
|
||||
minstage = nil
|
||||
story.clean_stagevars()
|
||||
return true
|
||||
end
|
||||
|
|
|
@ -12,34 +12,47 @@ function storybase.init(name)
|
|||
return true
|
||||
end
|
||||
|
||||
-- init
|
||||
-- check
|
||||
function storybase.check(name)
|
||||
-- Read data from database
|
||||
local data = minetest.deserialize(storage:get_string(name))
|
||||
-- Check if all required keys do exist
|
||||
corruptionmsg = "Your save data is corrupted. The game won't load. To fix this issue, please try to issue the command /gamereset"
|
||||
if not data.stage or not data.score then
|
||||
storybase.crashmsg(name, "check", "missing data", corruptionmsg)
|
||||
return false
|
||||
if not data.stage then
|
||||
data.stage = 1
|
||||
did_reset = true
|
||||
end
|
||||
data.stage = tonumber(data.stage)
|
||||
if not data.score then
|
||||
data.score = 0
|
||||
did_reset = true
|
||||
end
|
||||
if did_reset then
|
||||
minetest.chat_send_player(name, "Some of your game data required a reset. We're sorry for this bug, but already working on it.")
|
||||
end
|
||||
|
||||
data.stage = data.stage
|
||||
data.score = tonumber(data.score)
|
||||
storage:set_string(name, minetest.serialize(data))
|
||||
return true
|
||||
end
|
||||
|
||||
-- list
|
||||
function storybase.list_stages()
|
||||
local stagelist = {}
|
||||
local rawlist = minetest.get_dir_list(worldpath.."/stages/", false)
|
||||
for index, rawentry in pairs(rawlist) do
|
||||
table.insert(stagelist, string.reverse(string.gsub(rawentry:reverse(),"sop.", "", 1)))
|
||||
end
|
||||
return stagelist
|
||||
end
|
||||
|
||||
-- get
|
||||
function storybase.get_score(name)
|
||||
local data = minetest.deserialize(storage:get_string(name))
|
||||
return data.score
|
||||
end
|
||||
function storybase.get_stage(name, truestage)
|
||||
function storybase.get_stage(name)
|
||||
local data = minetest.deserialize(storage:get_string(name))
|
||||
if data.extrastage and truestage ~= true then
|
||||
return data.extrastage
|
||||
else
|
||||
return data.stage
|
||||
end
|
||||
return data.stage
|
||||
end
|
||||
|
||||
-- set
|
||||
|
@ -49,22 +62,19 @@ function storybase.set_score(name, newscore)
|
|||
storage:set_string(name, minetest.serialize(data))
|
||||
return true
|
||||
end
|
||||
function storybase.set_stage(name, newstage, extrastage)
|
||||
function storybase.set_stage(name, newstage)
|
||||
-- Check for existence
|
||||
if newstage and not story.get_stage(newstage) then
|
||||
storybase.crashmsg(name, "set_stage", "No such stage", newstage)
|
||||
return false
|
||||
end
|
||||
end
|
||||
-- Disable checkpoint
|
||||
story.set_checkpoint(name, false)
|
||||
-- Store data
|
||||
local data = minetest.deserialize(storage:get_string(name))
|
||||
if extrastage then
|
||||
data.extrastage = newstage
|
||||
else
|
||||
data.stage = newstage
|
||||
end
|
||||
data.stage = newstage
|
||||
storage:set_string(name, minetest.serialize(data))
|
||||
story.clean_stagevars()
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -75,12 +85,6 @@ function storybase.inc_score(name, amount)
|
|||
storybase.set_score(name, newscore)
|
||||
return newscore
|
||||
end
|
||||
function storybase.inc_stage(name, amount)
|
||||
oldstage = storybase.get_stage(name)
|
||||
newstage = oldstage + amount
|
||||
storybase.set_stage(name, newstage)
|
||||
return newstage
|
||||
end
|
||||
|
||||
-- debugmsg
|
||||
function storybase.debugmsg(name, message)
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
buildmode = false
|
||||
noreload = false
|
||||
allow_cheating = minetest.is_singleplayer()
|
||||
debugmsgs = true
|
||||
defaultprivs = {
|
||||
interact = true,
|
||||
shout = true,
|
||||
fast = true
|
||||
}
|
||||
defaulthud = {
|
||||
hotbar = true,
|
||||
crosshair = true,
|
||||
wielditem = true,
|
||||
breathbar = true,
|
||||
minimap = true,
|
||||
minimap_radar = true,
|
||||
healthbar = true
|
||||
}
|
||||
defaultnohud = {
|
||||
hotbar = false,
|
||||
crosshair = false,
|
||||
wielditem = false,
|
||||
breathbar = false,
|
||||
minimap = false,
|
||||
minimap_radar = false,
|
||||
healthbar = false
|
||||
}
|
||||
buildmode = false
|
||||
noreload = false
|
||||
allow_cheating = minetest.is_singleplayer()
|
||||
debugmsgs = true
|
||||
defaultprivs = {
|
||||
interact = true,
|
||||
shout = true,
|
||||
fast = true
|
||||
}
|
||||
defaulthud = {
|
||||
hotbar = true,
|
||||
crosshair = true,
|
||||
wielditem = true,
|
||||
breathbar = true,
|
||||
minimap = true,
|
||||
minimap_radar = true,
|
||||
healthbar = true
|
||||
}
|
||||
defaultnohud = {
|
||||
hotbar = false,
|
||||
crosshair = false,
|
||||
wielditem = false,
|
||||
breathbar = false,
|
||||
minimap = false,
|
||||
minimap_radar = false,
|
||||
healthbar = false
|
||||
}
|
||||
|
|
26
mods/storybase/globalsteps.lua
Normal file
26
mods/storybase/globalsteps.lua
Normal file
|
@ -0,0 +1,26 @@
|
|||
minetest.register_globalstep(function(dtime)
|
||||
local objs = minetest.object_refs
|
||||
for id, obj in pairs(objs) do
|
||||
if obj:is_player() then
|
||||
-- Get player and environment informations
|
||||
local pos = obj:get_pos()
|
||||
local name = obj:get_player_name()
|
||||
local node = minetest.get_node(pos)
|
||||
|
||||
-- Check if the node that the player is inside is a stageblock or checkpoint
|
||||
if string.match(node.name, "storybase:stageblock_") then
|
||||
-- Set stage to new one
|
||||
storybase.set_stage(name, node.name:gsub("storybase:stageblock_", ""), false)
|
||||
-- try to play warp sound
|
||||
pcall(function () minetest.sound_play("warp", {to_player=name, gain=2.00}) end)
|
||||
-- Finally, reload the stage
|
||||
story.reload_stage(name)
|
||||
|
||||
elseif node.name == "story:checkpoint" then
|
||||
local rel_pos = story.get_player_relative_pos(name)
|
||||
story.set_checkpoint(name, true, rel_pos["x"], rel_pos["y"], rel_pos["z"])
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end)
|
|
@ -1,8 +1,10 @@
|
|||
local modpath = minetest.get_modpath("storybase")
|
||||
local worldpath = minetest.get_worldpath()
|
||||
dofile(modpath.."/config.lua")
|
||||
dofile(modpath.."/api.lua")
|
||||
dofile(modpath.."/commands.lua")
|
||||
dofile(modpath.."/nodes.lua")
|
||||
dofile(modpath.."/globalsteps.lua")
|
||||
|
||||
-- Initalize server
|
||||
minetest.after(0, function()
|
||||
|
@ -25,9 +27,7 @@ end)
|
|||
-- Initalize joining players
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
-- Check players data
|
||||
if not storybase.check(player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
storybase.check(player:get_player_name())
|
||||
-- Load current stage
|
||||
player:hud_set_flags(defaulthud)
|
||||
story.reload_stage(player:get_player_name(), true)
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
-- Register stageblock
|
||||
minetest.register_node("storybase:stageblock", {
|
||||
description = "Stageblock",
|
||||
tiles ={"story_arrowup.png"},
|
||||
groups = {},
|
||||
on_punch = function(pos, node, player)
|
||||
if buildmode == true then
|
||||
minetest.node_dig(pos, node, player)
|
||||
return
|
||||
end
|
||||
storybase.inc_stage(player:get_player_name(), 1)
|
||||
story.reload_stage(player:get_player_name())
|
||||
end
|
||||
})
|
||||
-- Normal drawtype if buildmode is enabled otherwise airlike
|
||||
if buildmode == true then
|
||||
nodedrawtype = "glasslike"
|
||||
else
|
||||
nodedrawtype = "airlike"
|
||||
end
|
||||
|
||||
-- Create function to quickly register a stageblock
|
||||
function register_stageblock(stagename)
|
||||
minetest.register_node("storybase:stageblock_"..stagename, {
|
||||
description = "Stageblock ("..stagename..")",
|
||||
tiles ={"story_arrowup.png"},
|
||||
groups = {crumbly=3, soil=1},
|
||||
|
||||
drawtype = nodedrawtype,
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
walkable = false,
|
||||
air_equivalent = true,
|
||||
pointable = buildmode,
|
||||
diggable = buildmode
|
||||
})
|
||||
end
|
||||
|
||||
-- Create function to quickly register a scoreblock
|
||||
function register_scoreblock(score)
|
||||
minetest.register_node("storybase:scoreblock_" .. score, {
|
||||
description = "Scoreblock (" .. score .. ")",
|
||||
tiles ={"test.png"},
|
||||
drop = "",
|
||||
groups = {crumbly=3, soil=1},
|
||||
on_dig = function(pos, node, player)
|
||||
|
||||
on_dig = function(pos, node, player)
|
||||
if buildmode == true then
|
||||
minetest.node_dig(pos, node, player)
|
||||
return
|
||||
|
@ -29,6 +38,12 @@ function register_scoreblock(score)
|
|||
minetest.node_dig(pos, node, digger)
|
||||
end
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
-- Register all stageblocks
|
||||
for index, stagename in pairs(storybase.list_stages()) do
|
||||
register_stageblock(stagename)
|
||||
end
|
||||
|
||||
-- Register 100 scoreblocks
|
||||
|
|
Loading…
Add table
Reference in a new issue