firt commit

master
xisd 2019-05-03 16:42:39 +02:00
parent 6d8021a51c
commit cca79a4ccf
72 changed files with 18665 additions and 0 deletions

3
depends.txt Normal file
View File

@ -0,0 +1,3 @@
intllib?
default?
basic_robot?

37
docs/robot_doc_1.md Normal file
View File

@ -0,0 +1,37 @@
<!-- --------------------------------------------
-- title : Mémo à propos des robots
-- author :
-- description : Short Guide to Robots Usage
-- source : parts from https://wiki.minetest.net/Mods/basic_robot
------------------------------------------------- -->
# Ce qu'il faut savoir à propos des robots
Quelques informations utiles à propos des robots
* Les robots peuvent faire de nombreuses choses, ils executent des programmes écrits en 'Lua'.
* Il y a aussi une télécommande.
* Vous pouvez écrire du code dans le spawner ('cpu-box') et l'executer avec le bouton 'start'. Le 'worker' apparaitra alors au dessus du spawner et executera le programme.
* Le spwaner ressemble à une boite-cpu tandis que le robot est un bloc blanc avec une tête dessinée sur sa face avant et une flèche sur le dessus.
* Les utilisateurs standard peuvent controler 2 robots. Chaque spawner ne contrôle qu'un robot à la fois.
* Le robot peut stocker des choses dans son inventaire (ex. matériaux creusés), il peut aussi prendre des choses dans cet inventaire (ex. pour produire de l'énérgie)
* Le robot peut lire et écrire les livres présent dans sa bibliothèque. Ce qui est utile pour accèder facilement à des sous-programmes.
* Pour rammasser le spawner, son inventaire et sa bibliothèque doivent être vides. Mais le robot est capable de casser le sapwner, son contenu sera alors perdu.
* Le code d'un programme est executé en boucle, une fois par seconde environ.
* Le robot à besoin d'énérgie pour effectuer certaines actions telles que creuser, fondre, écraser ou compresser.
* Si la boucle contient un nombre trop important d'opérations limitées, le code refusera de s'executer et le robot reverra le message d'erreur `robot out of available operations in one step.`.
(L'astuce serait d'incrémenter un nombre afin de diviser l'action en étapes. )
* Le robot ne peut pas léviter au dessus de plus d'un bloc d'air (et ignorera les intructions en ce sens).
* Si vous partez en laissant votre robot travailler seul, il s'arrêterra et reprendra son programme au DEBUT à votre retour.

164
docs/robot_doc_2.md Normal file
View File

@ -0,0 +1,164 @@
<!-- --------------------------------------------
-- title : Manuel de robotique
-- author :
-- description : Guide de l'usage est des comportements des Robots.
-- source : parts from https://wiki.minetest.net/Mods/basic_robot
------------------------------------------------- -->
# À propos des robots
Ce document présente les comportements des robots ainsi leurs limitations et usages. Pour des instructions détaillées à propos de la programations des robots voir le manuel technique (API)
## Introduction
Les robots peuvent se déplacer, détécter des blocs, creuser, construire et plus encore.
Leurs utilisateurs peuvent écrire des programmes pour le robot dans le langage 'Lua'.
Il existe aussi une télécommande.
Le système consiste en un 'spawner' ('cpu-box') que les joueurs posent sur le sol.
Ce spawner propose un formulaire pour contrôler, programmer et faire fonctionner le robot ('worker').
Seule cette partie ('worker') du robot se déplace et dispose d'un rayon d'action limité (environ 23 blocs du joueur et du spawner).
Un clavier du mod basic_machines peut être programmé pour démarrer le robot via un bouton presser par d'autres joueurs et pour recevoir des entrées clavier.
C'est utile pour les robot qui executent des actions pour d'autres joueurs (ex. magasin, messagerie, robot parlant, mini-jeu, etc. )
Sommairement, on peu dire que les robots sont constitué des deux parties mentionnée plus haut :vous pouvez entrer du code dans le spawner ('cpu-box') et l'executer avec le bouton start. Le 'worker' apparaîtra au dessus du spawner et executera le code depuis cette position.
Il est parfois plus simple d'utiliser le terme 'robot' pour désigner la partie 'worker' dur robot.
En disant par exemple : Le spwaner ressemble à une boite-cpu tandis que le robot ressemble à un simple bloc blanc avec une tête sur sa face avant et une flêche sur le dessus.
## Comment obtenir un robot ?
Vous pouvez crafter le robot avec sur cristaux de mese au dessus d'une pierre, un lingot d'acier, et une autre pierre
Vous pouvrez crafter une télécommande avec un crisal de mese sous un bâton.
## Le formulaire de contrôle
Un clic-droit sur le spawner ouvre un formulaire composé de boutons et d'une zone de texte destinée à recevoir le programme du robot.
* Le boutton "Save" enregistre le programme. Vous devez sauvegardez avant de lancer le programme.
Note: La sauvegarde n'est pas permanente : une verification syntaxique est réalisées mais aucun fichier n'est enregisté et rammasser le spawner effecera le contenu de cette zone de texte.
Copier et collez ce texte ailleurs pour ne pas le perdre.
* Le boutton "Start" crée un robot-worker (au dessus du spawner) qui execute le programme.
* Le boutton "Stop" interrompt le programme et supprime le robot-worker.
* Le champ 'id permet de controller plusieurs robots depuis un spawner.
Les joueurs qui ne disposent pas du privilège 'robot' ne peuvent utiliser que 2 robots à la fois. Ce qui signifie qu'ils ne peuvent utiliser que les ids 1 and 2.
* Le boutton "Inventory" ouvre l'inventaire du robot.
L'inventaire contient 8*4 emplacements, la même taille qu'un coffre. Les robots peuvent stocker de choses dans cet inventaire (ex. les matériaux récoltés en creusant), et peuvent aussi prendre des choses dans cet inventaire (ex. pour produire de l'énérgie)
* Le boutton "Library" ouvre une "bibliothèque". Le robot peut lire et écrire les livres qui s'y trouvent.
Cela peut être utilisé pour stocker et accèder facilement à du code depuis le programme.
* Le boutton "Help" affiche une aide pour les fonctions du robot.
Utilisez la touche Echap pour quitter le formulaire.
### Notes
Chaque spawner ne peut activer qu'un robot à la fois.
Pour rammasser le spawner, son inventaire et sa bibliothèque doivent être vides. Mais le robot est capable de casser le sapwner, son contenu sera alors perdu.
Lorsque que le robot est en fonction, un clic-droit dessus oouvrira egalement le formulaire (sans le bouton 'start') vous permettant ainsi de le stoper ou d'accèder à son inventaire.
Le code du programme est exectuté en boucle répétée environ toutes les secondes
L'edition du programme d'un robot en fonctionnement ne prendra effet qu'après l'avoir stoppé et relancé.
## Capacitées
#### * Energie
Le robot à besoin d'énérgie pour effectuer certaines actions telles que creuser, fondre, écraser ou compresser.
Si le robot dispose d'un combustible (ex: un morceau de charbon) dans son inventaire, il peut être programmé pour produire de l'énérgie : `machine.generate_power("default:coal_lump")`
Le niveau actuel d'énérgie peut être consulté avec la fonction `machine.energy()`.
<!-- Note : Write about upgarde -->
#### * Limite d'actions
Comme mentionné plus haut, le programme du robot s'execute en boucle. Cela signifie qu'à moins de recevoir l'instruction de s'arrèter, le robot executera le code encore et encore... aussi longtemps qu'il le peut.
Pour cette raison, certaines opérations disposent d'un nombre d'execution limité par boucle. Ces actions incluents celles mentionnées plus haut, ainsi que quelques autres (ex. generer de l'energie).
<!-- Note : A complete list would be good !-->
Lorsque cette limite est atteinte, le code refusera de s'executer et le robot reverra le message d'erreur `robot out of available operations in one step.`.
Cette limite est parfois appelée 'maxdig' ou 'maxoperations' (le nom de la variable correspondante).
<!-- Note : Mabe store maxoperations in self in order to consult it in-gameb -->
<!-- Note : Setting the limit to 3 might be a good thing too -->
La limite par defaut est 2. Ce qui signifie que lorsque le robot démarre, il doit y avoir moins de 2 (=1) opération dans une boucle
Par exemple, je ne peux pas executer ce code :
> machine.generate_power("default:jungletree") -- operation limitée
> dig.forward() -- operation limitée
> move.forward()
> dig.up() -- operation limitée
Selon ce code, le robot devrait générer de l'énergie à partir d'arbre de jungle, creuser vers l'avant, se déplacer vers l'avant, et creuser vers le haut et ... repeter.
Mais cela ne fonctionnera pas car 3 actions limitées seraient éxécutées en une boucle.
L'astuce serait d'incrémenter un nombre afin de diviser l'action en étapes, comme ceci :
> if not i then i = 1 end --initialise i
> if i == 1 then machine.generate_power("default:jungletree")
> elseif i == 2 then dig.forward()
> move.forward()
> elseif i == 3 then dig.up()
> i = 0
> end
> i = i + 1
Dans cet exemple, 'i' prend d'abord la valeur 1 et sa valeurs sera augmenté à chaque boucle (à cause de la dernière ligne)
La première fois que le code est executé, 'i' vaut 1, de l'énérgie sera générée.
La seconde fois que le code est executé, 'i' vaudra 2, le robot creusera vers l'avant et se déplacera vers l'avant,
La troisième fois, 'i' vaudra 3, le robot will creusera vers le haut et réinitialisera la valeur de i à 0 ( de façon à ce qu'après la dernière ligne i sera égal a 1. )
Donc la quatrième fois que le code sera executé sera identique à la première fois, la cinquième sera comme la seconde, la sixième sera comme la troisième, la septième comme la première... et ainsi de suite.
#### * Gravité
Bien qu'il ne semble pas être sujet à la gravité, le robot ne peut pas léviter au dessus de plus d'un bloc d'air.
Si vous lui demandez d'avancer en direction d'une falaise, il ignorera simplement l'operation qui le placerai dans une situation de levitation au dessus de deux blocs d'air ou plus...
... mais il continuera d'executer le programme en ignorant totalement les conséquences - parfois catastrophiques - de cette étape sautée !
Si malgré cette prudence, le robot se retouve au dessus du vide, il s'arrêtra simplement et disparaîtra
#### * Le bruit de l'arbre qui tombe...
Contrairement à la question étérnelle 'Est ce qu'un arbre qui tombe fait du bruit s'il n'ya personne pour l'entendre?', nous pouvons répondre à celle-ci : 'Est ce q'un robot-worker travaille s'il n'y personne pour le voir ?'.
Et la réponse est NON.
Dans minetest, les zone qui ne sont pas peuplées (de joueurs) ne sont pas chargées.
Donc, si vous partez loins de votre robot (ou de son spawner), en le laissant travailler, il ne tardera pas à s'arrêter.
Mais il y a pire ! Lorsque vous reviendrez, la zone où se trouve le robot sera à nouveau chargée et celui-ci reprendra son programme AU DEBUT, sans considération pour la ligne à laquelle il s'était arrèté...
Ce qui, encore une fois, peut résuter en un désordre catasrophique à cause de quelques lignes sautées.
Donc, ... voilà
## Conclusion
L'usage des robots s'apprends par les essais et erreurs.
Le manuel technique (API) devrait aussi être utile.
Vous devriez aussi consulter le wiki :
and la page du forum : https://forum.minetest.net/feed.php?f=3&amp;t=18345
and essayer le serveur Basic_Robot.

258
docs/robot_doc_3.md Normal file
View File

@ -0,0 +1,258 @@
<!-- --------------------------------------------
-- title : Robot API
-- author :
-- description : Fonctions et syntaxe pour la programmation des robots
-- source : In-Game Help from basic_robot mod
-- converted to markdown syntax but still importable into a formspec
-- (...still waiting for in-game markdown parser)
------------------------------------------------- -->
# Robot API
Ce document présente la plupart des fonctions et la syntaxe pour la programmation des robots.
### BASIC LUA SYNTAX
la plupart des syntaxes basique du langage lua sont autorisés.
> if x==1 then A else B end
> for i = 1, 5 do something end
> while i<6 do A; i=i+1; end
Tableaux:
> myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}
( Voir la documentation sur le langage lua pour plus de détails )
## COMMANDES POUR LES ROBOTS
### MOVEMENT,DIGGING, PLACING, INVENTORY TAKE/INSERT
`move.direction()`
`turn.left()`, `turn.right()`, `turn.angle(45)`
`dig.direction()`
`place.direction('default:dirt', optional orientation param)`
`read_node.direction()`
`insert.direction(item, inventory)`
`check_inventory.direction(itemname, inventory, index)`
`activate.direction(mode)`
`pickup(r)`
`craft(item,mode)`
`take.direction(item, inventory)`
`read_text.direction(stringname,mode)`
`write_text.direction(text,mode)`
---------------------------------
`move.direction()`
Où direction peut être : forward, backward, left,right, up, down
La direction forward_down ne fonctionne qu'avec `dig`, `place` et `read_node`
`turn.left()`, `turn.right()`, `turn.angle(45)`
`dig.direction()`
`place.direction('default:dirt', optional orientation param)`
`read_node.direction()`
Renvoie le nom du bloc
`insert.direction(item, inventory)`
Ajoute un objet dans l'inventaire cible depuis l'invetaire du robot
`check_inventory.direction(itemname, inventory, index)`
Inspecte le bloc et renvoie false/true
- direction peut être self
- si index > 0 , renvoie itemname. Si itemname == '' , test si l'inventaire est vide.
`activate.direction(mode)`
Active le bloc cible
`pickup(r)`
Rammasse tous les objets autours du robot dans un rayon de r. r doit être < 8. Renvoie la liste des objets ou nil.
`craft(item,mode)`
Craft item si les matériaux nécessaires sont présents dans l'inventaire.
mode = 1 renvoie la recette
`take.direction(item, inventory)`
Prends item dans l'inventaire cible et l'ajoute à celui du robot.
`read_text.direction(stringname,mode)`
Lit le texte d'un panneau, coffre, ou autre bloc.
Paramètre stringname optionnel pour d'autres meta, mode 1 s'il s'agit d'un nombre
`write_text.direction(text,mode)`
Écrit le texte dans le bloc cible (en tant qu'infotext)
### BOOKS/CODE
`title,text=book.read(i)`
`book.write(i,title,text)`
`code.run(text)`
`code.set(text)`
`find_nodes('default:dirt',3)`
---------------------------------
`title,text=book.read(i)`
Renvoie titre, contenut du livre à la position i de la bibliothèque
`book.write(i,title,text)`
Ecrit dans le livre à la position i de la bibliothèque
`code.run(text)`
compile et execute le code dans la sandbox
`code.set(text)`
remplace le code actuel du robot
`find_nodes('default:dirt',3)`
renvoie la distance du bloc dans un rayon de 3 ou false s'il n'y en a pas
### PLAYERS
`find_player(3)`
`attack(target)`
`grab(target)`
`player.getpos(name)`
`find_player(3)`
finds players in radius 3 around robot and returns list, if none returns nil
`attack(target)`
attempts to attack target player if nearby
`grab(target)`
attempt to grab target player if nearby and returns true if succesful
`player.getpos(name)`
return position of player, player.connected() returns list of players
### ROBOT
`say(\"hello\")`
`self.listen(0/1)`
`speaker, msg = self.listen_msg()`
`self.send_mail(target,mail)`
`sender,mail = self.read_mail()`
`self.pos()`
`self.name()`
`self.set_properties({textures=.., visual=..,visual_size=.., , )`
`self.set_animation(anim_start,anim_end,anim_speed,anim_stand_start)`
`self.spam(0/1)`
`self.remove()`
`self.reset()`
`self.spawnpos()`
`self.viewdir()`
`self.fire(speed, pitch,gravity)`
`self.fire_pos()`
`self.label(text)`
`self.display_text(text,linesize,size)`
`self.sound(sample,volume)`
---------------------------------
`say(\"hello\")`
will speak
`self.listen(0/1)`
(de)attaches chat listener to robot
`speaker, msg = self.listen_msg()`
retrieves last chat message if robot listens
`self.send_mail(target,mail)`
sends mail to target robot
`sender,mail = self.read_mail()`
reads mail, if any
`self.pos()`
returns table {x=pos.x,y=pos.y,z=pos.z}
`self.name()`
returns robot name
`self.set_properties({textures=.., visual=..,visual_size=.., , )`
sets visual appearance
`set_animation(anim_start,anim_end,anim_speed,anim_stand_start)`
set mesh animation
`self.spam(0/1)`
(dis)enable message repeat to all
`self.remove()`
stops program and removes robot object
`self.reset()`
resets robot position
`self.spawnpos()`
returns position of spawner block
`self.viewdir()`
returns vector of view for robot
`self.fire(speed, pitch,gravity)`
fires a projectile from robot
`self.fire_pos()`
returns last hit position
`self.label(text)`
changes robot label
`self.display_text(text,linesize,size)`
displays text instead of robot face, if no size return text
`self.sound(sample,volume)`
plays sound named 'sample' at robot location
rom is aditional table that can store persistent data, like `rom.x=1`
.
### KEYBOARD
place spawner at coordinates (20i,40j+1,20k) to monitor events
`keyboard.get()`
`keyboard.set(pos,type)`
`keyboard.read(pos)`
---------------------------------
`keyboard.get()`
returns table {x=..,y=..,z=..,puncher = .. , type = .. } for keyboard event
`keyboard.set(pos,type)`
set key at pos of type 0=air, 1..6, limited to range 10 around
`keyboard.read(pos)`
return node name at pos
### TECHNIC FUNCTIONALITY
namespace 'machine'. most functions return true or nil, error
`machine.energy()`
`machine.generate_power(fuel, amount)`
`machine.smelt(input,amount)`
`machine.grind(input)`
`machine.compress(input)`
`machine.transfer_power(amount,target_robot_name)`
---------------------------------
`machine.energy()`
displays available energy
`machine.generate_power(fuel, amount)`
= energy, attempt to generate power from fuel material
if amount>0 try generate amount of power using builtin generator - this requires 40 gold/mese/diamonblock upgrades for each 1 amount
`machine.smelt(input,amount)`
= progress/true. works as a furnace
if amount>0 try to use power to smelt - requires 10 upgrades for each 1 amount, energy cost is 1/40*(1+amount)
`machine.grind(input)`
grinds input material, requires upgrades for harder material
`machine.compress(input)`
requires upgrades - energy intensive process
`machine.transfer_power(amount,target_robot_name)`
### CRYPTOGRAPHY
namespace 'crypto'
`crypto.encrypt(input,password)`
`crypto.decrypt(input,password)`
`crypto.scramble(input,randomseed,sgn)`
`crypto.basic_hash(input,n)`
---------------------------------
`crypto.encrypt(input,password)`
returns encrypted text, password is any string
`crypto.decrypt(input,password)`
attempts to decrypt encrypted text
`crypto.scramble(input,randomseed,sgn)`
(de)permutes text randomly according to sgn = -1,1
`crypto.basic_hash(input,n)`
returns simple mod hash from string input within range 0...n-1

10
docs/robot_doc_4.md Normal file
View File

@ -0,0 +1,10 @@
<!-- --------------------------------------------
-- title : Lua Syntax
-- author :
-- description : Lua syntax allowed in basic_robot scripts
-- source :
------------------------------------------------- -->
# Lua Syntax
... TODO

10
docs/robot_doc_5.md Normal file
View File

@ -0,0 +1,10 @@
<!-- --------------------------------------------
-- title : Robot API (admin)
-- author :
-- description : Robot functions and syntax usable inside robots with admin privilege
-- source :
------------------------------------------------- -->
# Robot API (Admin)
... TODO

546
init.lua Normal file
View File

@ -0,0 +1,546 @@
-- This mod add a way to import programs for basic_robot
--
-- TODO : Allow importing file from clients instead or in addition of worldpath
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local worldpath = minetest.get_worldpath()
-- Load support for intllib.
local S, NS = dofile(modpath.."/intllib.lua")
-- S = function(s) return s end
-- Request read/write access to files
local ie = minetest.request_insecure_environment()
assert(ie, "You must allow `"..modname.."` in `secure.trusted_mods`")
local private = { }
private.open = ie.io.open
private.mkdir = ie.core.mkdir
local brt = {}
brt.loglevel = "info"
brt.main_categories = {
-- Denomination, Prefix, Description
{S("Documentation"), "robot_doc", S("Documentation about the robot usage.")},
{S("Scripts"), "scripts_index", S("A library of scripts for the robot")},
}
brt.scripts_categories = {
-- Denomination, Prefix, Description
{S("Exemples"), "exemple", S("A set of exemples scripts for the robot")},
-- {S("Exemples (advanced)"), "exemple_advanced", S("A set of advanced exemples scripts for the robot")},
--{S("Testing"), "testing", S("Scripts that may not work and need testing")},
{S("Admin"), "admin", S("Scripts that are requiering some admin privilege to run")},
}
-- Filestructure into worldpath
brt.dir = worldpath.."/basic_robot"
-- Pattern identifiyng files infos
brt.patterns = {}
brt.patterns.lua = {
{ "title", "-- title : " },
{ "author", "-- author : " },
{ "description", "-- description : "},
}
---------------
-- Functions --
---------------
-- Send message to minetest log
brt.log = function(text,level)
if not level then level = brt.loglevel end
minetest.log(level,"["..modname.."] "..text)
end
-- Send notification message to player
brt.notify = function(player, text)
local name = player:get_player_name()
minetest.sound_play({ name = modname.."_blop", gain = 0.2 }, { to_player = name })
minetest.chat_send_player(name,text)
end
-- Test if player is admin (has the privs priviliege)
brt.player_is_admin = function(player)
local name = player:get_player_name()
local privs = minetest.get_player_privs(name)
if privs and privs.privs then return true
else return false
end
end
--______________________________________________________________
--
-- Part 1 : Import scripts from modpath into worlpath
--______________________________________________________________
-- Function : Copy content of a file into an other file
brt.copy = function(inpath,outpath)
local input = private.open(inpath,"r")
local output = private.open(outpath,"w")
-- If file successfully opened ...
if input and output then
-- write file content to the one in worldpath
local content = input:read("*all")
output:write(content)
output:close()
input:close()
else
brt.log("Uable to copy content of "..inpath.." to "..outpath)
end
end
local scpath = modpath.."/scripts"
-- Create the basic_robot directory into the world folder
private.mkdir(brt.dir)
-- Copy scripts files gathered in this mod
-- Including scripts by rnd contained in basic_robot
for _,v in ipairs(brt.scripts_categories) do
local n = 1
while true do
local sc = scpath.."/"..v[2].."_"..n..".lua"
local fd = private.open(sc)
if not fd then break end
fd:close()
local dst = brt.dir.."/"..v[2].."_"..n..".lua"
brt.copy(sc,dst)
n = n + 1
end
end
--______________________________________________________________
--
-- Part 2 : Access scripts and doc using a formspec
--______________________________________________________________
brt.lists = {}
brt.lists.scripts = {}
brt.lists.docs = {}
local get_file_infos = function(rep, prefix, ext)
local lst = {}
local n = 1
while true do
local sc = rep.."/"..prefix.."_"..n.."."..ext
local f = private.open(sc)
if not f then break end
lst[n] = {}
-- This whole thing is pretty heavy...
-- Can't I just get line 1,2,3 ?
local l = 1
for line in f:lines() do
local patterns
-- Look fot set of patterns specific to this extention
if brt.patterns[ext] then patterns = brt.patterns[ext]
-- Or default to lua
else patterns = brt.patterns.lua end
-- Look for pattern indicating infos
for _,v in ipairs(patterns) do
if string.match(line,v[2]) then
local k = v[1]
lst[n][k] = string.gsub(line,v[2],"")
break
end
end
-- Break the loop after 4 lines
l = l + 1
if l > 4 then break end
end
f:close()
if not lst[n]["title"] then lst[n]["title"] = prefix.."_"..n end
-- if not lst[n]["author"] then lst[n]["author"] = "" end
lst[n]["path"] = sc
n = n + 1
end
return lst
end
brt.lists_update = function()
-- Reinitialize the list of scripts
brt.lists.scripts = {}
-- Update list of scripts to match content of the worldpath rep
for _,v in ipairs(brt.scripts_categories) do
local listname = v[2]
local lst = get_file_infos(brt.dir,listname,"lua")
brt.lists.scripts[listname] = lst
end
end
brt.lists_make = function()
brt.lists_update()
local docpath = modpath.."/docs"
brt.lists.docs = get_file_infos(docpath,"robot_doc","md")
end
local line_parse = function(line)
local op = ","
local pt = {
{"^# ","#FF9292,"},
{"^## ","#9292DB,"},
{"^### ","#92FF92,"},
{"^#### ","#FFFF00,"},
}
for _,v in ipairs(pt) do
if string.match(line,v[1]) then
line = string.gsub(line,v[1],"")
op = v[2]
break
end
end
line = op .. minetest.formspec_escape(line)
return line
end
brt.form = {}
-- Function : main form content displaying text
brt.form.text = function(ref)
local ta, filetext, filettable
-- Read file to get its content
local f = private.open(ref.path)
if not f then filetext = S("Sorry, there was a problem while trying to read the file...") end
-- If we just want to read the file
if ref.read then
-- Lets import it as a table
filettable = {}
local maxlen = 59
for line in f:lines() do
-- Test if string is too long
while true do
local len = string.len(line)
if len > maxlen then
-- Get a resized substring
local sub = string.sub(line,1,maxlen)
-- Match the longest sequence finishing with a space in this substring
local match = string.match(sub, "^(.*%s)")
-- If no match, ... split anyways
if not match then match = sub end
-- Get length of this new substring
local matchlen = string.len(match)
-- Add it to table
filettable[#filettable+1] = line_parse(match)
-- Refefine line
line = string.sub(line, matchlen + 1, len )
else
-- TODO Remove comments at the beginning (keep them as tooltip)
-- even prepare it for formspec
filettable[#filettable+1] = line_parse(line)
break
end
end
end
print(dump(filettable))
-- Otherwise we need be able to select and edit and copy and paste, so string it is
else filetext = f:read("*all") end
f:close()
-- Display the text
-- Docs as readable text
if ref.read then
-- Table settings
ta = "tablecolumns[color;text,align=inline]" --,width=5
.. "tableoptions[color=#FFF;background=#494949;highlight=#494949;border=true]"
.. "table[0,0;6.7,6;doc;" .. table.concat(filettable, ",") .. ",] "
-- Script text into a textarea
else
ta = "textarea[0.4,0;6.7,6.7;script;;"..minetest.formspec_escape(filetext).."]"
end
-- Add a button to get back to the previous menu
ta = ta .. "image_button[1,6.5;0.8,0.8;parent.png;parent;;false;true;parent.png]"
-- Hidden field containing name of previous menu
ta = ta .. "field[-1,-1;0,0;previous;;"..ref.previous.."]"
-- TODO : Save button for later implementation of a save
-- ta = ta .. "image_button[2,6.5;0.8,0.8;save.png;save;;false;true;save.png]"
return ta
end
-- Function : main form content displaying a menu
brt.form.menu = function(player,ref,i)
local description
if ref == "main_index" then bck = "."
else bck = ".." end
-- Start the textlist item
local menu = "textlist[0,0;6.7,5.5;"..ref..";"..bck
local first = true
-- Main menu
if ref == "main_index" then
-- Loop through categories
for _,v in ipairs(brt.main_categories) do
-- Add button
local label = v[1]
menu = menu .. "," .. minetest.formspec_escape(label)
end
-- Get description
if i and brt.main_categories[i] then description = brt.main_categories[i][3] end
-- Documentation
elseif ref == "robot_doc" then
-- Loop through table containing scripts in categories
for _,v in ipairs(brt.lists.docs) do
local credit = ""
-- Add button
if v.author and v.author ~= "" then credit = " (by "..v.author..")" end
local label = v.title..credit
menu = menu .. "," .. minetest.formspec_escape(label)
end
if i and brt.lists.docs[i] then description = brt.lists.docs[i]["description"] end
-- Scripts menu
elseif ref == "scripts_index" then
local has_privs = brt.player_is_admin(player)
-- Loop through categories
for _,v in ipairs(brt.scripts_categories) do
-- Skip admin category if player is not admin
if ( v[2] == "admin" and not has_privs ) then
else
-- Add button
local label = v[1]
menu = menu .. "," .. minetest.formspec_escape(label)
end
end
-- Get description
if i and brt.scripts_categories[i] then description = brt.scripts_categories[i][3] end
-- Any scripts submenu
else
-- Loop through table containing scripts in categories
for _,v in ipairs(brt.lists.scripts[ref]) do
local credit = ""
-- Add button
if v.author and v.author ~= "" then credit = " (by "..v.author..")" end
local label = v.title..credit
menu = menu .. "," .. minetest.formspec_escape(label)
end
if i and brt.lists.scripts[ref][i] then description = brt.lists.scripts[ref][i]["description"] end
end
-- Close the textlist item
if i then menu = menu .. ';]'
else menu = menu .. ';1]' end
-- Display description
if description then
menu = menu .. "textarea[0.5,5.7;5,2;; ".. minetest.formspec_escape(description) ..";]"
end
return menu
end
-- Function display the formpec to the player
brt.showformspec = function(player,formspec,formname)
if not formname then formname = modname..":terminal" end
local player_name = player:get_player_name();
if not player_name or player_name == "" then return; end
minetest.show_formspec(player_name,formname,formspec)
end
local deco = default.gui_bg .. default.gui_bg_img
local formspec_base = "size[7,7,true]" .. deco
local formspec_content
local formspec_end = "image_button_exit[6,6.5;0.8,0.8;power.png;poweroff;;false;false;power.png]"
-- tooltip[<gui_element_name>;<tooltip_text>;<bgcolor>;<fontcolor>]
brt.terminal = function(player,ref,i)
-- Set index to default
if not ref then ref = "main_index" end
-- if not i then i = 1 end
-- Make or update files lists
if #brt.lists.docs < 1 then brt.lists_make()
else brt.lists_update() end
if type(ref) == "table" then formspec_content = brt.form.text(ref)
else formspec_content = brt.form.menu(player,ref,i)
end
local formspec = formspec_base .. formspec_content .. formspec_end
brt.showformspec(player, formspec)
end
--______________________________________________________________
--
-- Part 3 : Interpret data from fromspec
--______________________________________________________________
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == modname..":terminal" then
print(dump(fields))
local name = player:get_player_name()
-- Get back to previous menu from file view
if fields.parent and fields.previous then
brt.terminal(player,fields.previous)
-- Fields came from main menu
elseif fields.main_index then
-- Get event data
local event = minetest.explode_textlist_event(fields.main_index)
local i = event.index - 1
-- Double Click ( ignore first entry )
if event.type == "DCL" and i ~= 0 then
-- move to selected menu
brt.terminal(player,brt.main_categories[i][2])
-- Simple Click : Update description
else
brt.terminal(player,"main_index",i)
end
-- fields came from docs index
elseif fields.robot_doc then
-- Get event data
local event = minetest.explode_textlist_event(fields.robot_doc)
local i = event.index - 1
-- Double Click
if event.type == "DCL" then
-- First entry : Back to main menu
if i == 0 then brt.terminal(player,"main_index")
else
-- Display selected file
local tab = brt.lists.docs[i]
tab.previous = "robot_doc"
tab.read = true
brt.terminal(player,tab)
end
-- Simple Click : Update description
else
brt.terminal(player,"robot_doc",i)
end
-- fields came from scripts index
elseif fields.scripts_index then
-- Get event data
local event = minetest.explode_textlist_event(fields.scripts_index)
local i = event.index - 1
-- Double Click :
if event.type == "DCL" then
-- First entry : Back to main menu
if i == 0 then brt.terminal(player,"main_index")
-- Otherwise, move to selected categorie
else brt.terminal(player,brt.scripts_categories[i][2])
end
-- Simple Click : Update description
else
brt.terminal(player,"scripts_index",i)
end
-- Fields were received from inside a categorie
else
-- Loop through existing categories to find the right one
for _,v in ipairs(brt.scripts_categories) do
-- Get categorie name
local c = v[2]
if fields[c] then
-- Get event data
local event = minetest.explode_textlist_event(fields[c])
local i = event.index - 1
-- Double Click
if event.type == "DCL" then
-- First entry : Back to main menu
if i == 0 then brt.terminal(player,"scripts_index")
else
-- Display selected file
local tab = brt.lists.scripts[c][i]
tab.previous = v[2]
brt.terminal(player,tab)
--brt.terminal(player,brt.scripts_categories[i][2],i)
end
-- Simple Click : Update description
elseif i ~= 0 then
brt.terminal(player,c,i)
end
end
end
end
end
end)
--______________________________________________________________
--
-- Part 4 : Use a block to display fromspec
--______________________________________________________________
local texture = {
front = modname.."_front.png",
back = modname.."_side.png",
top = modname.."_side.png",
bottom = modname.."_side.png",
left = modname.."_side.png",
right = modname.."_side.png",
}
minetest.register_node(modname..":terminal",
{
description = S("Basic robot terminal"),
paramtype2 = "facedir",
tiles = {
texture.top, texture.bottom,
texture.left, texture.right,
texture.back, texture.front
},
sounds = default.node_sound_wood_defaults(),
groups = { choppy = 3, oddly_breakable_by_hand = 2 },
on_rightclick = function(pos, node, player, itemstack, pointedThing)
brt.terminal(player);
end
});
-- Register Recipe for the terminal block
-- ( if default mod is present )
if minetest.get_modpath("default") then
-- Default recipe
local r = {}
r.c = "default:tin_ingot"
r.m = "default:bookshelf"
r.t = "default:mese_crystal_fragment"
r.d = "default:mese_crystal_fragment"
r.l = ""
r.r = ""
-- Changes if mesecons
if minetest.get_modpath("mesecons") then
r.d = "mesecons:wire_00000000_off"
r.t = "mesecons:wire_00000000_off"
r.l = "mesecons:wire_00000000_off"
r.r = "mesecons:wire_00000000_off"
end
--[[ other changes if dye
if minetest.get_modpath("dye") then
r.l = "mesecons:magenta"
r.t = "mesecons:green"
r.r = "mesecons:cyan"
end
--]]
minetest.register_craft(
{
output = modname..":terminal",
recipe = {
{ r.c, r.t, r.c},
{ r.l, r.m, r.r},
{ r.c, r.d, r.c},
}
});
end

641
init_bokksave_attempt.lua Normal file
View File

@ -0,0 +1,641 @@
-- This mod add a way to import programs for basic_robot
--
-- TODO : Allow importing file from clients instead or in addition of worldpath
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local worldpath = minetest.get_worldpath()
-- Load support for intllib.
local S, NS = dofile(modpath.."/intllib.lua")
-- S = function(s) return s end
-- Request write access into the world folder
local ie = minetest.request_insecure_environment()
assert(ie, "You must allow `basic_robot_terminal` in `secure.trusted_mods`")
local private = { }
private.open = ie.io.open
private.mkdir = ie.core.mkdir
local brt = {}
brt.loglevel = "info"
brt.main_categories = {
-- Denomination, Prefix, Description
{S("Documentation"), "robot_doc", S("Documentation about the robot usage.")},
{S("Scripts"), "scripts_index", S("A library of scripts for the robot")},
}
brt.scripts_categories = {
-- Denomination, Prefix, Description
{S("Exemples"), "exemple", S("A set of exemples scripts for the robot")},
{S("Exemples (advanced)"), "exemple_advanced", S("A set of advanced exemples scripts for the robot")},
{S("Testing"), "testing", S("Scripts that may not work and need testing")},
{S("Admin"), "admin", S("Scripts that are requiering some admin privilege to run")},
}
-- Filestructure into worldpath
brt.dir = worldpath.."/basic_robot"
-- Pattern identifiyng files infos
brt.patterns = {}
brt.patterns.lua = {
{ "title", "-- title : " },
{ "author", "-- author : " },
{ "description", "-- description : "},
}
---------------
-- Functions --
---------------
-- Send message to minetest log
brt.log = function(text,level)
if not level then level = brt.loglevel end
minetest.log(level,"["..modname.."] "..text)
end
-- Send notification message to player
brt.notify = function(player, text)
local name = player:get_player_name()
minetest.sound_play({ name = modname.."_blop", gain = 0.2 }, { to_player = name })
minetest.chat_send_player(name,text)
end
-- Test if player is admin (has the privs priviliege)
brt.player_is_admin = function(player)
local name = player:get_player_name()
local privs = minetest.get_player_privs(name)
if privs and privs.privs then return true
else return false
end
end
--______________________________________________________________
--
-- Part 1 : Import scripts from modpath into worlpath
--______________________________________________________________
-- Function : Copy content of a file into an other file
brt.copy = function(inpath,outpath)
local input = private.open(inpath,"r")
local output = private.open(outpath,"w")
-- If file successfully opened ...
if input and output then
-- write file content to the one in worldpath
local content = input:read("*all")
output:write(content)
output:close()
input:close()
else
brt.log("Uable to copy content of "..inpath.." to "..outpath)
end
end
local scpath = modpath.."/scripts"
-- Create the basic_robot directory into the world folder
private.mkdir(brt.dir)
-- Copy scripts files gathered in this mod
-- Including scripts by rnd contained in basic_robot
for _,v in ipairs(brt.scripts_categories) do
local n = 1
while true do
local sc = scpath.."/"..v[2].."_"..n..".lua"
local fd = private.open(sc)
if not fd then break end
fd:close()
local dst = brt.dir.."/"..v[2].."_"..n..".lua"
brt.copy(sc,dst)
n = n + 1
end
end
--_____________________________________________________________________
--
-- Part 2 : Use a chatcommand to import a single script into inventory
--_____________________________________________________________________
--[[
minetest.register_chatcommand("terminal", {
privs = {
-- server = true
},
params = S("<name>"),
description = S("Display the starter kit choice message"),
func = function(name, param)
if not param or param == "" then
param = name
end
local player = minetest.get_player_by_name(param)
brt.terminal(player)
end
})
--]]
--______________________________________________________________
--
-- Part 2 : Save a book to a file
--______________________________________________________________
brt.save_to_book = function(player,text,confirm)
local msg
-- Check that there is something to save
if not text then
msg = S("Sorry, no content to save...")
brt.notify(player,msg
return
end
-- Check that there is a book to save to
local inv = player:get_inventory()
local has_empty_book = inv:contains_item("main", "default:book")
if not has_empty_book then
msg = S("You need at least one empty book in your inventory")
brt.notify(player,msg
return
end
-- Ask confirmation
if comfirm then
msg = S("Copy script into an empty book from your inventory ?")
local formspec = "size[7,3,true]" .. deco
.."textarea[0.4,0;6.4,2;;"..minetest.formspec_escape(msg)..";]"
.. "button_exit[1.5,2;1.5,1;confirm;"..S("Yes").."]"
.. "button_exit[4.5,2;1.5,1;abort;"..S("No").."]"
-- hidden area containing the text to copy
.."field[-1,-1;0,0;previous;;"..fields.script.."]"
local formname = modname..":confirm_save_to_book"
brt.showformspec(player,formspec,formname)
return
else -- Make the copy
local written_book =
inv:remove_item("main", "default:book")
if inv:room_for_item ("main", "default:book_written") then
add_item(listname, stack)
end
--______________________________________________________________
--
-- Part 2 : Access scripts and doc using a formspec
--______________________________________________________________
brt.lists = {}
brt.lists.scripts = {}
brt.lists.docs = {}
local get_file_infos = function(rep, prefix, ext)
local lst = {}
local n = 1
while true do
local sc = rep.."/"..prefix.."_"..n.."."..ext
local f = private.open(sc)
if not f then break end
lst[n] = {}
-- This whole thing is pretty heavy...
-- Can't I just get line 1,2,3 ?
local l = 1
for line in f:lines() do
local patterns
-- Look fot set of patterns specific to this extention
if brt.patterns[ext] then patterns = brt.patterns[ext]
-- Or default to lua
else patterns = brt.patterns.lua end
-- Look for pattern indicating infos
for _,v in ipairs(patterns) do
if string.match(line,v[2]) then
local k = v[1]
lst[n][k] = string.gsub(line,v[2],"")
break
end
end
-- Break the loop after 4 lines
l = l + 1
if l > 4 then break end
end
f:close()
if not lst[n]["title"] then lst[n]["title"] = listname.."_"..n end
-- if not lst[n]["author"] then lst[n]["author"] = "" end
lst[n]["path"] = sc
n = n + 1
end
return lst
end
brt.lists_update = function()
-- Reinitialize the list of scripts
brt.lists.scripts = {}
-- Update list of scripts to match content of the worldpath rep
for _,v in ipairs(brt.scripts_categories) do
local listname = v[2]
local lst = get_file_infos(brt.dir,listname,"lua")
brt.lists.scripts[listname] = lst
end
end
brt.lists_make = function()
brt.lists_update()
local docpath = modpath.."/docs"
brt.lists.docs = get_file_infos(docpath,"robot_doc","md")
end
local line_parse = function(line)
local op = ","
local pt = {
{"^# ","#FF9292,"},
{"^## ","#9292DB,"},
{"^### ","#92FF92,"},
{"^#### ","#FFFF00,"},
}
for _,v in ipairs(pt) do
if string.match(line,v[1]) then
line = string.gsub(line,v[1],"")
op = v[2]
break
end
end
line = op .. minetest.formspec_escape(line)
return line
end
brt.form = {}
-- Function : main form content displaying text
brt.form.text = function(ref)
local ta, filetext, filettable
-- Read file to get its content
local f = private.open(ref.path)
if not f then filetext = S("Sorry, there was a problem while trying to read the file...") end
-- If we just want to read the file
if ref.read then
-- Lets import it as a table
filettable = {}
maxlen = 59
for line in f:lines() do
-- Test if string is too long
while true do
local len = string.len(line)
if len > maxlen then
-- Get a resized substring
local sub = string.sub(line,1,maxlen)
-- Match the longest sequence finishing with a space in this substring
local match = string.match(sub, "^(.*%s)")
-- If no match, ... split anyways
if not match then match = sub end
-- Get length of this new substring
local matchlen = string.len(match)
-- Add it to table
filettable[#filettable+1] = line_parse(match)
-- Refefine line
line = string.sub(line, matchlen + 1, len )
else
-- TODO Remove comments at the beginning (keep them as tooltip)
-- even prepare it for formspec
filettable[#filettable+1] = line_parse(line)
break
end
end
end
print(dump(filettable))
-- Otherwise we need be able to select and edit and copy and paste, so string it is
else filetext = f:read("*all") end
f:close()
-- Display the text
-- Docs as readable text
if ref.read then
-- Table settings
ta = "tablecolumns[color;text,align=inline]" --,width=5
.. "tableoptions[color=#FFF;background=#494949;highlight=#494949;border=true]"
.. "table[0,0;6.7,6;doc;" .. table.concat(filettable, ",") .. ",] "
-- Script text into a textarea
else
ta = "textarea[0.4,0;6.7,6.7;script;;"..minetest.formspec_escape(filetext).."]"
end
-- Add a button to get back to the previous menu
ta = ta .. "image_button[1,6.5;0.8,0.8;parent.png;parent;;false;true;parent.png]"
-- Hidden field containing name of previous menu
ta = ta .. "field[-1,-1;0,0;previous;;"..ref.previous.."]"
-- TODO : Save button for later implementation of a save to a book option
-- ta = ta .. "image_button[2,6.5;0.8,0.8;save.png;save;;false;true;save.png]"
return ta
end
-- Function : main form content displaying a menu
brt.form.menu = function(player,ref,i)
local description
if ref == "main_index" then bck = "."
else bck = ".." end
-- Start the textlist item
local menu = "textlist[0,0;6.7,5.5;"..ref..";"..bck
local first = true
-- Main menu
if ref == "main_index" then
-- Loop through categories
for _,v in ipairs(brt.main_categories) do
-- Add button
local label = v[1]
menu = menu .. "," .. minetest.formspec_escape(label)
end
-- Get description
if i and brt.main_categories[i] then description = brt.main_categories[i][3] end
-- Documentation
elseif ref == "robot_doc" then
-- Loop through table containing scripts in categories
for _,v in ipairs(brt.lists.docs) do
local credit = ""
-- Add button
if v.author and v.author ~= "" then credit = " (by "..v.author..")" end
local label = v.title..credit
menu = menu .. "," .. minetest.formspec_escape(label)
end
if i and brt.lists.docs[i] then description = brt.lists.docs[i]["description"] end
-- Scripts menu
elseif ref == "scripts_index" then
local has_privs = brt.player_is_admin(player)
-- Loop through categories
for _,v in ipairs(brt.scripts_categories) do
-- Skip admin category if player is not admin
if ( v[2] == "admin" and not has_privs ) then
else
-- Add button
local label = v[1]
menu = menu .. "," .. minetest.formspec_escape(label)
end
end
-- Get description
if i and brt.scripts_categories[i] then description = brt.scripts_categories[i][3] end
-- Any scripts submenu
else
-- Loop through table containing scripts in categories
for _,v in ipairs(brt.lists.scripts[ref]) do
local credit = ""
-- Add button
if v.author and v.author ~= "" then credit = " (by "..v.author..")" end
local label = v.title..credit
menu = menu .. "," .. minetest.formspec_escape(label)
end
if i and brt.lists.scripts[ref][i] then description = brt.lists.scripts[ref][i]["description"] end
end
-- Close the textlist item
if i then menu = menu .. ';]'
else menu = menu .. ';1]' end
-- Display description
if description then
menu = menu .. "textarea[0.5,5.7;5,2;; ".. minetest.formspec_escape(description) ..";]"
end
return menu
end
-- Function display the formpec to the player
brt.showformspec = function(player,formspec,formname)
if not formname then formname = modname..":terminal" end
local player_name = player:get_player_name();
if not player_name or player_name == "" then return; end
minetest.show_formspec(player_name,formname,formspec)
end
local deco = default.gui_bg .. default.gui_bg_img
local formspec_base = "size[7,7,true]" .. deco
local formspec_content
local formspec_end = "image_button_exit[6,6.5;0.8,0.8;power.png;poweroff;;false;false;power.png]"
-- tooltip[<gui_element_name>;<tooltip_text>;<bgcolor>;<fontcolor>]
brt.terminal = function(player,ref,i)
-- Set index to default
if not ref then ref = "main_index" end
-- if not i then i = 1 end
-- Make or update files lists
if #brt.lists.docs < 1 then brt.lists_make()
else brt.lists_update() end
if type(ref) == "table" then formspec_content = brt.form.text(ref)
else formspec_content = brt.form.menu(player,ref,i)
end
local formspec = formspec_base .. formspec_content .. formspec_end
brt.showformspec(player, formspec)
end
--______________________________________________________________
--
-- Part 3 : Interpret data from fromspec
--______________________________________________________________
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == modname..":terminal" then
print(dump(fields))
local name = player:get_player_name()
-- Get back to previous menu from file view
if fields.parent and fields.previous then
brt.terminal(player,fields.previous)
-- TODO save text into an empty book
elseif fields.save_to_book then
--local msg = S("Sorry, the 'save to book' functionnality hasn't been implemented yet...")
-- .."\n"..S("... just use the old CRTL+C, CTRL+V, for now")
--brt.notify(player,msg)
brt.save_to_book(player,fields)
-- Fields came from main menu
elseif fields.main_index then
-- Get event data
local event = minetest.explode_textlist_event(fields.main_index)
local i = event.index - 1
-- Double Click ( ignore first entry )
if event.type == "DCL" and i ~= 0 then
-- move to selected menu
brt.terminal(player,brt.main_categories[i][2])
-- Simple Click : Update description
else
brt.terminal(player,"main_index",i)
end
-- fields came from docs index
elseif fields.robot_doc then
-- Get event data
local event = minetest.explode_textlist_event(fields.robot_doc)
local i = event.index - 1
-- Double Click
if event.type == "DCL" then
-- First entry : Back to main menu
if i == 0 then brt.terminal(player,"main_index")
else
-- Display selected file
local tab = brt.lists.docs[i]
tab.previous = "robot_doc"
tab.read = true
brt.terminal(player,tab)
end
-- Simple Click : Update description
else
brt.terminal(player,"robot_doc",i)
end
-- fields came from scripts index
elseif fields.scripts_index then
-- Get event data
local event = minetest.explode_textlist_event(fields.scripts_index)
local i = event.index - 1
-- Double Click :
if event.type == "DCL" then
-- First entry : Back to main menu
if i == 0 then brt.terminal(player,"main_index")
-- Otherwise, move to selected categorie
else brt.terminal(player,brt.scripts_categories[i][2])
end
-- Simple Click : Update description
else
brt.terminal(player,"scripts_index",i)
end
-- Fields were received from inside a categorie
else
-- Loop through existing categories to find the right one
for _,v in ipairs(brt.scripts_categories) do
-- Get categorie name
local c = v[2]
if fields[c] then
-- Get event data
local event = minetest.explode_textlist_event(fields[c])
local i = event.index - 1
-- Double Click
if event.type == "DCL" then
-- First entry : Back to main menu
if i == 0 then brt.terminal(player,"scripts_index")
else
-- Display selected file
local tab = brt.lists.scripts[c][i]
tab.previous = v[2]
brt.terminal(player,tab)
--brt.terminal(player,brt.scripts_categories[i][2],i)
end
-- Simple Click : Update description
elseif i ~= 0 then
brt.terminal(player,c,i)
end
end
end
end
end
end)
--______________________________________________________________
--
-- Part 4 : Register a chat command tool
--______________________________________________________________
-- TODO : chat command to list available scripts and to display one, or copy to a book
--[[
-- Chat command : display the formspec
minetest.register_chatcommand("terminal", {
privs = {
-- server = true
},
params = S("<name>"),
description = S("Display the starter kit choice message"),
func = function(name, param)
if not param or param == "" then
param = name
end
local player = minetest.get_player_by_name(param)
brt.terminal(player)
end
})
--]]
--______________________________________________________________
--
-- Part 5 : Use a block to display fromspec
--______________________________________________________________
local texture = {
front = modname.."_front.png",
back = modname.."_side.png",
top = modname.."_side.png",
bottom = modname.."_side.png",
left = modname.."_side.png",
right = modname.."_side.png",
}
minetest.register_node(modname..":terminal",
{
description = S("Basic robot terminal"),
paramtype2 = "facedir",
tiles = {
texture.top, texture.bottom,
texture.left, texture.right,
texture.back, texture.front
},
sounds = default.node_sound_wood_defaults(),
groups = { choppy = 3, oddly_breakable_by_hand = 2 },
on_rightclick = function(pos, node, player, itemstack, pointedThing)
brt.terminal(player);
end
});
-- Register Recipe for the terminal block
-- ( if default mod is present )
if minetest.get_modpath("default") then
-- Default recipe
local r = {}
r.c = "default:tin_ingot"
r.m = "default:bookshelf"
r.t = "default:mese_crystal_fragment"
r.d = "default:mese_crystal_fragment"
r.l = ""
r.r = ""
-- Changes if mesecons
if minetest.get_modpath("mesecons") then
r.d = "mesecons:wire_00000000_off"
r.t = "mesecons:wire_00000000_off"
r.l = "mesecons:wire_00000000_off"
r.r = "mesecons:wire_00000000_off"
end
--[[ other changes if dye
if minetest.get_modpath("dye") then
r.l = "mesecons:magenta"
r.t = "mesecons:green"
r.r = "mesecons:cyan"
end
--]]
minetest.register_craft(
{
output = modname..":terminal",
recipe = {
{ r.c, r.t, r.c},
{ r.l, r.m, r.r},
{ r.c, r.d, r.c},
}
});
end

45
intllib.lua Normal file
View File

@ -0,0 +1,45 @@
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.
-- Get the latest version of this file at:
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
local function format(str, ...)
local args = { ... }
local function repl(escape, open, num, close)
if escape == "" then
local replacement = tostring(args[tonumber(num)])
if open == "" then
replacement = replacement..close
end
return replacement
else
return "@"..open..num..close
end
end
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end
local gettext, ngettext
if minetest.get_modpath("intllib") then
if intllib.make_gettext_pair then
-- New method using gettext.
gettext, ngettext = intllib.make_gettext_pair()
else
-- Old method using text files.
gettext = intllib.Getter()
end
end
-- Fill in missing functions.
gettext = gettext or function(msgid, ...)
return format(msgid, ...)
end
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
return format(n==1 and msgid or msgid_plural, ...)
end
return gettext, ngettext

BIN
models/mobs_bunny.b3d Normal file

Binary file not shown.

3080
models/mobs_chicken.x Normal file

File diff suppressed because it is too large Load Diff

BIN
models/mobs_kitten.b3d Normal file

Binary file not shown.

BIN
models/mobs_penguin.b3d Normal file

Binary file not shown.

11058
models/mobs_turtle.x Normal file

File diff suppressed because it is too large Load Diff

28
readme.md Normal file
View File

@ -0,0 +1,28 @@
# Basic Robot Terminal
*Documentation is not up to date* and need to be remade
This add a craftable terminal unit related to the [basic_robot](https://github.com/ac-minetest/basic_robot/) mod.
It can be used to consult documentation about robots and browse some scripts contained into the world folder
I made it to use in a subgame that is meant to be used in surival mode, without any admin privileges and without basic machines.
Scripts and documentation included are focused on only basic robot
-[x] Fromspec window to browse scripts
-[x] Also provide some documentation
-[x] Terminal block to acces the formspec
-[ ] Documentation book (and treasurer support)
-[ ] Book storage into the terminal block
-[ ] Copy script to a book from the terminal
-[ ] Save scripts to external file
As a bonus : The documentation is stored in markdown format in the docs folder and is also converted into one single html file (robot_doc.html)
License:
I tryed to credit scripts and other things I took a the beginings of files
Some texture files are from the basic_robot mod
If I forgot something tell me I will add it
and for the rest : code LGPL 2.1 media CC BY-SA 3.0

346
robot_doc.html Normal file
View File

@ -0,0 +1,346 @@
<h1>Robot Documentation</h1>
<a name='top'></a><ul>
<li><a href="#robot_doc_1.md"> About Robots</a>
<br> - Guide to understand Robots usages and behaviors</li>
<li><a href="#robot_doc_2.md"> About Robots - The short version</a>
<br> - Short Guide to Robots Usage</li>
<li><a href="#robot_doc_3.md"> Robot API</a>
<br> - Robot functions and syntax usable inside robots</li>
<li><a href="#robot_doc_4.md"> Lua Syntax</a>
<br> - Lua syntax allowed in basic_robot scripts</li>
<li><a href="#robot_doc_5.md"> Robot API (admin)</a>
<br> - Robot functions and syntax usable inside robots with admin privilege</li>
</ul>
<hr>
<a name="robot_doc_1.md"></a><a href="#top">-- Return to Index --</a><br>
<!--
-- title : About Robots
-- author :
-- description : Guide to understand Robots usages and behaviors
-- source : parts from https://wiki.minetest.net/Mods/basic_robot
-->
<p>This document present robots behaviors, limitations and usages
for detailled functions and syntax, see 'api<em>basic</em>robot'</p>
<h1>About Robots</h1>
<h2>Introduction</h2>
<p>The robot can move around, sense the blocks, dig, build, and much more.</p>
<p>Users can write programs for the bot in Lua. There is also a remote control.</p>
<p>The system uses a spawner ('cpu-box') that the player has to place on the ground.
This spawner presents a form for controlling, programming and running the robot ("worker").
Only this worker moves around, and it has a limited range (about 23 nodes from player and spawner).</p>
<p>A keypad from the mod basic_machines can be set up to start a robot with a button-push by other players, and for keyboard-entry.
This is useful for robots that perform a task for other players, e.g. as a shop, mailbox, chatbot, game, etc. </p>
<p>Mainly, robots are constituted of the two part mentionned above : you can write code in the spawner ('cpu-box') and run it with the sart button. The 'worker' will appear above the spawns block and execute the code from there.</p>
<p>It is someting easier to call 'robot' the worker part of the robot, saying think like :
The spwaner looks like a ... cpu-box while the robot looks like a simple box, with a face on its frontside, and an arrow on top. </p>
<h2>How to get it ?</h2>
<p>You can craft a robot using 6 mese crystals atop a stone, a steel ingot, and another stone.
You can craft a remote control using mese crystal under a stick</p>
<h2>Controlling Form</h2>
<p>Rightclicking the spawner opens a form with buttons and a textbox for writing a program.</p>
<ul>
<li><p>Button "Save" saves the program. This must be done before starting a program. </p>
<p>Note: Saving is not permanent - there is no file to save to, just a quick syntax-check.
Also, picking up the spawner clears the textarea with the code.
So, use cut and paste to save your code to a texteditor, such as notepad. </p></li>
<li><p>Button "Start" creates a robot-worker (on top of the spawner) that starts executing the program. </p></li>
<li><p>Button "Stop" stops the program and removes the robot-worker. </p></li>
<li><p>Entryfield id for controlling several robots.
Players without the priv 'robot' are only allowed to run 2 robots at the same time.
This means they can only use the ids 1 and 2.</p></li>
<li><p>Button "Inventory" opens the inventory of the bot.
The inventory has 8*4 slots, same size as a chest. Robot can store thing to its inventory (e.g. materials gathered while digging), they can also take thing from it (e.g. to produce energy)</p></li>
<li><p>Button "Library" opens a "bookshelf". The robot can read and write books here.
This can be used for program code, input and output. </p></li>
<li><p>Button "Help" shows some helptext.</p></li>
</ul>
<p>Press ESC to exit the form. </p>
<h3>Notes</h3>
<p>Each spawner can only run one bot at a time.
To pick up the spawner, its inventory and library must be empty (like a chest).</p>
<p>While the robots is running, rightclicking it will also open the form
(without the Start-button), so you can stop it, and access its inventory. </p>
<p>The code of a program is executed repeatedly, about once every second.</p>
<p>After editing the program of a running robot won't affect it until it is stopped and restarted. </p>
<h2>Capabilities</h2>
<ul>
<li>Energy</li>
</ul>
<p>The robot require energy to perform some actions like digging, smelting, grinding or compressing.
If the robot as the required fuel in its inventory (e.g. a coal lump ), it can be programmed to produce energy using <code>machine.generate_power("default:coal_lump")</code>
Energy level can be consulted using <code>machine.energy()</code>.
<!-- Note : Write about upgarde --></p>
<ul>
<li>Maxdig</li>
</ul>
<p>As said above, the robot program runs in a loop. This means that unless specificly asking it to stop, the robot will execute the code again and again and again... as long as it can.
Because of this, some operations can only be performed a limited number of time per run. These actions includes the ones mentionned above along with some other (including generating energy).
<!-- Note : A complete list would be good !--></p>
<p>If this limit is reached, the code won't execute and the robot will return this error message : <code>robot out of available operations in one step.</code>
The limit is sometime called 'maxdig' or 'maxoperations' (as the name of the variable defining it).
<!-- Note : Mabe store maxoperations in self in order to consult it in-game-->
<!-- Note : Setting the limit to 3 might be a good thing too-->
The default for this limit is 2. This means that when the robot is starting, there must be less than 2 operations in one loop.
For exemple, I cannot run this programm :</p>
<blockquote>
<p>machine.generate_power("default:jungletree") -- limited operation
dig.forward() -- limited operation
move.forward()
dig.up() -- limited operation</p>
</blockquote>
<p>According to this code, the robot should, generate some power from some jungle tree, dig forward, move foward, dig upwards and ... repeat itself. But it won't do any of that because that 3 limited operations would be performed in one execution</p>
<p>The trick would be increment a number to divide the steps, like this :</p>
<blockquote>
<p>if not i then i = 1 end --initialize i
if i == 1 then machine.generate_power("default:jungletree") <br />
elseif i == 2 then dig.forward() <br />
move.forward()
elseif i == 3 then dig.up() <br />
i = 0
end <br />
i = i + 1 </p>
</blockquote>
<p>In this exemple 'i' is set to 1 initially, and its value will be upped each time code is executed (because of the last line).
The first time the code is executed, 'i' is set to one, power will be generated <br />
The second time the code is executed, 'i' will be egal 2, the robot will dig forward and move forward,
The third time the code is executed, 'i' will be egal 3, the robot will dig up and reset the value of i to 0 so that after last line, it will be egal 1.
So the fourth time is like the first, the fifth is like the second, the sixth is like the third, and so on...</p>
<ul>
<li>Gravity</li>
</ul>
<p>Altough it doesn't see to be subject to gravity, the robot cannot hover over more than one block of air.
If asked to move toward a cliff, the robot will simply ignore the operation that would put him in a situation with too much void under...
... but it will continue executing its program regardless of the - sometme catastophic - consequences of this skipped step !</p>
<p><check> If despite this beahior, the robot ends up above more than one block of air, it will simply stop and disapear.</p>
<ul>
<li>Sound of a falling tree</li>
</ul>
<p>Quite unlike the eternal question 'Does a falling tree make sound if there is no one to hear it ?', we can answer this one : 'Does a robot worker works when there is no one around to see it ?'. And the answer is NO.</p>
<p>In minetest, areas that are not populated (with players), are unloaded.
So if you wander off leaving your robot to its work, it will soon stop working. But there is worse !
When you come back, you robot will be loaded again and will restart its program AT THE BEGINNING, regardless to were it stopped. Resulting (again) in a giant mess due to a few steps skipped. So, ... Be warned</p>
<hr>
<a name="robot_doc_2.md"></a><a href="#top">-- Return to Index --</a><br>
<!--
-- title : About Robots - The short version
-- author :
-- description : Short Guide to Robots Usage
-- source : parts from https://wiki.minetest.net/Mods/basic_robot
-->
<h1>Things to know about Robots</h1>
<p>A few things to know about robots, summarized in a short lists</p>
<ul>
<li>The robot can do a lot of things, They execute programs written in Lua.</li>
<li><p>There is also a remote control.</p></li>
<li><p>You can write code in the spawner ('cpu-box') and run it with the 'start' button. The 'worker' will appear above the spawns block and execute the code from there.</p></li>
<li><p>The spwaner looks like a ... cpu-box while the robot looks like a simple box, with a face on its frontside, and an arrow on top. </p></li>
<li><p>Standard users are allowed to control 2 robots. Each spawner can only run one bot at a time.</p></li>
<li><p>Robot can store things to its inventory (e.g. materials gathered while digging), they can also take thing from it (e.g. to produce energy)</p></li>
<li><p>The robot can read and write books contained in their 'library'. This can be used for program code, input and output. </p></li>
<li><p>To pick up the spawner, its inventory and library must be empty (like a chest).</p></li>
<li><p>The code of a program is executed repeatedly, about once every second.</p></li>
<li><p>The robot require energy to perform some actions like digging, smelting, grinding or compressing.</p></li>
<li><p>Some operations can only be performed a limited number of time per run otherwise the robot will return this error message : <code>robot out of available operations in one step.</code></p></li>
</ul>
<p>(The trick would be increment a number to divide the steps, like this )</p>
<ul>
<li><p>The robot cannot hover over more than one block of air (and will skip the instruction to do so).</p></li>
<li><p>If you wander off leaving your robot to its work, it will stop and restart its program AT THE BEGINNING when you come back.</p></li>
</ul>
<hr>
<a name="robot_doc_3.md"></a><a href="#top">-- Return to Index --</a><br>
<!--
-- title : Robot API
-- author :
-- description : Robot functions and syntax usable inside robots
-- source : In-Game Help from basic_robot mod
-- converted to mod syntax but still importable into a text area
-- (...still waiting for in-game markdown parser)
-->
<h1>Robot API</h1>
<p>This document present most of functions and syntax usable inside robots
For details about behaviors, limitations and usages, see 'api<em>basic</em>robot'</p>
<h3>BASIC LUA SYNTAX</h3>
<p>most of basic lua syntax allowed</p>
<blockquote>
<p>if x==1 then A else B end
for i = 1, 5 do something end
while i&lt;6 do A; i=i+1; end</p>
</blockquote>
<p>arrays:</p>
<blockquote>
<p>myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}</p>
</blockquote>
<p>access table entries with myTable1[1] or myTable2.entry1 or myTable2[\"entry1\"]</p>
<p>( See lua syntax for more detailled informations )</p>
<h2>ROBOT COMMANDS</h2>
<h3>MOVEMENT,DIGGING, PLACING, INVENTORY TAKE/INSERT</h3>
<p><code>move.direction()</code> where direction is forward, backward, left,right, up, down), forward<em>down direction only works with dig, place and read</em>node
<code>turn.left()</code>, <code>turn.right()</code>, <code>turn.angle(45)</code>
<code>dig.direction()</code>
<code>place.direction(\"default:dirt\", optional orientation param)</code>
<code>read_node.direction()</code> tells you names of nodes
<code>insert.direction(item, inventory)</code> inserts item from robot inventory to target inventory
<code>check_inventory.direction(itemname, inventory, index)</code> looks at node and returns false/true, direction can be self,
if index>0 it returns itemname. if itemname == \"\" it checks if inventory empty
<code>activate.direction(mode)</code> activates target block
<code>pickup(r)</code> picks up all items around robot in radius r&lt;8 and returns list or nil
<code>craft(item,mode)</code> crafts item if required materials are present in inventory. mode = 1 returns recipe
<code>take.direction(item, inventory)</code> takes item from target inventory into robot inventory
<code>read_text.direction(stringname,mode)</code> reads text of signs, chests and other blocks, optional stringname for other meta, mode 1 read number
<code>write_text.direction(text,mode)</code> writes text to target block as infotext</p>
<h3>BOOKS/CODE</h3>
<p><code>title,text=book.read(i)</code> returns title,contents of book at i-th position in library
<code>book.write(i,title,text)</code> writes book at i-th position at spawner library
<code>code.run(text)</code> compiles and runs the code in sandbox
<code>code.set(text)</code> replaces current bytecode of robot
<code>find_nodes(\"default:dirt\",3)</code> returns distance to node in radius 3 around robot, or false if none</p>
<h3>PLAYERS</h3>
<p><code>find_player(3)</code> finds players in radius 3 around robot and returns list, if none returns nil
<code>attack(target)</code> attempts to attack target player if nearby
<code>grab(target)</code> attempt to grab target player if nearby and returns true if succesful
<code>player.getpos(name)</code> return position of player, player.connected() returns list of players</p>
<h3>ROBOT</h3>
<p><code>say(\"hello\")</code> will speak
<code>self.listen(0/1)</code> (de)attaches chat listener to robot
<code>speaker, msg = self.listen_msg()</code> retrieves last chat message if robot listens
<code>self.send_mail(target,mail)</code> sends mail to target robot
<code>sender,mail = self.read_mail()</code> reads mail, if any
<code>self.pos()</code> returns table {x=pos.x,y=pos.y,z=pos.z}
<code>self.name()</code> returns robot name
<code>self.set_properties({textures=.., visual=..,visual_size=.., , )</code> sets visual appearance
<code>set_animation(anim_start,anim_end,anim_speed,anim_stand_start)</code> set mesh animation
<code>self.spam(0/1)</code> (dis)enable message repeat to all
<code>self.remove()</code> stops program and removes robot object
<code>self.reset()</code> resets robot position
<code>self.spawnpos()</code> returns position of spawner block
<code>self.viewdir()</code> returns vector of view for robot
<code>self.fire(speed, pitch,gravity)</code> fires a projectile from robot
<code>self.fire_pos()</code> returns last hit position
<code>self.label(text)</code> changes robot label
<code>self.display_text(text,linesize,size)</code> displays text instead of robot face, if no size return text
<code>self.sound(sample,volume)</code> plays sound named 'sample' at robot location
rom is aditional table that can store persistent data, like <code>rom.x=1</code>
.</p>
<h3>KEYBOARD</h3>
<p>place spawner at coordinates (20i,40j+1,20k) to monitor events</p>
<p><code>keyboard.get()</code> returns table {x=..,y=..,z=..,puncher = .. , type = .. } for keyboard event
<code>keyboard.set(pos,type)</code> set key at pos of type 0=air, 1..6, limited to range 10 around
<code>keyboard.read(pos)</code> return node name at pos</p>
<h3>TECHNIC FUNCTIONALITY</h3>
<p>namespace 'machine'. most functions return true or nil, error</p>
<p><code>machine.energy()</code> displays available energy
<code>machine.generate_power(fuel, amount)</code> = energy, attempt to generate power from fuel material
if amount>0 try generate amount of power using builtin generator - this requires 40 gold/mese/diamonblock upgrades for each 1 amount
<code>machine.smelt(input,amount)</code> = progress/true. works as a furnace
if amount>0 try to use power to smelt - requires 10 upgrades for each 1 amount, energy cost is 1/40*(1+amount)
<code>machine.grind(input)</code> grinds input material, requires upgrades for harder material
<code>machine.compress(input)</code> requires upgrades - energy intensive process
<code>machine.transfer_power(amount,target_robot_name)</code></p>
<h2>CRYPTOGRAPHY</h2>
<p>namespace 'crypto'</p>
<p><code>crypto.encrypt(input,password)</code> returns encrypted text, password is any string
<code>crypto.decrypt(input,password)</code> attempts to decrypt encrypted text
<code>crypto.scramble(input,randomseed,sgn)</code> (de)permutes text randomly according to sgn = -1,1
<code>crypto.basic_hash(input,n)</code> returns simple mod hash from string input within range 0...n-1</p>
<hr>
<a name="robot_doc_4.md"></a><a href="#top">-- Return to Index --</a><br>
<!--
-- title : Lua Syntax
-- author :
-- description : Lua syntax allowed in basic_robot scripts
-- source :
-->
<h3>Lua Syntax</h3>
<p>... TODO</p>
<hr>
<a name="robot_doc_5.md"></a><a href="#top">-- Return to Index --</a><br>
<!--
-- title : Robot API (admin)
-- author :
-- description : Robot functions and syntax usable inside robots with admin privilege
-- source :
-->
<h3>Robot API (Admin)</h3>
<p>... TODO</p>

181
scripts/admin_1.lua Normal file
View File

@ -0,0 +1,181 @@
-- title : avl_tree~
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
-- 1. MEMORY MANAGEMENT using array with addresses
-- note: this is all done "automatic" in c with malloc
mem = {};
mem.size = 10; -- how many available addresses in memory for each of internally used arrays
mem.freestk = {}; -- stack of free addresses in memory
mem.stkidx = mem.size;
for i = 1,mem.size do mem.freestk[i]=mem.size-i+1 end -- so: freestk = {memsize, memsize-1,...., 2, 1 } and head is at last one
mem.allocate = function() -- pop free spot from stack
if mem.stkidx>0 then
mem.stkidx = mem.stkidx -1
return mem.freestk[mem.stkidx+1]
end
end
mem.free = function(addr) -- release spot and mark it as free
if mem.stkidx>=mem.size then -- cant release anymore, all is free
return
end
mem.stkidx = mem.stkidx +1;
mem.freestk[mem.stkidx ] = addr;
end
-- 2. BALANCED BINARY SEARCH TREES USING POINTERS created with above MEMORY MANAGEMENT
idx = mem.allocate();
tree = {};
tree.data = { root = 0, count = 0, -- current root, element count
left = {0}, parent = {0}, right = {0}, -- links
key = {0}, -- value
heightl = {0}, heightr = {0} -- needed for balancing
};
-- root: idx of root element, count: how many elements in tree
-- 6 arrays with data for node stored at address i in memory:
-- left[i] == left child, right[i] = right child, parent[i] = parent, key[i] = stored value, if value left[i]== 0 then no left child...
-- heightl[i] = height of left subtree, heightr[i] = height of right subtree
-- NOTE: initially a root node is added
tree.insert = function (value)
local data = tree.data;
local idx = data.root;
if data.count == 0 then data.key[idx] = key; data.count = 1 return true end -- just set root key and exit
local key;
local cidx; -- child idx
while (true) do -- insert into tree by walking deeper and doing comparisons
key = data.key[idx];
if value<key then
cidx = data.left[idx];
else
cidx = data.right[idx];
end
if cidx == 0 then -- we hit nonexistent child, lets insert here
cidx = mem.allocate();
if not cidx then return "out of space" end
data.count = data.count+1
data.key[cidx] = value;
data.parent[cidx]=idx;data.left[cidx]=0;data.right[cidx]=0;
data.heightl[cidx]=0;data.heightr[cidx]=0;
-- update all heights for parents and find possible violation of balance property
local vidx = 0; -- index where violation happens
local vdir = 0; -- type of violation: 1: in left tree , 2: in right tree
while (idx~=0) do
if data.left[idx] == cidx then
data.heightl[idx]=data.heightl[idx]+1
else
data.heightr[idx]=data.heightr[idx]+1;
end
if data.heightl[idx]>data.heightr[idx]+1 then
vidx = idx; vdir = 1
elseif data.heightr[idx]>data.heightl[idx]+1 then
vidx = idx; vdir = 2
end
cidx = idx; -- set new child
idx = data.parent[idx]; -- set new parent
end
if vidx~=0 then
say("violation vidx " .. vidx .. " direction " .. vdir)
-- TO DO: apply internal tree rotation to restore balance
if vdir == 1 then -- left violation
--[[ need to reconnect 3 nodes:
D <- vidx C
/ \ / \
C E => A D
/ \ / \
A B B E
CHANGES:
C: new parent is old parent of D, new children are A,D
B: new parent D,
D: new parent is C, new children are B,E
--]]
local Didx = vidx; local Dpar = data.parent[Didx];
local Cidx = data.left[Didx];
local Bidx = data.right[Cidx];
local Aidx = data.left[Cidx];
data.parent[Cidx] = Dpar;data.left[Cidx] = Aidx;data.right[Cidx] = Didx;
data.parent[Bidx] = Didx;
data.parent[Didx] = Cidx;data.left[Didx] = Bidx;
else -- right violation
--[[ need to reconnect 3 nodes:
B <-vidx C
/ \ / \
A C => B E
/ \ / \
D E A D
CHANGES:
B: new parent C, new children A,D
C: new parent is old parent of B, new children are B,E
D: new parent is B
--]]
local Bidx = vidx; local Bpar = data.parent[Bidx];
local Cidx = data.right[Bidx];
local Didx = data.left[Cidx];
data.parent[Bidx] = Cidx;data.right[Bidx] = data.left[Cidx]
data.parent[Cidx] = Bpar;data.left[Cidx] = Bidx;
data.parent[Didx] = Bidx;
end
end
else
-- we go deeper
idx = cidx;
end
end
tree.find = function(value)
local idx = data.root;
while (idx~=0) do
key = data.key[idx];
if key == value then return idx end
if value<key then
idx = data.left[idx];
else
idx = data.right[idx];
end
end
end
tree.next = function(idx)
local right = data.right[idx];
local nidx = 0;
if right~=0 then
--TO DO: smallest idx in right subtree
else
if data.parent[idx]==0 then
return 0
end
end
end
tree.remove = function(idx)
--TO DO :
-- 1.find next element
-- put it where idx was and move subtree..
end
end

89
scripts/admin_10.lua Normal file
View File

@ -0,0 +1,89 @@
-- title : perm2cycles
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not perm2cycles then
perm2cycles = function(perm)
local n = #perm;
local i = 1; -- already reached number
local ret = {};
local visited = {};
local step = 0;
while (true) do
local cycle = {i}
local j=i;
while (true) do
step = step +1
if step > 2*n then return {} end
j=perm[j];
visited[j] = 1;
if j and j~=cycle[1] then cycle[#cycle+1]=j else break end
end
i=n+1;
for k=1,n do -- find smallest non visited yet
if not visited[k] and k<i then i = k end
end
ret[#ret+1] = cycle;
if i == n+1 then return ret end
end
end
random_permute = function(arr)
local n = #arr;
for i = n,3,-1 do
local j = math.random(i);
local tmp = arr[j]; arr[j] = arr[i]; arr[i] = tmp;
end
end
get_permutations = function(n)
free = {}; --[1] = {stack of free for 1 : head, a1,..., a_head}
isfree = {}; -- [i]=1 if item i free, 0 if not
current = 1; --{1,..., current element, ... , n}
selection = {};
local data = free[1]; for i=1,n do data[i]=i isfree[i]=1 end data[0] = n;
--1. pick free spot from free stack ( if not possible backtrack ) and move on onto next element ( if not possible stay at this one)
-- backtrack: current--
local data = free[current];
if data[0]<1 then -- backtrack
isfree[selection[current]] = 1;
current = current - 1;
if current <=0 then return end -- exhausted all options
else
local i = data[data[0]]; -- free possibility
selection[current] = i; isfree[i] = 0;
data[0]=data[0]-1; -- pop the stack
--try move forward
if current<n then
current = current+1;
-- get new free spots for new current
local data = free[current]; data = {};
for i = 1,n do if isfree[i] == 1 then data[#data+1]=i; break end end;
data[0]=#data;
if data[0] == 0 then -- nothing free, backtrack
isfree[selection[current]] = 1;
current = current - 1;
end
end
end
end
arr2string = function(arr)
return string.gsub(_G.dump(arr),"\n","")
end
local arr = {}; for i =1,10 do arr[i] =i end; random_permute(arr);
local cycles = perm2cycles(arr);
say("random permutation = " .. arr2string(arr) .. " => cycles = " .. arr2string(cycles) )
end

110
scripts/admin_11.lua Normal file
View File

@ -0,0 +1,110 @@
-- title : radix_sort
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not integer_sort then
-- sorts input according to keys, changes ordering to permutation corresponding to new order
integer_sort = function(input, keys, ordering,temp_ordering, m) -- input, keys same length n, m = how many keys, O(2*n+m)
local n = #input;
local freq = {} -- frequencies of keys
local kpos = {} -- position of keys in sorted array
for i=1,n do -- count how many occurences - O(n)
local key = keys[ordering[i]]+1;
freq[key]=(freq[key] or 0)+1
temp_ordering[i]=ordering[i];
end
local curpos = 1;kpos[1]=1;
for i =1,m-1 do -- determine positions of keys in final sorted array - O(m)
curpos = curpos + (freq[i] or 0);
kpos[i+1]=curpos -- {2=3, 3 = 6,..., n-1 = 321}
end
-- actually place values here
for i = 1,n do -- O(n)
local key = keys[temp_ordering[i]]+1;
local pos = kpos[key];
ordering[pos] = temp_ordering[i];
kpos[key]=kpos[key]+1; -- move to next spot for that key place
end
end
permutate = function(input,ordering)
local output = {};
for i =1,#input do
output[i] = input[ordering[i]]
end
return output
end
get_digits = function(Num,d,base) -- number, how many digits, what base
local digits = {};
local num = Num;
local r;
for i = 1, d do
r = num % base;
digits[#digits+1] = r;
num = (num-r)/base
end
return digits
end
dumparr = function(array)
if _G.type(array) ~= "table" then return array end
local ret = "{";
for i =1,#array-1 do
ret = ret .. dumparr(array[i]) .. ","
end
ret = ret .. dumparr(array[#array])
return ret .. "}"
end
radix_sort = function(input,d,base) -- array of numbers; base is also number of keys = m, d = how many steps of sorting = number of digits for single number
out = out .."\nRADIX SORT\n\narray to be sorted " .. dumparr(input)
local n = #input;
local ordering = {}; local temp_ordering = {}; for i = 1, n do ordering[i]=i end
local keys = {};
local keylist = {};
for i = 1,n do
keylist[i] = get_digits(input[i],d,base)
end
out = out .."\nlist of keys - ".. d .. " digits of base " .. base .. " expansion of numbers : \n" ..
dumparr(keylist)
for step = 1, d do
for i =1,n do
keys[i] = keylist[i][step];
end
integer_sort(input, keys, ordering, temp_ordering, base)
out = out .."\n"..step .. ". pass integer_sort : " .. dumparr(permutate(input,ordering))
end
out = out .. "\nradix sort final result : " .. dumparr(permutate(input,ordering))
end
--input = {"a","b","c","d","e"}
--keys = {5,3,4,1,2}
--ordering = {}; temp_ordering = {}; for i = 1, #input do ordering[i]=i end
--m=5;
--integer_sort(input, keys, ordering, temp_ordering,m);
--say(string.gsub(_G.dump(ordering),"\n",""))
--say(string.gsub(_G.dump(permutate(input,ordering)),"\n",""))
out = ""; self.label("")
input = {23,42,15,8,87};
radix_sort(input,5,3) -- d, base
self.display_text(out,60,3)
end

96
scripts/admin_12.lua Normal file
View File

@ -0,0 +1,96 @@
-- title : serialization
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not itemlist then
inv2string = function(name)
local inv = _G.minetest.get_player_by_name(name):get_inventory()
local list = ""
for i = 1,32 do
local item = inv:get_stack("main", i):to_string()
list = list .. item .. ","
end
return name .. "," .. list
end
string2inv = function(str)
local i = string.find(str,",");
local name = string.sub(str,1,i-1);
local step = 0
local invlist = {};
while i and step < 33 do
step = step +1
local i1 = string.find(str,",",i+1)
if not i1 then break end
invlist[#invlist+1]=string.sub(str,i+1,i1-1)
i=i1
end
return name, invlist
end
array2string = function(array)
if _G.type(array) ~= "table" then return array end
local ret = "{";
for i =1,#array-1 do
ret = ret .. array2string(array[i]) .. ","
end
ret = ret .. array2string(array[#array])
return ret .. "}"
end
string2array = function(str)
if not string.find(str,"{") then return str end
local lvl = 1; local n = string.len(str);
local i1,i2,count;
count = 0; --1 = {}, 2 = ,
local arr = {}; i1 = 2;
for i =2,n do
local c = string.sub(str,i,i);
if c == "{" then
lvl = lvl+1
elseif c == "}" then
lvl = lvl -1
elseif c == "," and lvl == 1 then
i2 = i;
count = count+1
arr[count] = string2array(string.sub(str,i1,i2-1)); i1 = i2+1
end
end
if i1< n then count = count+1 ; arr[count] = string2array(string.sub(str,i1,n-1)) end
return arr
end
local arr = {{1,{2,4}},{3,4},{0,{2}},-2};
--local arr = {1,2,3}
self.spam(1)
say("original array : ".. string.gsub(_G.dump(arr),"\n","") )
local str = array2string(arr);
say("array2string : " .. str)
local arr1 = string2array(str);
say("string2array: " .. string.gsub(_G.dump(arr1),"\n",""))
player = find_player(2);
itemlist = {};
if player then
local list = inv2string(player[1])
local name,invlist = string2inv(list)
list = "UNIT TEST inv2string\n"..list .. "\nUNIT TEST string2inv\n".. "name = " .. name .."\n".. string.gsub(_G.dump(invlist),"\n","")
form = "size[8,8.5]" ..
"textarea[0,0;7.75,7.5;list;list;" .. list .. "]";
self.show_form(player[1],form)
end
end
sender,fields = self.read_form();
if sender then
if fields.list then
if string.sub(fields.list,1,3) == "DCL" then
local sel = tonumber(string.sub(fields.list,5)) or 1
say("you selected item " .. itemlist[sel])
end
end
if fields.quit then self.remove() end
end

148
scripts/admin_13.lua Normal file
View File

@ -0,0 +1,148 @@
-- title : shop
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not s then
s=0;item = 1; price =""; buyer = ""
_G.minetest.forceload_block(self.pos(),true)
_G.basic_robot.data[self.name()].obj:set_properties({nametag = ""})
self.listen(1);self.spam(1)
shoplist = {};
--scan shops:
pos = self.pos(); pos.y=pos.y-5; pos.x=pos.x-6
pos1 = {x=pos.x-8,y=pos.y-2,z=pos.z-8};pos2 = {x=pos.x+8,y=pos.y+2,z=pos.z+8};
local shoppos = _G.minetest.find_nodes_in_area(pos1, pos2, "shop:shop");
--say("scanning... i found " .. #shoppos .. " shops ");
count = 0
for _,p in pairs(shoppos) do
local inv = _G.minetest.get_meta(p):get_inventory()
local s = inv:get_list("sell");local b = inv:get_list("buy")
local k = s[1]:to_string();
local v = b[1]:to_string();
if (k and k~="") and (v and v~="") then count = count +1 shoplist[count] = {k,v} end
end
local f_sort = function(a,b) return a[1]<b[1] end;
table.sort(shoplist,f_sort)
itemlist = ""; count = 0;
shopinventory = {};
for k,v in pairs(shoplist) do
if v[1] then
count = count +1
itemlist = itemlist .. string.format("%-5s%-40s%-32s",k,v[1],v[2]) .. ","
end
end
t = 0; ct = 0; ci = 0
say("scanning shops ... completed. Added " .. count .. " items for sale ");
end
ct=ct+1
if ct%5 == 0 then
ct=0
ctext = "say #shop to buy";
if ci>0 and ci<=count then
if shoplist[ci] then
iname = shoplist[ci][1];local p=string.find(iname,":"); if p then iname = string.sub(iname,p+1) end
sname = shoplist[ci][2];p=string.find(sname,":"); if p then sname = string.sub(sname,p+1) end
ctext = "#SHOP ".. ci .."\n" ..iname .. "\nPRICE\n" .. sname
end
ci=ci+1
elseif ci == count+1 then ci=0
elseif ci == 0 then ci=1
end
text = "SHOP ROBOT"..os.date("%x") .." " .. os.date("%H:%M:%S").. "\n\n"..ctext
self.display_text(text,10,3);
end
speaker,msg = self.listen_msg()
if msg then
if s == 0 then
if string.sub(msg,1,5)=="#shop" then
if msg == "#shop" then
--say("say #shop command, where command is list OR armor OR item number. Example: #shop list or #shop 1")
local list = "1,2,3";
local form = "size[8,8.5]" ..
"label[0.,0.5;ID]"..
"label[0.4,0.5;BUY]"..
"label[3.,0.5;SELL]" ..
"field[7.2,0.25;1.,1;count;count;".. 1 .."]"..
"button[5.,-0.05;2.,1;ARMOR;ARMOR]"..
"textlist[0,1;7.75,7.5;list;" .. itemlist .. "]";
self.show_form(speaker,form)
end
end
elseif s == 1 then
if string.sub(msg,1,3)~="buy" then
t=t+1; if t>1 then say("timeout. trade cancelled."); s = 0 end
else
s=0
end
end
end
sender,fields = self.read_form()
if sender then
local sel = fields.list; --"CHG:3"
--say( string.gsub(_G.dump(fields),"\n",""))
if sel and string.sub(sel,1,3) == "DCL" then
local quantity = tonumber(fields.count) or 1;
local select = tonumber(string.sub(sel,5) or "") or 1;
local item, price
if shoplist[select] then
item,price = shoplist[select][1],shoplist[select][2];
end
local player = _G.minetest.get_player_by_name(sender);
if player and item and price then
local inv = player:get_inventory();
if quantity > 99 then quantity = 99 end
if quantity > 1 then
local k = 1;
local i = string.find(price," ");
if i then
k = tonumber(string.sub(price,i+1)) or 1
price = string.sub(price,1,i-1).. " " .. k*quantity
else
price = price.. " " .. quantity
end
k=1;i = string.find(item," ");
if i then
k = tonumber(string.sub(item,i+1)) or 1
item = string.sub(item,1,i-1).. " " .. k*quantity
else
item = item .. " " .. quantity
end
end
if inv:contains_item("main", price ) then
inv:remove_item("main",price)
inv:add_item("main",item)
_G.minetest.chat_send_player(sender,"#SHOP ROBOT: " .. item .. " sold to " .. sender .. " for " .. price)
else
_G.minetest.chat_send_player(sender,"#SHOP ROBOT: you dont have " .. price .. " in inventory ")
end
end
elseif fields.ARMOR then
local player = _G.minetest.get_player_by_name(sender);
if player then
local inv = player:get_inventory();
if inv:contains_item("main",_G.ItemStack("default:diamond 30")) then
player:set_armor_groups({fleshy = 50})
_G.minetest.chat_send_player(sender,"#SHOP ROBOT: you bought 50% damage reduction.")
else
_G.minetest.chat_send_player(sender,"#SHOP ROBOT: you need 30 diamonds to get armor effect")
end
end
end
end

30
scripts/admin_14.lua Normal file
View File

@ -0,0 +1,30 @@
-- title : simple_layout_gen
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not data then
m=50;n=50; minescount = m*n/10;
t0 = _G.minetest.get_gametime();
data = {}; spawnpos = self.spawnpos();
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
get_mine_count = function(i,j)
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
for k = -1,1 do for l = -1,1 do
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
end end
return count
end
for i = 1,m do for j = 1,n do
if get_mine_count(i,j) > 0 or (data[i] and data[i][j] == 1) then
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"})
else
_G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "default:dirt"})
end
end end
end
self.remove()

28
scripts/admin_15.lua Normal file
View File

@ -0,0 +1,28 @@
-- title : simple_parser
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not text then
text = "diamond 3;mese 4;gold 2;diamond 1;"
function parse(text)
ret = {};
for a,b in text:gmatch("(%w+) (%w+)%;") do
ret[a] = (ret[a] or 0) + (tonumber(b) or 0)
end
return ret
end
function export(array)
ret = "";
for k,v in pairs(array) do
ret = ret .. (_G.tostring(k) or "") .. " " .. (_G.tostring(v) or "") ..";"
end
return ret
end
say("input: " .. text)
local arr = parse(text);
say("parsed text: " .. string.gsub(_G.dump(arr),"\n",""))
say("back to string :" .. export(arr))
end

70
scripts/admin_16.lua Normal file
View File

@ -0,0 +1,70 @@
-- title : substring_search
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
-- RabinKarp substring s search in string t
-- https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm
-- rnd 2017
-- ALGORITHM:
-- 1.) loop compute hashes of all substrings of t of length |s| using rolling hash idea
-- 2.) if some hash matches hash of s check more closely ( waste |s| time here, so this should only occur with probability < O(1)/|s| so expected waste is O(1))
-- how to do 1) rolling hash: how does hash of string change if you remove first character and add new last character? ... dont need to recompute whole hash!
-- summary: we end up using O(|t|+|s| + (number of needed hits)*O(1)) time (if you want more than 1 hit..)
-- this is big improvement compared to O(|t|*|s|) when doing naive substring search
-- improvement: we could also precompute all substring hashes of length |s| and then compute hash of some string of same length and
-- do quick lookups for that hash ( hash of hash :) )
if not hash then
hash = function(s,p)
local length = string.len(s);
local h = 0 ;
for i = 1, length do
h=(256*h + string.byte(s,i))%p
end
return h%p
end
getpower = function(p,k) -- safe computation of power 256^k in mod p arithmetic
local r=1; for i = 1,k do r=(256*r) % p end; return r
end
karpin_rabin = function(t,s)
local ls = string.len(s);
local lt = string.len(t);
if lt<ls then return nil end
local p = 10011;
local hs = hash(s,p);
local rht = hash(string.sub(t,1,ls),p); -- rolling hash
if hs == rht then
if s == string.sub(t,1,ls) then return 1 end -- match at position 1!
end
local power256 = getpower(p,ls-1);
for i = 2,lt-ls+1 do
local c1 = string.byte(t,i-1); local c2 = string.byte(t,i+ls-1);
rht = (256*(rht - c1*power256)+c2)%p; -- update rolling hash to hash of next substring; this is ok since: a===b then also a*c===b*c
if hs == rht then
if s == string.sub(t,i,i+ls-1) then return i end -- match at position i!
end
end
return nil
end
local t = "The RabinKarp algorithm is inferior for single pattern searching to KnuthMorrisPratt algorithm, BoyerMoore string search algorithm and other faster single pattern string searching algorithms because of its slow worst case behavior. However, it is an algorithm of choice for multiple pattern search.";
local s = "inferior";
local chk = karpin_rabin(t,s)
if chk then say("found '" .. s .. "' in text pos. " .. chk .." : ..." .. string.sub(t,chk,chk+50) .. "...")
else say("could not find '"..s.."' in text")
end
end

97
scripts/admin_2.lua Normal file
View File

@ -0,0 +1,97 @@
-- title : battle_bot_arena
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not s then
-- init
bots = {[4] = {}, [5] = {}}; -- [type] = {{1,1,10}, {3,2,10}}; -- {x,y,hp}
arena = {}; --[x][z] = {type, idx}
for i = -10,10 do arena[i] = {} for j=-10,10 do arena[i][j] = {0,0} end end
centerpos = self.spawnpos(); centerpos.y = centerpos.y+2
TYPE = 4; -- 4,5 defines which bots are on the move/attack
DIR = 1
s=0
t=0
-- load user progs
_,script1 = book.read(1);_,script2 = book.read(2);
prog1, _ = _G.loadstring( script1 ); prog2, _ = _G.loadstring( script2 );
spawn_bot = function (x,z,type)
if arena[x] and arena[x][z] and arena[x][z][1] == 0 then
keyboard.set({x=centerpos.x+x,y=centerpos.y,z=centerpos.z+z},type)
table.insert(bots[type],{x,z,10})
arena[x][z] = {type,#bots[type]}
else
return false
end
end
move_bot = function (i,dx,dz)
local bot = bots[TYPE][i];if not bot then return false end
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
local x1=bot[1]+dx; local z1=bot[2]+dz;
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then else return false end
keyboard.set({x=centerpos.x+bot[1],y=centerpos.y,z=centerpos.z+bot[2]},0);
keyboard.set({x=centerpos.x+x1,y=centerpos.y,z=centerpos.z+z1},TYPE);
arena[bot[1]][bot[2]] = {0,0}
arena[x1][z1] = {TYPE,i}
bot[1]=x1;bot[2]=z1;
end
attack_bot = function(i,dx,dz)
local bot = bots[TYPE][i];if not bot then return false end
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
local x1=bot[1]+dx; local z1=bot[2]+dz;
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then return false end
local type = arena[x1][z1][1]; local idx = arena[x1][z1][2];
local tbot = bots[type][idx];
if not tbot then return false end
tbot[3]=tbot[3]-5;
if tbot[3]<=0 then
keyboard.set({x=centerpos.x+tbot[1],y=centerpos.y,z=centerpos.z+tbot[2]},0);
table.remove(bots[type],idx);
arena[x1][z1] = {0,0}
end
end
read_arena = function(x,z)
local data = arena[x][z];
if not data then return end
return {data[1],data[2]};
end
read_bots = function (type, idx)
local data = bots[type][idx];
if not data then return end
return {data[1],data[2],data[3]}
end
end
if t%10 == 0 then
spawn_bot(0,-10,4)
spawn_bot(0,10,5)
end
t=t+1
self.label(#bots[4] .. " " .. #bots[5])
-- PROGRAM RULES:
-- not allowed to modify api code: TYPE, bots,t,s, spawn_bot, move_bot, attack_bot, read_arena, read_bots
-- only allowed to move bot or attack, but not to dig/place
TYPE = 4+(t%2);
DIR = - DIR
if TYPE == 5 then
_G.setfenv(prog1, _G.basic_robot.data[self.name()].sandbox )
_,err = pcall(prog1)
else
_G.setfenv(prog2, _G.basic_robot.data[self.name()].sandbox )
_,err = pcall(prog2)
end
if err then say(err) self.remove() end

156
scripts/admin_3.lua Normal file
View File

@ -0,0 +1,156 @@
-- title : battle_minesweeper
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not data then
m=10;n=10;
players = {};
paused = true
turn = 2;
t = 0;
SIGNUP = 0; GAME = 1; INTERMISSION = 2
state = SIGNUP
t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines
data = {};
init_game = function()
data = {}; minescount = 32
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
minescount = 0;
for i = 1,m do for j = 1,n do -- render game
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
end
end end
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
end
get_mine_count = function(i,j)
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
for k = -1,1 do for l = -1,1 do
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
end end
return count
end
chk_mines = function()
local count = minescount;
for i=1,m do for j=1,n do
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
count=count-1
end
end end
return count
end
greeting = function()
_G.minetest.chat_send_all(colorize("red","#BATTLE MINESWEEPER : two player battle in minesweeper. say join to play.\nRules: 1. each player has 5 second turn to make a move 2. if you dont make move you lose\n3. if you make move in other player turn you lose. 4. if you hit bomb or mark bomb falsely you lose"))
end
player_lost = function ()
for i=1,#players do
local player = _G.minetest.get_player_by_name(players[i]);
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+10,z=spawnpos.z}) end
end
state = INTERMISSION; t = 0
end
function change_turn()
if turn == 1 then
_G.minetest.sound_play("default_break_glass",{pos=spawnpos, max_hear_distance = 100})
else
_G.minetest.sound_play("note_a",{pos=spawnpos, max_hear_distance = 100})
end
if paused == false then
say(players[turn] .. " lost : didn't make a move");
player_lost()
else
if turn == 1 then turn = 2 else turn = 1 end
self.label("turn " .. turn .. " " .. players[turn])
t=0
paused = false
end
end
init_game()
greeting()
self.listen(1)
end
if state == SIGNUP then
speaker,msg = self.listen_msg()
if speaker then
if msg == "join" then
players[#players+1] = speaker;
local plist = ""; for i=1,#players do plist = plist .. players[i] .. ", " end
_G.minetest.chat_send_all("BATTLE MINESWEEPER, current players : " .. plist)
if #players >= 2 then
state = GAME
change_turn();
keyboard.get(); t=0;
for i = 1, #players do
local player = _G.minetest.get_player_by_name(players[i]);
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z}) end
end
_G.minetest.chat_send_all(colorize("red","BATTLE MINESWEEPER " .. m .. "x" ..n .. " with " .. minescount .. " mines.\n" .. players[turn] .. " its your move!"))
end
end
end
elseif state == GAME then
t = t + 1;
if t>5 then -- change of turn
change_turn()
end
event = keyboard.get();
if event and event.type == 2 and not paused then
if event.puncher == players[turn] then
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
if x<1 or x>m or z<1 or z>n then
else
local ppos = player.getpos(event.puncher)
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
if data[x] and data[x][z] == 1 then
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
keyboard.set({x=event.x,y=event.y,z=event.z},2)
else
keyboard.set({x=event.x,y=event.y,z=event.z},3)
end
else
say(event.puncher .. " lost : marked a bomb where it was none! ");
player_lost()
end
else
if data[x] and data[x][z]==1 then
_G.minetest.sound_play("tnt_boom",{pos=spawnpos, max_hear_distance = 100})
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3)
say(event.puncher .. " lost : punched a bomb! ");
player_lost()
else
local count = get_mine_count(x,z);
if count == 0 then keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4)
else keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},7+count) end
end
end
end
paused = true
else
say(event.puncher .. " lost : played out of his/her turn"); player_lost()
end
end
elseif state == INTERMISSION then
t=t+1; if t> 15 then state = SIGNUP;players = {}; paused = true; init_game(); greeting() end
end

201
scripts/admin_4.lua Normal file
View File

@ -0,0 +1,201 @@
-- title : blackbox_game
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
--black box by rnd, 03/18/2017
--https://en.wikipedia.org/wiki/Black_Box_(game)
if not data then
m=8;n=8;turn = 0;
attempts = 1;
self.spam(1);t0 = _G.minetest.get_gametime();
spawnpos = self.spawnpos()
data = {};
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
for i=1,4 do -- put in 4 atoms randomly
data[math.random(m)][math.random(n)] = 1
end
render_board = function(mode) -- mode 0 : render without solution, 1: render solution
for i = 1,m do for j = 1,n do -- render game
if mode == 0 or data[i][j] == 0 then
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
end
else
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},3)
end
end end
end
get_dirl = function(dir)
local dirl; -- direction left
if dir[1] > 0.5 then dirl = {0,-1}
elseif dir[1] < -0.5 then dirl = {0,1}
elseif dir[2] > 0.5 then dirl = {-1,0}
elseif dir[2] < -0.5 then dirl = {1,0}
end
return dirl
end
read_pos = function(x,z)
if x<1 or x>m or z<1 or z>n then return nil end
return data[x][z]
end
newdir = function(x,z,dir) -- where will ray go next
local retdir = {dir[1],dir[2]};
local xf = x+dir[1]; local zf = z+dir[2] -- forward
local dirl = get_dirl(dir)
local nodef = read_pos(xf,zf)
local nodel = read_pos(xf + dirl[1],zf + dirl[2])
local noder = read_pos(xf - dirl[1],zf - dirl[2])
if nodef == 1 then
retdir = {0,0} -- ray hit something
elseif nodel == 1 and noder ~= 1 then
retdir = {-dirl[1],-dirl[2]}
elseif nodel ~= 1 and noder == 1 then
retdir = {dirl[1],dirl[2]}
elseif nodel == 1 and noder == 1 then
retdir = {-dir[1],-dir[2]}
end
return retdir
end
shootray = function(x,z,dir)
--say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2])
local xp = x; local zp = z;
local dirp = {dir[1],dir[2]};
local maxstep = m*n;
for i = 1,maxstep do
dirp = newdir(xp,zp,dirp);
if dirp[1]==0 and dirp[2]==0 then return -i end -- hit
xp=xp+dirp[1];zp=zp+dirp[2];
if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out
end
return 0 -- hit
end
count = 0; -- how many letters were used up
border_start_ray = function(x,z)
local rdir
if x==0 then rdir = {1,0}
elseif x == m+1 then rdir = {-1,0}
elseif z == 0 then rdir = {0,1}
elseif z == n+1 then rdir = {0,-1}
end
if rdir then
local result,out = shootray(x,z,rdir);
if result >= 0 then
if out then
if out[1]==x and out[2]==z then -- got back where it originated, reflection
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},1);
else
if result<=1 then
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off
else
local nodename = "default:obsidian_letter_"..string.char(97+count) .. "u";
_G.minetest.set_node(
{x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]},
{name = nodename, param2 = 1})
_G.minetest.set_node(
{x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z},
{name = nodename, param2 = 1})
count = count + 1;
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4);
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4);
end
end
end
elseif result<0 then
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit
end
end
end
-- initial border loop and marking
--render blue border
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+0},5) keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+n+1},5) end
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y,z=spawnpos.z+j},5) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y,z=spawnpos.z+j},5) end
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+0},0) keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+n+1},0) end
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y+1,z=spawnpos.z+j},0) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y+1,z=spawnpos.z+j},0) end
z=0 -- bottom
for x = 1,m do
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
border_start_ray(x,z)
end
end
x=m+1 -- right
for z = 1,n do
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
border_start_ray(x,z)
end
end
z=n+1 -- top
for x = m,1,-1 do
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
border_start_ray(x,z)
end
end
x=0 -- left
for z = n,1,-1 do
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
border_start_ray(x,z)
end
end
check_solution = function()
for i = 1,m do
for j = 1,n do
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonFF8080" then -- red
if data[i][j]~=1 then return false end
else
if data[i][j]~=0 then return false end
end
end
end
return true
end
--render board
render_board(0)
keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},5)
end
event = keyboard.get();
if event then
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
if x<1 or x>m or z<1 or z>n then
if event.type == 5 then
if check_solution() then
say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove()
else
say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.")
attempts = attempts+1
end
end
else -- interior punch
nodetype = 2;
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button808080" then
nodetype = 3
end
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype);
end
end
::END::

