1
0
Fork 0
mirror of https://gitlab.com/niansa/storybuilder.git synced 2025-03-06 20:48:28 +01:00

Compare commits

...

21 commits

Author SHA1 Message Date
niansa
69f1e614e3 Fix checkpoints (stupid cause) 2020-02-03 18:32:16 +00:00
niansa
543618251d Fixed intendation 2020-02-03 11:03:07 +00:00
niansa
71dc47ea1f Updated menu background/icon 2020-01-30 21:26:34 +01:00
niansa
a1bb16aec8 Implement worldedit file definition in stage.pos 2020-01-18 15:49:54 +01:00
niansa
69ee57c20b Added test entity (based on wildlife mod) temporarily 2020-01-15 21:56:16 +01:00
niansa
fa9f7d1e9f Revert buildmode to false 2020-01-15 21:01:17 +01:00
niansa
5ea9576505 Don't play warp sound if non-existent 2020-01-15 20:57:50 +01:00
niansa
69df8fbdb7 Fixed stuff in data repairment and background music 2020-01-15 20:54:54 +01:00
niansa
761fa881c6 Make checkpoints walkable 2020-01-07 16:51:34 +01:00
niansa
e7e69d44fe Restore functionality of scoreblock 2020-01-07 16:38:41 +01:00
niansa
47ba33a418 Merge branch 'master' of https://git.tuxifan.ga/niansa/minetest-storybuilder 2020-01-07 16:21:46 +01:00
niansa
a04e2396cc Make Stageblocks and Scoreblocks walkable 2020-01-07 16:21:05 +01:00
niansa
9eb48525d4 Yet another fix in readme 2019-12-20 16:49:33 +00:00
niansa
1c3f1042a2 Merge branch 'master' of https://git.tuxifan.ga/niansa/minetest-storybuilder 2019-12-20 17:09:49 +01:00
niansa
e7f813f168 Fix readme again 2019-12-20 16:09:41 +00:00
niansa
017e4f9bb7 Deprecate extrastages (needs reimplementation) 2019-12-20 17:03:14 +01:00
niansa
5681fa4cc2 Fix #9 2019-12-20 16:08:53 +01:00
niansa
785dd8d75f Fix readme 2019-12-20 13:51:06 +01:00
niansa
ba28918201 This game has its first stable release now 2019-12-12 19:52:11 +00:00
niansa
91b77d4716 moved get_minstage from story to storybase 2019-11-28 22:19:55 +01:00
niansa
7c736e9f41 Fixed even more CRITICAL bugs! 2019-11-28 22:09:12 +01:00
23 changed files with 2413 additions and 141 deletions

View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 568 KiB

View file

@ -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},

View file

@ -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

View file

@ -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
View 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
})

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

21
mods/mobkit/LICENSE Normal file
View 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

File diff suppressed because it is too large Load diff

545
mods/mobkit/mobkit_api.txt Normal file
View 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
View file

@ -0,0 +1,2 @@
name = mobkit
description = Entity API

BIN
mods/mobkit/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View file

@ -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,

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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
}

View 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)

View file

@ -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)

View file

@ -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