114
scripts/admin_5.lua Normal file
View File

@ -0,0 +1,114 @@
-- title : hash_table_implementation
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not get_hash then
get_hash = function(s,p)
if not s then return end
local h = 0; local n = string.len(s);local m = 4; -- put 4 characters together
local r = 0;local i = 0;
while i<n do
i=i+1;r = 256*r+ string.byte(s,i);
if i%m == 0 then h=h+(r%p) r=0 end
end
if i%m~=0 then h=h+(r%p) end
return h%p
end
hashdb = {}; --array with entries: [hash] = list of possible hits
insert = function(key,value)
local hash = get_hash(key,10011);
local data = hashdb[hash]; if data == nil then hashdb[hash] = {}; data = hashdb[hash] end
data[#data+1]={key,value};
return hash
end
lookup = function(key)
local hash = get_hash(key,10011);
if not hash then return end
local data = hashdb[hash];
if not data then return nil end
for i = 1,#data do
if data[i][1]==key then return data[i][2] end
end
return nil
end
analyse = function()
local count = 0; local maxlen = 0; local maxidx = 1; local n = #hashdb;
for i = 1, 10000 do
local data = hashdb[i];
if data then
local length = #data;
if length > maxlen then maxlen = length; maxidx = i end
count = count + 1
end
end
if maxlen>0 then
local data = hashdb[maxidx];
say("number of used hash entries is " .. count .. ", average " .. (step/count) .. " entries per hash, "..
" max length of list is " .. maxlen .. " at hash ".. maxidx )--.. " : " ..
--string.gsub(_G.dump(data),"\n","") )
end
end
-- LOAD DICTIONARY WORDS into hashtable
lang = "german"
fname = "F:\\games\\rpg\\minetest-0415server\\mods\\basic_translate\\"..lang;
local f = _G.assert(_G.io.open(fname, "r"));local dicts = f:read("*all");f:close()
step = 0; maxwords = 10000;
i=0
dict = {}; -- for comparison
while(step<maxwords) do
step=step+1
i1 = string.find(dicts,"\t",i+1)
i2 = string.find(dicts,"\n",i+1)
if not i2 then break end
local word = string.sub(dicts, i+1,i1-1);
local tword = string.sub(dicts, i1+1,i2-1);
insert(word, tword) -- load into hashtable
dict[word]=tword
i=i2
end
self.listen(1)
self.spam(1)
say(step .. " words loaded")
end
-- handle chat event
speaker,msg = self.listen_msg()
if msg then
if msg == "?*" then
local n = 1000000; local msg = "hello"
local ret = "";
local t1 = os.clock();
for i = 1, n do ret = lookup(msg) end; t1 = os.clock()-t1;
local t2 = os.clock();
for i = 1, n do ret = dict[msg] end
t2 = os.clock()-t2;
say(t1 .. " " .. t2)
elseif msg == "??" then
analyse()
elseif string.sub(msg,1,1)=="?" then
msg = string.sub(msg,2);
local ret = lookup(msg);
if ret then say("found entry for " .. msg .. " : " .. ret) else say("entry not found") end
end
end

44
scripts/admin_6.lua Normal file
View File

@ -0,0 +1,44 @@
-- title : messenger
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not s then
s=0
msgsize = 2500;
_,text = book.read(1);text = text or "";
write_msg = function(sender,msg)
local newsize = string.len(text)+string.len(msg);
if newsize>msgsize then return "messages space exceeded" end
text = text .. "\n"..os.date() .. " " .. sender .. ": " .. msg;
book.write(1,"messages",text)
end
end
--textarea[X,Y;W,H;name;label;default]
--button[X,Y;W,H;name;label]
if s == 0 then
players = find_player(4);
if players and players[1] then
s=1
local form = "size[8,4.5]" ..
"textarea[0,0;9,4.5;msg;MESSAGE FOR ADMIN;]"..
"button_exit[-0.5,4.15;2,1;send;send]"
self.show_form(players[1],form)
end
elseif s==1 then
sender,fields = self.read_form();
if sender then
if fields.send then
msg = fields.msg;
if msg and msg~="" then
write_msg(sender,msg);activate.up(1)
_G.minetest.chat_send_player(sender,"#mailbot: message has been stored")
end
end
self.remove()
end
end

78
scripts/admin_7.lua Normal file
View File

@ -0,0 +1,78 @@
-- title : minesweeper_game
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not data then
m=10;n=10; minescount = 32;
t0 = _G.minetest.get_gametime();
data = {}; spawnpos = self.spawnpos() -- place mines
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
minescount = 0;
for i = 1,m do for j = 1,n do -- render game
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
end
end end
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
get_mine_count = function(i,j)
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
for k = -1,1 do for l = -1,1 do
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
end end
return count
end
chk_mines = function()
local count = minescount;
for i=1,m do for j=1,n do
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
count=count-1
end
end end
return count
end
say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ")
self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).")
end
event = keyboard.get();
if event then
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
if x<1 or x>m or z<1 or z>n then
if x == 0 and z == 1 then
local count = chk_mines();
if count == 0 then
t0 = _G.minetest.get_gametime() - t0;
say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s")
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.itemstack("default:diamond 5")) -- diamond reward
else
say("FAIL! " .. count .. " mines remaining ")
end
self.remove()
end
elseif event.type == 2 then
local ppos = player.getpos(event.puncher)
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
keyboard.set({x=event.x,y=event.y,z=event.z},2)
else
keyboard.set({x=event.x,y=event.y,z=event.z},3)
end
else
if data[x] and data[x][z]==1 then
say("boom! "..event.puncher .. " is dead ");keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3);self.remove()
else
local count = get_mine_count(x,z);
if count == 0 then keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4)
else keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},7+count) end
end
end
end
end

78
scripts/admin_8.lua Normal file
View File

@ -0,0 +1,78 @@
-- title : minesweeper
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not data then
m=10;n=10; minescount = 32;
t0 = _G.minetest.get_gametime();
data = {}; spawnpos = self.spawnpos() -- place mines
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
minescount = 0;
for i = 1,m do for j = 1,n do -- render game
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
end
end end
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
get_mine_count = function(i,j)
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
for k = -1,1 do for l = -1,1 do
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
end end
return count
end
chk_mines = function()
local count = minescount;
for i=1,m do for j=1,n do
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
count=count-1
end
end end
return count
end
say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ")
self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).")
end
event = keyboard.get();
if event then
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
if x<1 or x>m or z<1 or z>n then
if x == 0 and z == 1 then
local count = chk_mines();
if count == 0 then
t0 = _G.minetest.get_gametime() - t0;
say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s")
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.itemstack("default:diamond 5")) -- diamond reward
else
say("FAIL! " .. count .. " mines remaining ")
end
self.remove()
end
elseif event.type == 2 then
local ppos = player.getpos(event.puncher)
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
keyboard.set({x=event.x,y=event.y,z=event.z},2)
else
keyboard.set({x=event.x,y=event.y,z=event.z},3)
end
else
if data[x] and data[x][z]==1 then
say("boom! "..event.puncher .. " is dead ");keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3);self.remove()
else
local count = get_mine_count(x,z);
if count == 0 then keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4)
else keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},7+count) end
end
end
end
end

190
scripts/admin_9.lua Normal file
View File

@ -0,0 +1,190 @@
-- title : multiplication
-- author : rnd
-- description :
-- source : https://github.com/ac-minetest/basic_robot/
--
if not number then
number = {};
number.base = 10; -- what base: 2-10
number.data = {};
number.size = -1;
function number:tostring()
local ret = ""
--say("tostring size " .. self.size)
for i = self.size,1,-1 do ret = ret .. (self.data[i] or "X").."" end
return ret
end
function number:new(data)
local o = {};_G.setmetatable(o, self); o.data = {};
for i = 1,#data do o.data[i] = data[i] end -- do copy otherwise it just saves reference
o.size = #data;
self.__index = self; return o
end
number.add = function (lhs, rhs,res)
local n1 = lhs.size;local n2 = rhs.size;local n = math.max(n1,n2);
local carry=0, sum; local base = lhs.base;
local out = false;
if not res then res = number:new({}) out = true end
local data = res.data
for i = 1,n do
sum = (lhs.data[i] or 0)+(rhs.data[i] or 0)+carry;
if carry>0 then carry = 0 end
if sum>=base then data[i]=sum-base; carry = 1 else data[i] = sum end
end
if carry>0 then data[n+1]=1 res.size = n+1 else res.size = n end
if out then return res end
end
number.__add = add;
function number:set(m)
local data = self.data;
local mdata = m.data;
for i=1,#mdata do
data[i]=mdata[i];
end
self.size = m.size;
end
-- 'slow' long multiply
number.multiply = function (lhs, rhs, res)
local n1 = lhs.size;local n2 = rhs.size;local n = n1+n2;
--say("multiply sizes " .. n1 .. "," .. n2)
local out = false;
if not res then res = number:new({}); out = true end;
res.size = n1+n2-1;
res.data = {} -- if data not cleared it will interfere with result!
local data = res.data;
local c,prod,carry = 0; local base = lhs.base;
for i=1,n1 do
carry = 0;
c = lhs.data[i] or 0;
for j = 1,n2 do -- multiply with i-th digit and add to result
prod = (data[i+j-1] or 0)+c*(rhs.data[j] or 0)+carry;
carry = math.floor(prod / base);
prod = prod % base;
data[i+j-1] = (prod)%base;
end
if carry>0 then data[i+n2] = (data[i+n2] or 0)+ carry ;if res.size<i+n2 then res.size = i+n2 end end
end
if out then return res end
end
--TO DO
number.karatsuba_multiply = function(lhs,rhs,res)
local n1 = lhs.size;local n2 = rhs.size;
local n0 = math.max(n1,n2);
if n0< 2 then -- normal multiply for "normal" sized numbers
number.multiply(lhs, rhs, res)
return
end
n0 = math.floor(n0/2);
local n = n1+n2;
local a1 = number:new({});
local tdata = a1.data; local sdata = lhs.data;
for i = 1,n0 do tdata[i] = sdata[i] or 0 end; a1.size = n0;
local a2 = number:new({}); tdata = a2.data;
for i = n0+1,n1 do tdata[i-n0] = sdata[i] or 0 end; a2.size = n1-n0;
local b1 = number:new({});
local tdata = b1.data; sdata = rhs.data;
for i = 1,n0 do tdata[i] = sdata[i] or 0 end; b1.size = n0;
local b2 = number:new({}); tdata = b2.data;
for i = n0+1,n1 do tdata[i-n0] = sdata[i] or 0 end; b2.size = n1-n0;
--say("a1 " .. a1:tostring())
--say("a2 " .. a2:tostring())
--say("b1 " .. b1:tostring())
--say("b2 " .. b2:tostring())
local A = number:new({}); number.karatsuba_multiply(a1,b1,A);
local B = number:new({}); number.karatsuba_multiply(a2,b2,B);
local C = number:new({}); number.karatsuba_multiply(a1+a2,b1+b2,C);
--== C-A-B
--TODO minus, reassemble together..
end
karatsuba_multiply_test = function()
--local in2 = number:new({2,1,1})
--local in1 = number:new({2,1,1})
local res = number:new({});
local in1 = number:new({3,1,4}); --413
local in2 = number:new({7,2,5}); -- 527
number.karatsuba_multiply(in1,in2,res)
end
karatsuba_multiply_test()
multiply_test = function()
--local in2 = number:new({2,1,1})
--local in1 = number:new({2,1,1})
local res = number:new({});
local in1 = number:new({4})
number.multiply(in1,in1,res)
say("mult check 1 " .. res:tostring())
--say("mult check 2 " .. number.multiply(in1,in2):tostring())
end
--multiply_test()
number.__mul = number.multiply;
number.power = function(n,power_) -- calculate high powers efficiently - number of steps is log_2(power)
local power = power_;
local input = number:new(n.data);
local out = number:new({});
local count = 0;
local r; local powerplan = {}; -- 0: just square a->a^2, 1 = square and multiply a-> a*a^2
while (power>0) do
r=power%2; powerplan[#powerplan+1] = r; power = (power-r)/2
end
for i = #powerplan-1,1,-1 do
number.multiply(input,input,out);
if powerplan[i] == 1 then
input,out = out, input;
number.multiply(input,n,out); count = count + 2
else count = count + 1;
end
input,out = out, input;
end
return input
end
split = function(s,k)
local ret = "";
local j=1,length; length = string.len(s)/k
for i = 1, length do
j = (i-1)*k+1;
ret = ret .. string.sub(s,j,j+k-1) .. "\n"
end
--say("j " .. j)
if j>1 then j = j+k end
ret = ret .. string.sub(s,j)
return ret
end
self.spam(1)
-- little endian ! lower bits first ..
--n = number:new({7,1,0,2}); local power = 2017;
--self.label(split(n:tostring().."^"..power .. " = " .. number.power(n,power):tostring(),100))
--2017^2017 = 3906...
end

13
scripts/exemple_1.lua Normal file
View File

@ -0,0 +1,13 @@
-- title : Hello_World_1
-- author :
-- description : Say "Hello World", while spinning atop the spawner.
-- source : https://wiki.minetest.net/Mods/basic_robot
--
-- The robot will say "Hello World" again and again, while spinning atop the spawner.
-- To stop it, rightclick the robot or spawner, and press the Stop-button.
say("Hello World")
turn.left()

34
scripts/exemple_10.lua Normal file
View File

@ -0,0 +1,34 @@
-- title : Mining_103
-- author : rnd/hajo
-- description : A more advanced digger
-- source : https://wiki.minetest.net/Mods/basic_robot
--
-- rnd 2017-01-23 / hajo 2017-01-26
if not commands then
--turn.left() -- aim robot in a different direction
--move.up()
script = "DuDdf" -- Variant 1 - uses only dig-forward
-- script = "^Df" -- Variant 2: DigUp, DigForward, MoveForward
commands = {
["f"] = function() move.forward() end,
["b"] = function() move.backward() end,
["l"] = function() move.left() end,
["r"] = function() move.right() end,
["u"] = function() move.up() end,
["d"] = function() move.down() end,
["<"] = function() turn.left() end,
[">"] = function() turn.right() end,
["D"] = function() dig.forward() end,
["^"] = function() dig.up() end,
}
i=1; n=string.len(script)
end
c=string.sub(script,i,i); commands[c]();
if i>=n then i=1 else i=i+1 end

20
scripts/exemple_11.lua Normal file
View File

@ -0,0 +1,20 @@
-- title : Big_Display
-- author :
-- description : Show text from the book in slot#1 on a big screen.
-- source : https://wiki.minetest.net/Mods/basic_robot
--
if not pn then pn="BigDisplay" -- show news on big display
title,text = book.read(1);
if not text then say("No text... \n Place a written book into the first slot of the library"); self.remove(); goto ex end
self.name("")
self.label(title)
self.display_text(text,20,3) --triple-size, 20 char per line
--turn.left() --turn into the direction of the player
turn.right()
end
::ex::

14
scripts/exemple_12.lua Normal file
View File

@ -0,0 +1,14 @@
-- title : Find_you
-- author :
-- description : Find players in a 5 nodes radius.
-- source : https://forum.minetest.net/viewtopic.php?f=3&t=18345
--
players = find_player(5);
if players then
msg = table.concat(players," and ")
msg = "Around me i see " .. msg
say(msg)
self.remove()
end

30
scripts/exemple_13.lua Normal file
View File

@ -0,0 +1,30 @@
-- title : Ore-detector1
-- author :
-- description : Find ores in a 9 nodes radius.
-- source : https://wiki.minetest.net/Mods/basic_robot
--
-- TIP : Run this detector on the robot, and use an 'empty' remote controller to move the bot around to find the good stuff.
if (rg==nil) then s="Ore-detector v0.3"
say(s)
self.label(s)
rg=9 i=1 next=0
ore={'diamond', 'mese', 'gold', 'copper', 'iron', 'coal', '-'} --Table of valueable stuff
end
if (ore[i]=="-") then next=1 i=0 end
if next>0 then
i=i+1 rg=9 next=0
say("Seaching: "..ore[i])
end
if (rg>8) then rg=8 end
f = find_nodes("default:stone_with_"..ore[i], rg) -- returns true/false
if f then
say("Found "..ore[i].." within range "..rg)
rg=rg-1
else
--say("no "..i.." @ rg"..rg)
next=1
end

48
scripts/exemple_14.lua Normal file
View File

@ -0,0 +1,48 @@
-- title : Ore-detector2
-- author :
-- description : A faster, optimized version of the Ore-Detector1.
-- source : https://wiki.minetest.net/Mods/basic_robot
--
-- TIP : Run this detector on the robot, and use an 'empty' remote controller to move the bot around to find the good stuff.
-- It also searches for water and lava, to prevent surprises during mining.
if not s then s="Ore-detector2 v0.4f"
say(s)
self.label(s)
rg=9 i=1 next=0 msg=""
--Table of stuff to search:
ore={'@diamond', '@mese', '@gold',
'@copper', '@iron', '@coal',
--'!torch', '!torch_wall',
'!lava_source', '!lava_flowing',
'!water_source', '!water_flowing',
'-'} --todo: moreores, mobs
say(#ore) --
end
if (ore[i]=="-") then next=1 i=0
--say("done.") self.remove()
end
if next>0 then
i=i+1 rg=9 next=0
--msg="Seaching: "..ore[i] say(msg) msg=""
end
if (rg>8) then rg=8 end
s = ore[i]
c1 = string.sub(s,1,1)
s = string.sub(s,2)
if c1=="@" then s="default:stone_with_"..s end
if c1=="!" then s="default:" ..s end
f = find_nodes(s, rg) -- returns true/false
if f then
msg="Found "..s.." within range "..rg
rg=rg-1
else
--say(i.." no "..s.." @ rg"..rg) --debug
if #msg>0 then say("--> "..msg) end
msg=""
next=1
end

39
scripts/exemple_15.lua Normal file
View File

@ -0,0 +1,39 @@
-- title : Robot Penguin
-- author : ixfud
-- description : Robot that look like a penguin and interact with the player.
-- source :
--
-- Initialise
if not i then
i = 0
move.left()
-- Change le texte au dessus du Robot
self.label("Robot Penguin")
-- Definit le modele 3D
self.set_properties({
visual = "mesh",
mesh = "mobs_penguin.b3d",
visual_size = {x = 0.35, y = 0.35},
collisionbox = {-0.2, -0.0, -0.2, 0.2, 0.5, 0.2},
textures = {"mobs_penguin.png"}
})
elseif i < 6 then
-- say(i.." - place")
move.up()
place.down("default:dirt")
else
under = read_node.down()
if (( under ~= "basic_robot:spawner" )
and (under ~= "default:obsidian_block")) then
dig.down()
move.down()
else
i = 0
turn.left()
move.left()
end
end
i = i + 1

210
scripts/exemple_16.lua Normal file
View File

@ -0,0 +1,210 @@
-- title : Cobble Stair
-- author : ixfud
-- description : Build cobble stairway from the bottom and dig if necessary. it needs cobble and tree to create energy, and optionnally torches.
-- source :
--
-- .
-- TODO Change variable "tower_mode" to true to prevent digging while climbing
-- TODO Change variable "spiral_mode" to false to clime a straigth line prevent digging while climbing
local c = "default:cobble"
local t = "default:torch"
local d = "default:dirt"
local s = "stairs:stair_cobble"
local p = self.pos()
local r = 1
if not l then l = 1
elseif l > 4 then
-- return
l = 1
end
local e = machine.energy()
say(e)
local cmb = {"default:tree", "default:pine_tree", "default:jungle_tree", "default:aspen_tree", "default:acacia_tree", "default:coal_lump"}
if e < 1 then
local src
for _,s in ipairs(cmb) do
if check_inventory.self(s) then src = s ; break ; end
end
if not src then
say("I can't create energy ! Give me some wood or coal !")
l = 404
else
say('Creating energy from '..src)
machine.generate_power(src)
end
return
end
if check_inventory.self(c) == false then
say("I need cobble !")
return
end
if check_inventory.self(s) == false then
say("Ho I have to craft some stairs ...")
if craft(s) then say("... done")
else
l = 404
say("Oups, I can't do that ! I may be short of cobble.")
return
end
end
if l == 1 then r = 1
elseif l == 2 then r = 24
elseif l == 3 then r = 3
elseif l == 4 then r = 4
end
if not a then a = "place_stair" end
if not i then i = 1 end
if a == "place_stair" then
if not hold then
if i == 1 then say("Placing stairs ...")
elseif i == 2 then move.left()
elseif i == 3 then move.right() ; move.right()
else
move.left()
a = "move_stair"
i = 1
return
end
else
hold = nil
end
n = read_node.forward()
if n ~= "air" then
dig.forward()
hold = true
return
else
place.forward(s,r)
i = i + 1
end
end
if a == "move_stair" then
if i == 1 then dir = "up"
elseif i == 2 then dir = "forward"
end
n = read_node[dir]()
if n ~= "air" then dig[dir]() ; return end
move[dir]()
i = i + 1
if i > 2 then
a = "do_floor"
i = 1
return
end
end
if a == "do_floor" then
if not j then
say("Let's make sure there is a floor")
j = 1
end
if j == 1 then dir = "forward_down"
elseif j == 2 then
n = read_node.forward()
if n ~= "air" then dig.forward() ; return
else move.forward(); dir="left_down"
end
elseif j == 3 then dir = "right_down"
end
if i < 4 then
n = read_node[dir]()
-- say('I should place '.. c.. ' '..dir)
if n == "air" then
place[dir](c)
-- say("Therefore I place "..c)
end
j = j + 1
if j > 3 then
j = 1
i = i + 1
end
return
elseif i > 6 then
n = read_node.up()
if n ~= "air" then dig.up()
else
move.up()
a = "clear_floor"
i = 1
j = nil
end
return
else
move.backward()
i = i + 1
return
end
end
if a == "clear_floor" then
if not j then
say("I will clear the floor now")
j = 1
end
where = {"down","up","left_up" , "right_up", "left","right", "left_down","right_down"}
if j < ( #where + 1 ) then
dir = where[j]
while read_node[dir]() == "air" do
j = j + 1
if j > #where then
j = ( #where + 1 )
return
else dir = where[j]
end
end
dig[dir]()
j = j + 1
return
else
say('Slice '.. i .. '/4 ...done.')
if i < 4 then
n = read_node.forward()
if n ~= "air" then dig.forward() ; return
else
move.forward()
j = 1
i = i + 1
return
end
else
if check_inventory.self(t) then place.right_down(t,1) end
a = "turn_and_restart"
i = 1
j = nil
say("Ok, let's get ready for the next step !")
return
end
end
end
if a == "turn_and_restart" then
if i == 1 then move.down()
elseif i == 2 then move.backward()
elseif i == 3 then move.left()
else
turn.left()
l = l + 1
i = nil
a = nil
return
end
i = i + 1
return
end

30
scripts/exemple_2.lua Normal file
View File

@ -0,0 +1,30 @@
-- title : Hello_World_2
-- author :
-- description : Outputs "Hi !" (only once), then start counting and turning.
-- source : https://wiki.minetest.net/Mods/basic_robot
--
if (i == nil) then
say("Hi !")
self.label("Hugo")
i = 0
end
i = i+1
turn.left()
say(i)
-- Outputs "Hi !" (only once), then start counting and turning.
--
-- == is the check to compare the values on both sides, i.e. "if i is-equal-to nil".
-- = is an assignment, i.e. "i=0" means "put the value 0 into the variable i".
-- To calculate i+1, the variable i must have a value assigned first.
-- And because all the code is executed again every second,
-- this first assignment must be done only once.
-- (Otherwise, we couldn't count to 2)
-- At the start, no variables exist yet, so we can test for nil ("no-value").
-- Note: nil is different from any string or number !

14
scripts/exemple_3.lua Normal file
View File

@ -0,0 +1,14 @@
-- title : Hello_World_3
-- author :
-- description : Shorter version of Hello_World_2.
-- source : https://wiki.minetest.net/Mods/basic_robot
--
if i==nil then i="Hi !"; say(i); i=0 end
i=i+1 say(i) turn.left()
-- Same as above, but with multiple statements in one line,
-- and some variations (e.g. braces and ";" are optional).
-- In lua, the value of a variables can be of any type.
-- Here, i first is assigned the string "Hi !", and later a number.

16
scripts/exemple_4.lua Normal file
View File

@ -0,0 +1,16 @@
-- title : Catch_Me_1
-- author :
-- description : Some program that works perfectly well, but still shoots the player's foot :-)
-- source : https://wiki.minetest.net/Mods/basic_robot
--
dig.down()
move.down()
-- The robot keeps digging down, and will continue unless it is stopped or it finds a cave, or an unloaded map-block.
-- With the first dig, it will also destroy its spawner.
-- So, to get at the stop-button, the user has to jump into the hole to get at the robot.
-- (Quickly, otherwise the player also gets falling damage !)
-- Leaving the game, and entering again probably also stops the bot...
-- (Maybe a bug: without the spawner, the inventory of the robot is not accessible)

22
scripts/exemple_5.lua Normal file
View File

@ -0,0 +1,22 @@
-- title : Catch_Me_2
-- author :
-- description : Now we go up, instead of down. :-)
-- source : https://wiki.minetest.net/Mods/basic_robot
--
dig.up()
move.up()
local dirt_place = place.down("default:dirt")
if not dirt_place then say("I need dirt in my inventory") end
p = self.pos() -- returns a table, with elements x, y, and z
say("Position: " .. p.x .. "," .. p.y .. "," .. p.z)
-- Now we go up, instead of down.
-- After stopping the robot, and pressing start again,
-- there will be a block of dirt above the bot from the first go.
-- We need to dig away this block so that the bot can move up again.
-- The command "say()" outputs a single string, but several strings can be concatenated with '..'.
-- Numbers are automatically converted to strings for output (e.g. the number in p.x).
-- Note: y is used as the height here in minetest.

19
scripts/exemple_6.lua Normal file
View File

@ -0,0 +1,19 @@
-- title : Run_1
-- author :
-- description : The robot run and turn when hitting an obstacle
-- source : https://wiki.minetest.net/Mods/basic_robot
--
if (i==nil) then say("Demo1"); i=0 end
if read_node.forward()=="air" then
move.forward()
else
turn.right()
end
-- If nothing ("air" mean nothing solid) is in front of it, the robot moves forward, otherwise it turns.
-- The robot can "fly" one block above the ground, like the player can reach by jumping up. The bot just doesn't need to bounce up & down.
-- That means, if the robot reaches a place where the ground one step ahead is more then one block lower, he cannot move forward.
-- With the above program, he just stops moving, because there are no instructions in the code to handle that case.

49
scripts/exemple_7.lua Normal file
View File

@ -0,0 +1,49 @@
-- title : Run_2
-- author :
-- description : Essentially the same program as 'Run_1', but with more checking and reporting
-- source : https://wiki.minetest.net/Mods/basic_robot
--
local function report_pos(msg,complement)
p = self.pos()
say(msg .. " position x=" .. p.x .. " y=" .. p.y .. " z=" .. p.z .. "/n" .. complement)
end
--
if (i==nil) then say("\nDemo2"); i=0 end
--
n = read_node.forward()
if n~="air" then
report_pos("Now at")
say("found "..n..", turning")
turn.right()
else
if move.forward() then
i=i+1 -- move was ok
else
report_pos("Cannot move at","(probably because I would need to fly to go there)")
--self.remove() -- stop program, and remove robot
end
end
if i > 20 then
say("After 20 steps, I turn without any reason, this way I avoid getting stuck")
if math.random(1,2) == 1 then turn.right() else turn.left() end
i=1
end
-- This is essentially the same program as 'Run1' above, but with more checking and reporting:
-- now there is a check if the move was successful, and output to report the position.
--
-- Because we want to report the current position of the bot at more then one place in the code,
-- the common instructions for that have been moved into a function.
-- When called, the function gets a string as a parameter, that is used in the message that is generated.
-- There is no return-statement at the end of the function, because we don't need a result.
-- ".." is the lua-command for concatenating strings.
-- "\n" is newline.
-- "~=" is the check for not-equal.
-- "--" starts a comment. Text after this until the end of the line is ignored.
-- Here, we have also commented-out the statement "self.remove()",
-- so that it doesn't get executed.

19
scripts/exemple_8.lua Normal file
View File

@ -0,0 +1,19 @@
-- title : Mining_101
-- author :
-- description : Simple digger 1x1
-- source : https://wiki.minetest.net/Mods/basic_robot
--
-- simple digger1x1
if not i then -- Init:
i=0
turn.left() -- initital positioning: aim robot in a different direction
--move.up()
end
-- Work:
i=i+1 say(i)
if i<=20 then
dig.forward()
move.forward()
end

23
scripts/exemple_9.lua Normal file
View File

@ -0,0 +1,23 @@
-- title : Mining_102
-- author :
-- description : simple digger 2x1
-- source : https://wiki.minetest.net/Mods/basic_robot
--
-- simple digger 2x1
if not i then -- Init:
i=0
turn.left() -- initital positioning: aim robot in a different direction
--move.up()
end
-- Work:
i=i+1 say(i)
if i<=20 then
dig.forward()
-- ??
dig.up() -- !! doesn't work with setting 'maxdig=1'
move.forward()
else
say("stop") self.remove()
end

11
scripts/notes.txt Normal file
View File

@ -0,0 +1,11 @@
bugs :
Return compilation errors for commented words :
repeat not allowed! because some comment contains the word 'repeatedly' (exemple1)
until not allowed! because some comment contains the word 'until' (exemple4)
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
=²exemple 5 - catch_me_2 Ajouter retour "I need dirt !"
exemple 7 - run_2 Remplacer "cannot move" with explaination, bug until
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
exempe 9 - 11 - mining request energy
exempe 10 - mining102 out of available operation

64
shell/doc_to_html.sh Normal file
View File

@ -0,0 +1,64 @@
#!/bin/sh
# Generate a html page from all the markdown documents present in rep 'docs'
# Require 'markdown' command
#
#-- Markdown docs path
docpath="../docs"
if [ ! -d "$docpath" ];then exit ;fi
#-- Temporary files
html_menu_tmp="~html_doc_menu.tmp"
html_content_tmp="~html_doc_content.tmp"
if [ -f "$html_menu_tmp" ];then
echo "The temporary destination file '$html_menu_tmp' already exist. Aborting"
exit
fi
if [ -f "$html_content_tmp" ];then
echo "The temporary destination file '$html_menu_tmp' already exist. Aborting"
exit
fi
#-- output file
output="../robot_doc.html"
# if [ -f "$output" ];then echo "The output file '${output}' already exist"; exit; fi
#-- Initialize files
echo "<h1>Robot Documentation</h1>" > "$html_menu_tmp"
echo "" > "$html_content_tmp"
echo "<a name='top'></a><ul>" >> "$html_menu_tmp"
#-- Loop through file
files=$(ls $docpath |grep -e "^robot_doc_.*\.md$")
for mddoc in $files ; do
#-- Full path
doc="${docpath}/${mddoc}"
#-- Get infos
pat_title="\-\- title :"
title=$(grep "$pat_title" $doc |sed "s#$pat_title##")
pat_description="\-\- description :"
description=$(grep "$pat_description" $doc |sed "s#$pat_description##")
#-- Menu entry
echo " <li><a href=\"#${mddoc}\">${title}</a>" >> "$html_menu_tmp"
echo " <br> - ${description}</li>" >> "$html_menu_tmp"
#-- Anchor
echo "<hr>" >> "$html_content_tmp"
echo "<a name=\"${mddoc}\"></a><a href=\"#top\">-- Return to Index --</a><br>" >> "$html_content_tmp"
#-- Content
markdown "$doc" >> "$html_content_tmp"
echo "" >> "$html_content_tmp"
done
echo "</ul>" >> "$html_menu_tmp"
#-- Join files
echo "" > "$output"
cat "$html_menu_tmp" >> "$output"
cat "$html_content_tmp" >> "$output"
#-- Delete temporary files
rm -f "$html_menu_tmp"
rm -f "$html_content_tmp"

54
shell/rename_scripts.sh Normal file
View File

@ -0,0 +1,54 @@
#!/bin/sh
# Rename scripts to be readable by lua as <prefix>_<number>.lua
# And add title and author as comment at the beginning of the file
#
# Arguments :
### 1 : path of directory containing scripts
### 2 : prefix
### 3 : number to start numerotation
### 4 : author of the scripts
### 5 : source of the scripts
### 6 : description of the scripts
dirpath="$1"
prefix="$2"
n="$3"
author="$4"
source="$5"
description="$6"
help="Usage :
rename_scripts <folder_containing_scripts> <prefix> <number_to_start_numerotation> <author_of_scripts> <source_of_scripts> <description_of_scripts>"
#-- Return if not a valid directory
if [ ! -d "$dirpath" ]; then
echo "Wrong argument 1 (directory path) :'$dirpath' "
echo "$help"
exit
fi
#-- Return if prefix is empty
if [ -z "$prefix" ]; then
echo "Argument 2 (prefix) is empty and must be definined"
echo "$help"
exit
fi
#-- Set n to 1 if empty
if [ -z "$n" ]; then n=1 ;fi
for scp in $(ls $dirpath |grep -e "\.lua$"); do
title=$(basename --suffix=.lua $scp)
in="${dirpath}/${scp}"
out="${dirpath}/${prefix}_${n}.lua"
echo "-- title : $title" > "$out"
echo "-- author : $author" >> "$out"
echo "-- description : $description" >> "$out"
echo "-- source : $source" >> "$out"
echo "--" >> "$out"
echo "" >> "$out"
cat "$in" >> "$out"
rm "$in"
n=$((n+1))
done

Binary file not shown.

BIN
textures/255.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

BIN
textures/255.xcf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

BIN
textures/mobs_chicken.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

BIN
textures/mobs_penguin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 B

BIN
textures/mobs_turtle1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

BIN
textures/mobs_turtle2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

BIN
textures/mobs_turtle3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

BIN
textures/mobs_turtle4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B

BIN
textures/mobs_turtle5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

BIN
textures/mobs_turtle6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

BIN
textures/mobs_turtle7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

BIN
textures/open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

BIN
textures/parent.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

BIN
textures/power.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

BIN
textures/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B