From ca09e73c6b4e85e303afaf8d678518d969375809 Mon Sep 17 00:00:00 2001 From: rnd Date: Wed, 21 Feb 2018 10:54:22 +0000 Subject: [PATCH 01/39] tweaks --- commands.lua | 115 ++++++++++++++++++++++++++++++++++++++++++++- init.lua | 25 +++++++--- textures/arrow.png | Bin 1521 -> 3893 bytes textures/cpu.png | Bin 676 -> 2143 bytes textures/face.png | Bin 795 -> 172 bytes 5 files changed, 132 insertions(+), 8 deletions(-) diff --git a/commands.lua b/commands.lua index 11d64da..8b80c8f 100644 --- a/commands.lua +++ b/commands.lua @@ -1353,4 +1353,117 @@ basic_robot.commands.puzzle = { end return true end, -} \ No newline at end of file +} + +-- VIRTUAL PLAYER -- + + +local Vplayer = {}; +function Vplayer:new(name) -- constructor + if not basic_robot.data[name].obj then return end -- only make it for existing robot + if basic_robot.virtual_players[name] then return end -- already exists + + local o = {} + setmetatable(o, self) + self.__index = self + o.obj = basic_robot.data[name].obj; + o.data = basic_robot.data[name]; + + local spawnpos = o.data.spawnpos; + local meta = minetest.get_meta(spawnpos); if not meta then return end + o.inv = meta:get_inventory(); + + basic_robot.virtual_players[name] = o; +end + + -- functions + function Vplayer:getpos() return self.obj:getpos() end + function Vplayer:remove() end + function Vplayer:setpos() end + function Vplayer:move_to() end + function Vplayer:punch() end + function Vplayer:rightlick() end + function Vplayer:get_hp() return 20 end + function Vplayer:set_hp() return 20 end + + function Vplayer:get_inventory() return self.inv end + function Vplayer:get_wield_list() return "main" end + function Vplayer:get_wield_index() return 1 end + function Vplayer:get_wielded_item() return self.inv:get_stack("main", 1) end + function Vplayer:set_wielded_item() end + function Vplayer:set_armor_groups() end + function Vplayer:get_armor_groups() return {fleshy = 100} end + function Vplayer:set_animation() end + function Vplayer:get_animation() end + function Vplayer:set_attach() end + function Vplayer:get_attach() end + function Vplayer:set_detach() end + function Vplayer:set_bone_position() end + function Vplayer:get_bone_position() end + function Vplayer:set_properties() end + function Vplayer:get_properties() end + function Vplayer:is_player() return true end + function Vplayer:get_nametag_attributes() end + function Vplayer:set_nametag_attributes() end + + function Vplayer:set_velocity() end + function Vplayer:get_velocity() end + function Vplayer:set_acceleration() end + function Vplayer:get_acceleration() end + function Vplayer:set_yaw() end + function Vplayer:get_yaw() end + function Vplayer:set_texture_mod() end + function Vplayer:get_luaentity() end + + function Vplayer:get_player_name() return self.data.name end + function Vplayer:get_player_velocity() return {x=0,y=0,z=0} end + function Vplayer:get_look_dir() return {x=1,y=0,z=0} end + function Vplayer:get_look_vertical() return 0 end + function Vplayer:get_look_horizontal() return 0 end + function Vplayer:set_look_vertical() end + function Vplayer:set_look_horizontal() end + function Vplayer:get_breath() return 1 end + function Vplayer:set_breath() end + function Vplayer:set_attribute() end + function Vplayer:get_attribute() end + function Vplayer:set_inventory_formspec() end + function Vplayer:get_inventory_formspec() return "" end + function Vplayer:get_player_control() return {} end + function Vplayer:get_player_control_bits() return 0 end + function Vplayer:set_physics_override() end + function Vplayer:get_physics_override() return {} end + function Vplayer:hud_add() end + function Vplayer:hud_remove() end + function Vplayer:hud_change() end + function Vplayer:hud_get() end + function Vplayer:hud_set_flags() end + function Vplayer:hud_get_flags() return {} end + function Vplayer:hud_set_hotbar_itemcount() end + function Vplayer:hud_get_hotbar_itemcount() return 0 end + function Vplayer:hud_set_hotbar_image() end + function Vplayer:hud_get_hotbar_image() return "" end + function Vplayer:hud_set_hotbar_selected_image() end + function Vplayer:hud_get_hotbar_selected_image() return "" end + function Vplayer:set_sky() end + function Vplayer:get_sky() end + function Vplayer:set_clouds() end + function Vplayer:get_clouds() end + function Vplayer:override_day_night_ratio() end + function Vplayer:get_day_night_ratio() end + function Vplayer:set_local_animation() end + function Vplayer:get_local_animation() end + function Vplayer:set_eye_offset() end + function Vplayer:get_eye_offset() end + + + -- code for act borrowed from: https://github.com/minetest-mods/pipeworks/blob/fa4817136c8d1e62dafd6ab694821cba255b5206/wielder.lua, line 372 + + + + + + + + + + diff --git a/init.lua b/init.lua index c925578..5483929 100644 --- a/init.lua +++ b/init.lua @@ -18,7 +18,7 @@ basic_robot.dig_require_energy = true; -- does robot require energy to dig? basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2017/12/18a"; +basic_robot.version = "2018/02/06a"; basic_robot.data = {}; -- stores all robot related data --[[ @@ -28,6 +28,8 @@ robot object = object of entity, used to manipulate movements and more basic_robot.ids = {}; -- stores maxid for each player --[name] = {id = .., maxid = .. }, current id for robot controller, how many robot ids player can use +basic_robot.virtual_players = {}; -- this way robot can interact with the world as "player" TODO + basic_robot.data.listening = {}; -- which robots listen to chat dofile(minetest.get_modpath("basic_robot").."/commands.lua") @@ -524,8 +526,6 @@ function getSandboxEnv (name) end - - --special sandbox for admin if authlevel<3 then -- is admin? env._G = env; @@ -836,7 +836,7 @@ minetest.register_entity("basic_robot:robot",{ --textures={"character.png"}, visual="cube", - textures={"arrow.png","basic_machine_side.png","face.png","basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"}, + textures={"topface.png","legs.png","face.png","face-back.png","left-hand.png","right-hand.png"}, visual_size={x=1,y=1}, running = 0, -- does it run code or is it idle? @@ -994,7 +994,7 @@ local spawn_robot = function(pos,node,ttl) local sec_hash = minetest.get_password_hash("",data.authlevel.. owner .. basic_robot.password) if meta:get_string("sec_hash")~= sec_hash then - minetest.chat_send_all("#ROBOT: " .. name .. " is using fake auth level. dig and place again.") + minetest.chat_send_player(owner,"#ROBOT: " .. name .. " is using fake auth level. dig and place again.") return end @@ -1074,7 +1074,7 @@ local spawn_robot = function(pos,node,ttl) local sec_hash = minetest.get_password_hash("",luaent.authlevel.. owner .. basic_robot.password) if meta:get_string("sec_hash")~= sec_hash then - minetest.chat_send_all("#ROBOT: " .. name .. " is using fake auth level. dig and place again.") + minetest.chat_send_player(owner,"#ROBOT: " .. name .. " is using fake auth level. dig and place again.") obj:remove(); return end @@ -1270,6 +1270,9 @@ local on_receive_robot_form = function(pos, formname, fields, sender) if fields.code then local code = fields.code or ""; + if string.len(code) > 64000 then + minetest.chat_send_all("#ROBOT: " .. name .. " is spamming with long text.") return + end if meta:get_int("admin") == 1 then local privs = minetest.get_player_privs(name); -- only admin can edit admin robot code @@ -1537,6 +1540,9 @@ minetest.register_on_player_receive_fields( local name = string.sub(formname, string.len(robot_formname)+1); -- robot name if fields.OK and fields.code then local item = player:get_wielded_item(); --set_wielded_item(item) + if string.len(fields.code) > 1000 then + minetest.chat_send_player(player,"#ROBOT: text too long") return + end item:set_metadata(fields.code); player:set_wielded_item(item); if fields.id then @@ -1678,6 +1684,9 @@ minetest.register_on_player_receive_fields( local data = itemstack:get_meta():to_table().fields -- 0.4.16, old minetest.deserialize(itemstack:get_metadata()) if not data then data = {} end local text = fields.book or ""; + if string.len(text) > 64000 then + minetest.chat_send_all("#ROBOT: " .. sender .. " is spamming with long text.") return + end data.text = text or "" data.title = fields.title or "" data.text_len = #data.text @@ -1997,4 +2006,6 @@ minetest.register_craft({ minetest.register_privilege("robot", "increased number of allowed active robots") -minetest.register_privilege("puzzle", "allow player to use puzzle. namespace in robots") \ No newline at end of file +minetest.register_privilege("puzzle", "allow player to use puzzle. namespace in robots") + +print('[MOD]'.. " basic_robot " .. basic_robot.version .. " loaded.") \ No newline at end of file diff --git a/textures/arrow.png b/textures/arrow.png index 5bb11164ab531bfce19f6d776bcec277b1870d20..c8116e8728b3a32b57fd524335ab388255e7dc15 100644 GIT binary patch literal 3893 zcmYjT2{_bU7r&Fqz9mbNUR!AFd-i4QYt|W2*+)qzZ?+QI(@T;)vhP$B31tgeL-s9O z!b{(4X@oJKJEf=dJokU^J&N+9UXQIv<>(Ei3qy_-c>FH{k9qf#7QIZ3IaNX?1 zA6%$Bb**m#Kyw@}1W0{y0sv~0D`@oj^R6D=9yeV*Jh}AHXfDqi9xhj|IRoH3mTr!- zFkfO-#!qc(7(@m=Ht;ZGq2w~thz??o<418*G3rNhXN|I$HXhT`LUOccMv}$E1U+Ce z6{fvUJwh?hogEXB6M4U5cP1dmZKw|arF;L?u*zcTMtWs0MFS;mqOO#wOb~66#_409 zL)uzeXXZuKf~omD4|rEPao;5HBEWW_l9CWl1I1f_@ExF`1Pw-M4dSOFhz!dbDYl59 zP(*`w+&KfvAZDQEAES{E)U**n>B;=&AQK7fIvpM6zIoP63Sib=$0+?LRL;1FI_OkNg;EWlyjHLcrHB^=1|xb}ut$$gh*{SDh<&P> zXp>(GyJWC5!{?1I05aoP4yxTC`1a73_w>ldRMB4+S$;=O2d>xaP3E+(jI>6^V?azV9ATYI>_zMORI0X70-%{0jC!ul zvDtV+xPkmsKY#J zNiW)D`I=tYlVQi6GNVzjE|?0b-cEVko_j13;cy?z8$-#K61L6VsEcHZ=HvPh$!yAF z5GSK6VRqkw%TkwfUHQfl`3PI>H*t#XK=R&Rc%6>Kp7=t8 zm;7&z{b)JiASn@n)fMI)Vv0R3#uyNZZjs|-ib6{Y)I2<*_afJ#pQ&HF|GF9Pl5kO) zwC+jX_zz=FZWR>rkA=B}+Qa74=bPuH<~ioM*KMfN9n`#Xx6Nk-%{s2?voA0%NH35( zCrheZ=A@cU7G_x-lZm~cQI=I@_RKV*@VNBfsq)<+ta=7{W!VV>CtcZG@mHRG^?q|~ zGU2pw(nm~x=aSCXq_1R~KD2b^>)+B^&0m4@9%KS%}L?tRyd{ zFr;``Vo_D7+bnS%)Cu|t{0ST=F^i(&{Nkx%8p{pKPK(&0OXhdVwJoL0=ZdI4C6-tg zr=MRj=P<_=DHcW);|e`2RC5EZ2~p(M&4$e;=v>px7nkSiTDfQ(*(-g8x!veW!v7AI zY2km-J$m~Vsy3OxSsSffT$Ck`meQBPRGMu(c2T96OZM7+;%!W|C|HoR5qN)!`P)l{_^)`J0TLB$hY|qj3Me|Yf z$pY#ERwHGFt#Z*~w>*xlT1}sK*9+E?Gu~bb$}sdOnDVP%+&Ohej9@`<-znJAp~aF{ z((;jy)ApQDdN1SUp~NZHHvXR}`eL&hI?q1AK4~U2uBJ~ZXHfeeZH5O|T5ad}=GO)mg zHT(005%Tf!D)*{8_BaPNT=o0%=C%0x_&aaz{6kQtpol95=5l!Q0oouh4 z*Z1D?x;W$Czm=X=nbx#gIR0!+cJ0bq#KhAs>8EEitfg*#Y;H$?HeKjbrFohl?lxT- zXo5Bg#=TnP6>}7e!;}p_ug>!+#@l~mQ~9oP}S zW!$dq|2{0^#xExK#PwXE4?7?Db?767{TKV_=0IA3u!t~BxN3}NB=%&VM3Rh8-vs*R zwC0XxqUKZ@PQpa~nTWm&N~Kuzs?wO;8|gQyt)ed0U4z(ktll+)Q}Uvya@p(G-cIkW zZbZzc&(h2VuqBJNU-b0L>S@{xD_|`WWfE3MR5Oe;KKJ;4>WrvD)(2sI>@nl}7$oiwlCo zD2fW4GsfjzcT|UbVo8Qj_i9&V$M{3JC*PhDSDBiaW*$}Lg&SZ*II&;qORvgTD0%sf zQ_ry+6$-Z^FI9SdzY66(Xi~C@Nh%Ml=l3^q3%=CYaj)?e!fl1dl6oQR&dS8_>O~W* znDz84`(cMC^kqsU!-hVw zIO$6g{dKz0v6pnyRxek#zH5z(C3Opq*M?M~YjWNxy{>aE_|Vg>sA`dGv1zemA-mQ; z`O1EH`+DCRM?l?o>3;{uZR+p@l@D*nEfJPRgD!*4yL!8oc-8W#;-&4xUH6M+#_nFL zZDjpq1PWgIs^OYXgPi@!7G|+SLMuA-ZGXMpPygm4LMKaS`OlNqSzb>~zxgFR)inMz zox<3y2LYus6a#2-^UDZfkPt{H{Pu;a~uz6$BrP4RFJmtTqGyWID6KN%DKm84h z4_C&P?hVA_)_WhMC#2sA6#X$_H=bP;u;|z`eU-uE+Z#sjnSL!|^rB*&Vvp_mYwz`n z@rC1<*O=623YXoNy_c2t;|EOIYM<1mme<(2)vWo~zY1`{FL7kZ^-ONo`~AmC92(4! zSDvij@b|#K$8SxuP6pl(AoO(Bj`|3!mDQQlUEWk%RUbu9MhvRj>`#n*o49+m!6$R0 zZ$bx89v0HOAGa0DfIb?V65AP07rv5lPDNgRR`mv9X|Dj?>(tAi!M2~h-&xMaDi*js zxH8<+m)w_?w4N0I&i!^CVYy+2{n>bHQ|sxsAAF@GE^S)vt~UBi_;7y9nPxDVzs+@5 zkm&m3#g_TUg57xb_;@0cEO}$kQA%X|;QuCO@H6wTAV5t`4G;(fG=uf^^#dOwAR&>H zlaqjc#R4oA3knMh!OqSOK*9*PNVWZu8QvjJ5*-480Dk=VL8=KV^Yilo9*-xb0F(&) z{_oboq%aibIxGZ6BO%LQcz}X}f^>#VH#Rnai;K%orVb7cP)CxIk`mJX2O1LO=H>!h zTU!zV2z$sJ?xb3w5em!=l~7<>I1Q2mEF3lf!a@f~7|L%1XoO;JZcf_aykRw@cEUq; zb~XtT!on!nMyQ8;0Ob)pfz%9G@1a01n2(Z@@;9kJbHg4$fhk;FU4IgV4sgMq9Fjf+ z{BtiWD+5kWP9P&AgQO<$gdZ3T+mMr!^Sj%ysqmpe`ST1xzJLEts)IBo_<@w+4tok2 zK!JtAGX#fcD6qE}3Bz>S-BkmTTDE`#(>HTyOvY literal 1521 zcmVuq)9K61-a9(pEGyh< zYvj|^-zg~FZEfS!*7^AP-zq8GZg1n&*6Ga5-YqQKaB<_-*WM{8+j4W`*V*~``TqU> z_xJbq_4fDo`1<<#{QUg={r%o9E!%W-h9az?&Rg}>g@0C@A2^Q^z-!g z_V)Yx``dPRV1HO5#>kB!0I*V*ggqvN@{-!n4bJUrh^OW!y*+J1lK+}-TJ!QL=0RhltZqveyuIts(CXRS>Eh(*=IH0;=;!6< z?(FU6#Kzw_Io>WV-+q4J*4OaK$>U2)FDa} z>FV_J_51q!=*r9Adwby3*YfM^^x)w0=jie6?(XgG?Cb69>+JXT_uzVa;nmmk^Yq_& zdEP86;dgi9*4W{7cH`C8>C4UHb93d@*Xhj7<8g51*4W@bKI3q3>CVsOfr01M*YWT1 z?B(a^-r)WG{p{-O000000Ri;10000I0000W007$%0000gs%X0(00001VoOIv0Eh)0 zNB{r;32;bRa{vG?BLDy{BLR4&KXw2B0*XmQK~#9!V_?7z7#SHENCJ#Z%%tgHWMO3^ zNe3f42PYRPIvBZmc=<@t!N@NlC?rgR4xk1RK~XVrMiMkghzLqb5v_w!T0lZVL`GJQ zSRG&u67n(%ib`lYlvPyK)HO8GBNn1TLPSej2Tg~rp1y&hk&&^9skj-EHmC-m9p)Cg zC_1<-t*mWq?d%;Kot#}ssS4iDKDvj>mWvhgh*voHB5&}4G|h7zk>(bN4q`P($TzpPfl>+48YD#8J34`>M5T*Z4H6>VJ-r$vY3S>p0Msy% zXeUjYJO!A^iPj)rKXsZ4$kP*v4c6&1OhLiQMw|xu+L^Q5z!Aqbo2aBwK68!;*jAuq(Uq%KuUWfp{f3R3 zkh17zg!y0%;6h-_s;%3$@7TF(_ny7`_J@wXU`d& z55I6R!uS%~Wnej@ab*!yz1G!h$m$umuHU$M>-L?ywFJNdu~i*aj)@WC9pzyNwj*929bo9 zuZUL9pz(U8;G4I^s|RX$_kN?XCh^s-#)preK9f?hYkc|oS(B8e!?*8|BsNaWaF`DO X5WDxzXbk|k00000NkvXXu0mjfLP2Ip diff --git a/textures/cpu.png b/textures/cpu.png index d178edb1f27943a3c9048e2127b1dd7651b0f936..6e99d78b96980a0dd2f5d3596aba67f15275ce97 100644 GIT binary patch literal 2143 zcmeHIOK%fb7(L&3?AUpbmnY!F_Bc*Lg_@LzO2m(`W1NI0)F2eH>8jBFfL1N21uBT0 zGzF=vB1G9BN*8QUVMm0-GJwRR4H764Ac3eu@GEgcNbJ}%{br2R1?{f8_FmmP7R535-xw-CD5sr6+Cp*FkXK=#ByOar6;F!~Q*yTUc9z7|C;-1K)tc=Uyad+UTClvQazLSIB zxOYss!*Mw>;SP_>{c$;T)Du3^=L^)2A z{`~oK=gyryd-lwkGpA3VK6UEUfddC(vDmI%y9NdZLZMJ!Ute!;Z+CZhXJ@A@%MOP_ zlBC+&TC3H{PH7r@^MCuVJ1{opIm8}}9UR@WkB`K^-LP-}!G@X^oVs zqCnAx0#QgRWJsx5vNHufkH+diQCy?gx;U**FK8r(N0rr1JklsyWuaB8=>|n?`prUy z`fG^SGY={9`+VE_GqY659>!IT2GlhxsVGp@pjlh7i5YswE2t(_Sfrk6Y+6}vO_QsY zH$Yk;dk0$7j3YzhfQYK%`YU=jUld!CY7b7}D~*B=no_Mv=d(u3^PZ>PxozanZ!VLX zmeP%jZ7S|{kg{qoTe3PF18O(ADe%f$rkC~%(3ErFaXp1*{hZOb2(+Xm?O_`Qvo6be zzOtoRc+x<=yF1BLDw@sn+MVsv9WN>To|2{tV#!=q%|${3EvoDxRN=MAK`t2;@HV^X zOjHmF+FH3rHETD!Ds-8QJ=0_}=th%WWm};n2&8EOuKEoA%M~tm()mZS&Kw*%#&S!l z^}fRpl#L5i)Ip3Wa{&d>st-U z?1%XMOZA8f+_}8A>cJc z?j@L^8J$qz6$;`3nvg;(l5pY~8eze6^x!GHn8P+u6f=nOei8bi(y)dyY8X++xrr{2 zA9-x%AVFo_#G|~3CL5;!_t2puBss4PTaqJxwPu0uufhi=xylE93C&q-FfopWX$mQ} zi=#Kt4T>PkmhF6^ffoN$|U=vE19(0`lQ5cCvmG2a0U48S}h&haW5P?A3ipbJf<6 zLoIV&;fFO~mQBi|j+t>S6yvE}R=(;*uN1t@_hv>U?)*_X_ak)L00001VoOIv z0Eh)0NB{r;32;bRa{vGf6951U69E94oEQKA00(qQO+^Rb0|ylm0;k>3>;M1&9cffp zbVF}#ZDnqB0000007G(RVRU6=Aa`kWXdp*PO;BVmWd{HN0ij7mK~zY`wU&u;gCG!w z*VcH%a3~5M#J>Mqc3HHML^G0^{+YxDJ~Kbd2|o|X!AVLv$l;D(7g5cIfi2I3V2rhG zhk;;S+hPfWLtYyrgz7paj5aS}m$In@o6;6Wupoir{u@KaT z%)Ic^HUB1k?aMdj*JMaa$im_#h$R9lq)m7nN$dTkQDXmMYC~CW}n(~*HTB`{LjXah4c)*XG{ttOQz@U`!d~c7`dmc&8 zlmB!)K0_ek_$m*1=ZA%U972G5^5mH(KZ7x*T>QY|7uMgvPl+VvQ%CSR(wE@xXmLH?ifX*0ke=<}f?kBPPRrG!sx!?Zx{{O*w6noyqo~NZzIhB0000< KMNUMnLSTYISU3;> diff --git a/textures/face.png b/textures/face.png index 7ec59762f6fa1d173301c51fc78f25c7dbb7fd4f..b991d223aa361bc1d26c11abe75fccbe52573af7 100644 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPE^3h)VWJ$dq^mX?;TuCBVe`iT=K z&YU@O;^X&+K-o}F7sn8e>%J#1ayA(7v^?xCYlu<5;Ip8FMfL#u-Fi`d-6ne$JG~AL zwwoJ77`|7koLJ6rLhs;;8nFddXU_ir@NpCW&H&X5#s^I}X7e%cK#GhT? Twx81+XaR$#tDnm{r-UW|*p@uX literal 795 zcmV+$1LXXPP)shr^hfKc)Z{5{T}i`GDz{y5CBL9ph2TCh>`#|G6a#`kN^ke2vUAP z0$hm^M0`a80Jliw24qetrByGJD^wq zE_hWS1yQ`>f=E>%sEXi%Agd6tg18{?F9`fs08iP4fPD(FLE72J`B@N7pEHdGa;sv~ zzSkcJ1bp6neEEgnyKfS&<}g55y5bozJ>_@yTi?^8|DE5)%sMB1Fa_)jE;Oc8#!%OS9i#^7PMsM8OdD)83!2d&6CkJ8 z=fDJ_$)FaR%`lAx^mf`L$j&I1Kzd*x4VDx$Sujbn>#@LW|1n2;3*Tbz@S?W%0=~!I z;H{K%+&_x}+!C*)&J@qDUPjsM(U<&haZ{beD{&VGABo{^cfj|YOeUizM@J{o{NmXc zaCZ+UFzNvB#~V9Uz2BnhY#hIbSgiq*dz`{v;_M|MZnyH)^>TxX+uO@k8RYe9K2duM ztZ~geu*n}Ejr83{Gw}itj>vv|=mXP?0^4Pk|jkdeHwvEeQY+gxs`bQ<7(b2)+S5k?GK!{gZ z2VMvlBzA`ynAERvNue?uhISM9!x&~B{5y+AomQ(eF7EgrGO&av3qllRvjBy#^fwMW Ze*pZ2BckEc)TjUe002ovPDHLkV1nx0Sy2E0 From 43514e673307d4423aa6d9ac730ec7d352a7ca37 Mon Sep 17 00:00:00 2001 From: rnd Date: Tue, 3 Apr 2018 23:21:12 +0100 Subject: [PATCH 02/39] scripts --- init.lua | 6 +- scripts/command_robot.lua | 242 +++++++++++ scripts/copy_paste.lua | 70 +++ scripts/craft_guide.lua | 108 +++++ scripts/games/CTF_bot.lua | 137 ++++++ scripts/games/battle_bot_arena.lua | 91 ++++ scripts/games/battle_minesweeper_game.lua | 150 +++++++ scripts/games/blackbox_game.lua | 195 +++++++++ scripts/games/casino_bot.lua | 38 ++ scripts/games/connect4.lua | 60 +++ scripts/games/fallout_hacking.lua | 95 +++++ scripts/games/hacking_game.lua | 97 +++++ scripts/games/hide_and_seek.lua | 106 +++++ scripts/games/hide_and_seek_1.lua | 151 +++++++ scripts/games/minesweeper_game.lua | 83 ++++ scripts/games/nonogram.lua | 259 +++++++++++ scripts/games/sliding_puzzle_game.lua | 74 ++++ scripts/games/sokoban_game.lua | 175 ++++++++ scripts/games/tank_bot.lua | 85 ++++ scripts/math/fractal_bot.lua | 92 ++++ scripts/math/multiplication.lua | 184 ++++++++ scripts/math/perm2cycles.lua | 83 ++++ scripts/pine_tree_harvest.lua | 65 +++ scripts/programming/brainfuck generator.lua | 59 +++ scripts/programming/brainfuck.lua | 66 +++ scripts/programming/os_show_demo.lua | 114 +++++ scripts/programming/rndscript.lua | 271 ++++++++++++ scripts/simulators/genetic_trust.lua | 335 +++++++++++++++ scripts/simulators/group_assembly.lua | 153 +++++++ scripts/simulators/layout_designer.lua | 132 ++++++ scripts/simulators/nuclear.lua | 70 +++ scripts/simulators/redstone_emulator.lua | 448 ++++++++++++++++++++ scripts/simulators/trust_game.lua | 266 ++++++++++++ scripts/simulators/turtlebot.lua | 28 ++ scripts/spawn_quiz.lua | 62 +++ scripts/tree_harvest.lua | 112 +++++ scripts/utils/chatlog.lua | 46 ++ scripts/utils/language translator.lua | 123 ++++++ scripts/utils/serverbot.lua | 76 ++++ scripts/utils/simple_house_builder.lua | 34 ++ textures/face-back.png | Bin 0 -> 164 bytes textures/face3.png | Bin 0 -> 4740 bytes textures/left-hand.png | Bin 0 -> 159 bytes textures/legs.png | Bin 0 -> 195 bytes textures/right-hand.png | Bin 0 -> 175 bytes textures/robot_side.png | Bin 0 -> 3893 bytes textures/topface.png | Bin 0 -> 203 bytes 47 files changed, 5039 insertions(+), 2 deletions(-) create mode 100644 scripts/command_robot.lua create mode 100644 scripts/copy_paste.lua create mode 100644 scripts/craft_guide.lua create mode 100644 scripts/games/CTF_bot.lua create mode 100644 scripts/games/battle_bot_arena.lua create mode 100644 scripts/games/battle_minesweeper_game.lua create mode 100644 scripts/games/blackbox_game.lua create mode 100644 scripts/games/casino_bot.lua create mode 100644 scripts/games/connect4.lua create mode 100644 scripts/games/fallout_hacking.lua create mode 100644 scripts/games/hacking_game.lua create mode 100644 scripts/games/hide_and_seek.lua create mode 100644 scripts/games/hide_and_seek_1.lua create mode 100644 scripts/games/minesweeper_game.lua create mode 100644 scripts/games/nonogram.lua create mode 100644 scripts/games/sliding_puzzle_game.lua create mode 100644 scripts/games/sokoban_game.lua create mode 100644 scripts/games/tank_bot.lua create mode 100644 scripts/math/fractal_bot.lua create mode 100644 scripts/math/multiplication.lua create mode 100644 scripts/math/perm2cycles.lua create mode 100644 scripts/pine_tree_harvest.lua create mode 100644 scripts/programming/brainfuck generator.lua create mode 100644 scripts/programming/brainfuck.lua create mode 100644 scripts/programming/os_show_demo.lua create mode 100644 scripts/programming/rndscript.lua create mode 100644 scripts/simulators/genetic_trust.lua create mode 100644 scripts/simulators/group_assembly.lua create mode 100644 scripts/simulators/layout_designer.lua create mode 100644 scripts/simulators/nuclear.lua create mode 100644 scripts/simulators/redstone_emulator.lua create mode 100644 scripts/simulators/trust_game.lua create mode 100644 scripts/simulators/turtlebot.lua create mode 100644 scripts/spawn_quiz.lua create mode 100644 scripts/tree_harvest.lua create mode 100644 scripts/utils/chatlog.lua create mode 100644 scripts/utils/language translator.lua create mode 100644 scripts/utils/serverbot.lua create mode 100644 scripts/utils/simple_house_builder.lua create mode 100644 textures/face-back.png create mode 100644 textures/face3.png create mode 100644 textures/left-hand.png create mode 100644 textures/legs.png create mode 100644 textures/right-hand.png create mode 100644 textures/robot_side.png create mode 100644 textures/topface.png diff --git a/init.lua b/init.lua index 5483929..38d0ade 100644 --- a/init.lua +++ b/init.lua @@ -7,7 +7,7 @@ basic_robot.call_limit = 48; -- how many execution calls per script run allowed basic_robot.entry_count = 2 -- how many robots ordinary player can have basic_robot.advanced_count = 16 -- how many robots player with robot privs can have basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches -basic_robot.password = "password"; -- IMPORTANT: change it before running mod, password used for authentifications +basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses ["craft_guide:sign_wall"] = true, @@ -1724,13 +1724,15 @@ end -- handle chats minetest.register_on_chat_message( function(name, message) + local hidden = false; + if string.sub(message,1,1) == "\\" then hidden = true; message = string.sub(message,2) end local listeners = basic_robot.data.listening; for pname,_ in pairs(listeners) do local data = basic_robot.data[pname]; data.listen_msg = message; data.listen_speaker = name; end - return false + return hidden end ) diff --git a/scripts/command_robot.lua b/scripts/command_robot.lua new file mode 100644 index 0000000..1ebc33d --- /dev/null +++ b/scripts/command_robot.lua @@ -0,0 +1,242 @@ +--COMMAND ROBOT by rnd, v2, adapted for skyblock +if not s then + self.listen(1) + s=1;_G.minetest.forceload_block(self.pos(),true) + self.spam(1) + users = {["rnd"]=3,["rnd1"]=3,["Giorge"]=1,["quater"]=1,["UltimateNoob"]=1,["reandh3"]=1,["karo"]=1,["Fedon"]=1,["DS"]=2, + ["Arcelmi"]=1,["Gregorro"]=1,Mokk = 1, Evandro010 = 1} + cmdlvl = {["kill"]=2,["tp"]=3,["heal"]=1, ["rename"]=1,["jump"]=1,["givediamond"]=3, ["msg"]=1,["calc"]=0, ["acalc"]=3,["run"]=3, ["shutdown"] = 3,["sayhi"]=0, ["day"] = 1}; + + tpr = {}; + + cmds = { + help = + { + level = 0, + run = function(param) + local arg = param[2]; + if not arg then + say(colorize("red","OPEN INVENTORY AND READ 'Quests'. YOU GET REWARD AND BETTER STUFF FOR COMPLETING QUESTS. Say /spawn to get to spawn area.")) + return + end + + if arg and cmds[arg] then + local text = cmds[arg]["docs"]; + if text then say(text) end + else + --say(colorize("red","commands") .. colorize("LawnGreen",": 0 status, 2 kill $name, 3 tp $name1 $name2, 1 heal $name, 1 day, 1 rename $name $newname, 3 givediamond $name, 1 msg $name $message, 0 sayhi, 0 calc $formula, 3 acalc $formula, 3 run $expr")) + local msg = "" + for k,v in pairs(cmds) do + msg = msg .. v.level .. " " .. k .. ", " + end + say(colorize("red","commands") .. " " .. colorize("LawnGreen", msg)) + end + end, + docs = "Displays available commands. 'help command' shows help about particular command" + }, + + status = + { + level = 0, + run = function(param) + local usr = param[2] or speaker; + local id = _G.skyblock.players[usr].id; + local pos = _G.skyblock.get_island_pos(id) + minetest.chat_send_all(minetest.colorize("yellow", + usr .. " data : permission level " .. (users[usr] or 0).. ", island " .. id .." (at " .. pos.x .. " " .. pos.z .. "), skyblock level " .. _G.skyblock.players[usr].level + )) + end, + docs = "status name, Show target(speaker if none) level, which determines access privilege." + }, + + kill = + { + level = 2, + run = function(param) + local name = param[2]; if not name then return end + local player = _G.minetest.get_player_by_name(name); + if player then + if (users[name] or 0)<=(users[speaker] or 0) then player:set_hp(0) end + end + end, + docs = "kill name; kills target player", + }, + + tp = + { + level = 2, + run = function(param) + local player1 = _G.minetest.get_player_by_name(param[2] or ""); + local player2 = _G.minetest.get_player_by_name(param[3] or ""); + if player1 and player2 then if (users[param[2]] or 0)<=(users[speaker] or 0) then player1:setpos(player2:getpos()) end end + end, + docs = "tp name1 name2; teleports player name2 to name2", + }, + + tpr = + { + level = 0, + run = function(param) + local name = param[2] or ""; + local player1 = _G.minetest.get_player_by_name(name); + if player1 then tpr = {speaker, name} else return end + _G.minetest.chat_send_player(name,minetest.colorize("yellow","#TELEPORT REQUEST: say tpy to teleport " .. speaker .. " to you.")) + + end, + docs = "tpr name; request teleport to target player", + }, + + tpy = + { + level = 0, + run = function(param) + if speaker == tpr[2] then + local player1 = _G.minetest.get_player_by_name(tpr[1] or ""); + local player2 = _G.minetest.get_player_by_name(tpr[2] or ""); + if player1 and player2 then else return end + player1:setpos(player2:getpos()) + _G.minetest.chat_send_player(tpr[2],minetest.colorize("yellow","#teleporting " .. tpr[1] .. " to you.")) + tpr = {} + end + + end, + docs = "tpy; allow player who sent teleport request to teleport to you.", + }, + + calc = + { + level = 0, + run = function(param) + + local formula = param[2] or ""; + if not string.find(formula,"%a") then + result = 0; + code.run("result = "..formula); + result = tonumber(result) + if result then say(result) else say("error in formula") end + else + say("dont use any letters in formula") + end + end, + docs = "calculate expression", + }, + + day = + { + level = 1, + run = function() minetest.set_timeofday(0.25) end, + docs = "set time to day" + }, + + sayhi = + { + level = 0, + run = function() + local players = _G.minetest.get_connected_players();local msg = ""; + for _,player in pairs(players) do + local name = player:get_player_name(); + local color = string.format("#%x",math.random(2^24)-1) + if name~=speaker then msg = msg..colorize(color , " hi " .. name) .."," end + end + _G.minetest.chat_send_all("<"..speaker..">" .. string.sub(msg,1,-2)) + end, + docs = "say hi to all the other players" + }, + + msg = { + level = 2, + run = function(param) + local text = string.sub(msg, string.len(param[1] or "")+string.len(param[2] or "") + 3) + local form = "size [8,2] textarea[0.,0;8.75,3.75;book;MESSAGE from " .. speaker .. ";" .. _G.minetest.formspec_escape(text or "") .. "]" + _G.minetest.show_formspec(param[2], "robot_msg", form); + end, + docs = "msg name message, displays message to target player", + + }, + + plist = { + level = 0, + run = function() + local p = {}; + for k,v in pairs(minetest.get_connected_players()) do + local name = v:get_player_name() + local pdata = _G.skyblock.players[name] + p[#p+1] = name..", level " .. pdata.level .. "+" .. pdata.completed .. "/" .. pdata.total + end + local text = table.concat(p,"\n") + local form = "size [8,5] textarea[0.,0;8.75,6.75;book;PLAYERS;" .. _G.minetest.formspec_escape(text or "") .. "]" + _G.minetest.show_formspec(speaker, "robot_msg", form); + end, + docs = "plist, displays player list and their levels", + + }, + + run = { + level = 3, + run = function(param) + local expr = string.sub(msg,5); + --say("running " .. expr) + code.run(expr); + end, + docs = "run lua code", + }, + } + + self.label(colorize("red","\nCMD ROBOT")) +end +speaker, msg = self.listen_msg(); + +if msg then + local words = {}; + for word in string.gmatch(msg,"%S+") do words[#words+1]=word end -- extract words + + local level = users[speaker] or 0; + local cmd = words[1]; + + cmdlevel = cmdlvl[cmd] or 0; + if level < cmdlevel then + say("You need to be level " .. cmdlevel .. " to use " .. words[1]) + else + if cmds[cmd] then + cmds[cmd].run(words) + elseif words[1]=="heal" and words[2] then + local player = _G.minetest.get_player_by_name(words[2]); + if player then player:set_hp(20) end + elseif words[1]=="rename" then + local player = _G.minetest.get_player_by_name(words[2]); + if player then if ((users[words[2]] or 0)<=level) and (level>=3 or words[2]~=speaker) then player:set_nametag_attributes({text = words[3] or words[2]}) end end + elseif words[1]=="robot"then + local player = _G.minetest.get_player_by_name(words[2]) + if player then + player:set_properties({visual = "cube"}); + player:set_properties({textures={"arrow.png^[transformR90","basic_machine_side.png","basic_machine_side.png","basic_machine_side.png","face.png","basic_machine_side.png"}}) + player:set_properties({collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}}) + end + elseif words[1] == "mese" then + local player = _G.minetest.get_player_by_name(words[2]) + if player then + player:set_properties({visual = "mesh",textures = {"zmobs_mese_monster.png"},mesh = "zmobs_mese_monster.x",visual_size = {x=1,y=1}}) + end + + elseif words[1] == "givediamond" then + local player = _G.minetest.get_player_by_name(words[2]) + local pos = player:getpos(); + _G.minetest.add_item(pos,_G.ItemStack("default:diamond")) + elseif words[1] == "acalc" then + local formula = words[2] or ""; + if not string.find(formula,"_G") then + result = 0; + code.run("result = "..formula); + result = tonumber(result) + if result then say(result) else say("error in formula") end + end + elseif words[1] == "shutdown" then + _G.minetest.request_shutdown("maintenance, come back",true) + elseif words[1] == "web" then + local text = string.sub(msg,5); + local f = _G.io.open("H:\\sfk\\files\\index.html", "w") + f:write(text);f:close() + + end + end +end \ No newline at end of file diff --git a/scripts/copy_paste.lua b/scripts/copy_paste.lua new file mode 100644 index 0000000..9dae0a6 --- /dev/null +++ b/scripts/copy_paste.lua @@ -0,0 +1,70 @@ +-- COPY PASTE ROBOT by rnd: c1 c2 r = markers, c = copy p = paste + +if not paste then + _G.minetest.forceload_block(self.pos(),true) + paste = {}; + round = function(x) + if x>0 then + return math.floor(x+0.5) + else + return -math.floor(-x+0.5) + end + end + data = {}; + self.listen(1) + self.label("COPY-PASTE MASTER v1.2 gold edition. commands: c1 c2 r c p") +end + + +speaker, msg = self.listen_msg() + +if speaker == "rnd" then + local player = _G.minetest.get_player_by_name(speaker); + local p = player:getpos(); p.x = round(p.x); p.y=round(p.y); p.z = round(p.z); + if msg == "c1" then + paste.src1 = {x=p.x,y=p.y,z=p.z};say("marker 1 set at " .. p.x .. " " .. p.y .. " " .. p.z) + elseif msg == "c2" then + paste.src2 = {x=p.x,y=p.y,z=p.z};say("marker 2 set at " .. p.x .. " " .. p.y .. " " .. p.z) + elseif msg == "r" then + paste.ref = {x=p.x,y=p.y,z=p.z};say("reference set at " .. p.x .. " " .. p.y .. " " .. p.z) + elseif msg == "c" then -- copy + local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z); + local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z); + local count = 0; data = {}; + for i = x1,x2 do + for j = y1,y2 do + for k = z1,z2 do + local node = _G.minetest.get_node({x=i,y=j,z=k}); + if node.name ~= "air" then + if not data[i] then data[i]= {} end + if not data[i][j] then data[i][j]= {} end + data[i][j][k] = {node, _G.minetest.get_meta({x=i,y=j,z=k}):to_table()} + count = count +1; + end + end + end + end + say(count .. " nodes copied "); + elseif msg == "p" then -- paste + local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z); + local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z); + local count = 0; p.x = p.x-paste.ref.x; p.y = p.y-paste.ref.y; p.z = p.z-paste.ref.z; + for i = x1,x2 do + for j = y1,y2 do + for k = z1,z2 do + local pdata; + if data[i] and data[i][j] and data[i][j][k] then + pdata = data[i][j][k] + end + if pdata then + count = count + 1 + _G.minetest.set_node({x=i+p.x,y=j+p.y,z=k+p.z}, pdata[1]); + _G.minetest.get_meta({x=i+p.x,y=j+p.y,z=k+p.z}):from_table(pdata[2]) + end + + end + end + end + say(count .. " nodes pasted "); + end +end \ No newline at end of file diff --git a/scripts/craft_guide.lua b/scripts/craft_guide.lua new file mode 100644 index 0000000..fc088e1 --- /dev/null +++ b/scripts/craft_guide.lua @@ -0,0 +1,108 @@ +-- ROBOT craft guide by rnd, 2017 +if not list then + + tname = "rnd"; + list = {}; + tmplist = _G.minetest.registered_items; + for k,v in pairs(tmplist) do + local texture = v.inventory_image or ""; + if texture=="" and v.tiles then texture = v.tiles[1] or "" end + if (not v.groups.not_in_craft_guide or v.groups.not_in_craft_guide == 0) and type(texture)=="string" and texture~="" then + list[#list+1] = {_G.minetest.formspec_escape(k),_G.minetest.formspec_escape(v.description),_G.minetest.formspec_escape(texture)}; -- v.inventory_image, k, v.description + end + end + + + idx = 1; n = 35; row = 6; size = 1.25; + filter = "" item = "" recipeid = 1 + filterlist = {}; for i = 1,#list do filterlist[i] = i end + + get_texture = function(ritem) + local v = _G.minetest.registered_items[ritem]; if not v then return "" end + local texture = v.inventory_image or ""; + if texture=="" and v.tiles then texture = v.tiles[1] or "" end + if type(texture)~="string" then return "" end + return texture + end + + get_form = function() + local form = "size[7.5,8.5]"; + local x,y,i; local idxt = idx+n; if idxt > #filterlist then idxt = #filterlist end + for i = idx, idxt do + local id = filterlist[i]; + if list[id] and list[id][3] then + x = ((i-idx) % row) + y = (i-idx-x)/row; + form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. list[id][3] ..";".."item"..";".. list[id][1] .."]" + end + end + form = form .. "textarea[0.25,0;2,0.75;filter;filter;"..filter .. "]" .. "button[2.,0;1,0.5;search;search]".. + "button[5.5,0;1,0.5;prev;PREV]" .. "button[6.5,0;1,0.5;next;NEXT]" .. "label[4,0;".. idx .. "-"..idxt .. "/" .. #filterlist.."]"; + return form + end + + get_recipe = function() + local form = "size[7.5,8.5]"; + local recipes = _G.minetest.get_all_craft_recipes(item); if not recipes then return end; + local recipe = recipes[recipeid]; if not recipe then return end + local items = recipe.items + local x,y,i; + for i = 0, 8 do + local ritem = items[i+1] or ""; local sritem = ""; + local j = string.find(ritem,":"); if j then sritem = string.sub(ritem,j+1) end; --ritem = _G.minetest.formspec_escape(ritem); + x = (i % 3) + y = (i-x)/3; + form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. get_texture(ritem) ..";".."item"..";".. sritem .."]" + end + form = form .. "textarea[0.25,0;2,0.75;recipeid;recipeid ".. #recipes .. ";"..recipeid .. "]" .. "button[2.,0;1,0.5;go;go]".. + "label[3,0;" .. item .. "]" .. "button[6.5,0;1,0.5;back;BACK]" ; + return form + end + + s=0 +end + +if s==0 then + local p = find_player(4); s = 1 + if p then + self.show_form(p[1],get_form()) + else + self.remove() + end +end + + +sender,fields = self.read_form() +if sender then + + if fields.search then + filter = fields.filter or "" + filterlist = {}; + for i = 1,#list do + if string.find(list[i][1],filter) then filterlist[#filterlist+1] = i end + end + idx=1;self.show_form(sender,get_form()) + + elseif fields.prev then + idx = idx - n; if idx<1 then idx =#filterlist-n end + self.show_form(sender,get_form()) + elseif fields.next then + idx = idx+n; if idx > #filterlist then idx = 1 end + self.show_form(sender,get_form()) + elseif fields.back then + self.show_form(sender,get_form()) + elseif fields.recipeid then + recipeid = tonumber(fields.recipeid) or 1; + self.show_form(sender,get_recipe()) + elseif fields.item then + item = fields.item; + local recipes = _G.minetest.get_all_craft_recipes(item); + local count = 0; if recipes then count = #recipes end + if count>0 then + recipeid = 1 + self.show_form(sender,get_recipe() or "") + end + elseif fields.quit then + self.remove() + end +end \ No newline at end of file diff --git a/scripts/games/CTF_bot.lua b/scripts/games/CTF_bot.lua new file mode 100644 index 0000000..5cff02d --- /dev/null +++ b/scripts/games/CTF_bot.lua @@ -0,0 +1,137 @@ +-- simple ctf robot, rnd +--instructions: build game arena and place blue/red buttons as flags. edit flag positions below +--you must register 'keyboard' events by placing robot with same 'id' as robot running this code at 'event register' positions - default (32*i,64*j+1, 32*k) + +if not ctf then + _G.minetest.forceload_block(self.pos(),true) + ctf = { + [1] = {state = 1, flagnode = "basic_robot:button8080FF", pos = {x=-18,y=501,z=17}, name = "blue", owner = "", score = 0}, -- team[1] + [2] = {state = 1, flagnode = "basic_robot:buttonFF8080", pos = {x=-79,y=501,z=46}, name = "red", owner = "", score = 0}, -- team[2] + } + + teams = {} -- example : {["rnd"] = {1,0, player, health points at start}}; -- team, ownership of flag + maxscore = 3; + t = 0 + teamid = 1; -- team selector when joining + + gamestate = 0; + self.listen(1) + self.spam(1) + + get_id = function(pos) + local range = 1000; + return pos.x + range*pos.y+range^2*pos.z + end + + flag_id = {}; for i = 1,#ctf do flag_id[get_id(ctf[i].pos)] = i end + + render_flags = function() + for i = 1,#ctf do minetest.set_node(ctf[i].pos, {name = ctf[i].flagnode}) end + end + +end + +if gamestate == 0 then -- welcome + say(colorize("red","#CAPTURE THE FLAG GAME. say '\\join' to join game. to start game one of joined players says '\\start'")) + gamestate = 1 +elseif gamestate == 1 then + speaker,msg = self.listen_msg() + if msg == "join" then + local pl = minetest.get_player_by_name(speaker); + teams[speaker] = {teamid, 0, pl,20};pl:set_hp(20) + local msg1 = ""; local msg2 = "" + for k,v in pairs(teams) do + if v[1] == 1 then msg1 = msg1 .. k .. " " elseif v[1] == 2 then msg2 = msg2 .. k .. " " end + end + + say(colorize("yellow","#CTF : " .. speaker .. " joined team " .. ctf[teamid].name .. ". TEAM " .. ctf[1].name .. ": " .. msg1 .. ", TEAM " .. ctf[2].name .. ": " .. msg2)) + teamid = 3-teamid; -- 1,2 + elseif msg == "start" then -- game start + if teams[speaker] then + gamestate = 2 + keyboard.get() -- clear keyboard buffer + say(colorize("red","#CTF GAME STARTED. GET ENEMY FLAG AND BRING IT BACK TO YOUR FLAG. DONT LET YOUR HEALTH GO BELOW 5 HEARTS OR YOU ARE OUT.")) + for k,_ in pairs(teams) do -- teleport players + local data = teams[k];data[3]:setpos( ctf[data[1]].pos ) + end + render_flags() + end + end + +elseif gamestate == 2 then + -- check player health + for k,v in pairs(teams) do + local hp = teams[k][3]:get_hp(); + if not hp or hp<10 then -- teams[k][4] + + local cflag = teams[k][2]; + if cflag>0 then -- drop flag + ctf[cflag].state = 1 + ctf[cflag].owner = "" + minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode}) + say(colorize("red", "#CTF " .. k .. " dropped " .. ctf[cflag].name .. " flag!")) + end + if not hp then -- player left + say(colorize("yellow", "#CTF " .. k .. " left the game!")) + teams[k] = nil + else -- reset player + say(colorize("yellow", "#CTF " .. k .. " resetted!")) + v[2] = 0 -- player has no flag + v[3]:set_hp(20) + v[3]:setpos( ctf[v[1]].pos ) + end + + + + end + end + + + event = keyboard.get() + if event and teams[event.puncher] then + --say(serialize(event)) + local punch_id = get_id({x=event.x,y=event.y,z=event.z}); + local flag = flag_id[punch_id]; + if flag then + local state = ctf[flag].state + local puncher = event.puncher; + if state == 1 then -- flag is here, ready to be taken or capture of enemy flag + if teams[puncher][1] ~= flag then -- take + say(colorize("red","#CTF " .. puncher .. " has taken " .. ctf[flag].name .. " flag !")) + ctf[flag].state = 2; + ctf[flag].owner = puncher; + teams[puncher][2] = flag; + minetest.set_node(ctf[flag].pos, {name = "basic_robot:buttonFFFF80"}) + else -- capture? + if teams[puncher][2] > 0 then + local cflag = teams[puncher][2] -- puncher has this flag + local data = ctf[cflag]; + + local team = teams[puncher][1]; + ctf[team].score = ctf[team].score + 1 + ctf[team].owner = "" + ctf[cflag].state = 1; -- reset captured flag state + minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode}) + teams[puncher][2] = 0 + say(colorize("orange","#CTF " .. puncher .. " has captured " .. data.name .. " flag! Team " .. ctf[team].name .. " has score " .. ctf[team].score )) + if ctf[team].score == maxscore then + say(colorize("yellow","#CTF: TEAM " .. ctf[team].name .. " WINS! ")) + gamestate = 3;t=5; -- intermission, duration 5 + + --reset + teams = {} + for i=1,#ctf do ctf[i].state = 1 ctf[i].score = 0 ctf[i].owner = "" end + end + + end + + end + + + end + end + --say(serialize(event)) + end +elseif gamestate == 3 then -- intermission + if t>0 then t=t-1 else gamestate = 0 end +end \ No newline at end of file diff --git a/scripts/games/battle_bot_arena.lua b/scripts/games/battle_bot_arena.lua new file mode 100644 index 0000000..a9ceb57 --- /dev/null +++ b/scripts/games/battle_bot_arena.lua @@ -0,0 +1,91 @@ +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 \ No newline at end of file diff --git a/scripts/games/battle_minesweeper_game.lua b/scripts/games/battle_minesweeper_game.lua new file mode 100644 index 0000000..b1ebc06 --- /dev/null +++ b/scripts/games/battle_minesweeper_game.lua @@ -0,0 +1,150 @@ +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 \ No newline at end of file diff --git a/scripts/games/blackbox_game.lua b/scripts/games/blackbox_game.lua new file mode 100644 index 0000000..d7f0d1c --- /dev/null +++ b/scripts/games/blackbox_game.lua @@ -0,0 +1,195 @@ +--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:: \ No newline at end of file diff --git a/scripts/games/casino_bot.lua b/scripts/games/casino_bot.lua new file mode 100644 index 0000000..657cef4 --- /dev/null +++ b/scripts/games/casino_bot.lua @@ -0,0 +1,38 @@ +-- rnd 2017 +if not s then + s=0 + player0 =""; + reward = "default:gold_ingot 6" + price = "default:gold_ingot"; + self.spam(1) +end +if s==0 then + local player = find_player(5); + if player then + player=player[1] + if player~=player0 then + self.label("Hello " .. player .. ". Please insert one gold ingot in chest to play.\nYou need to roll 6 on dice to win 6 gold.") + player0 = player + end + else + self.label(colorize("red","Come and win 6 gold!")) + end + if check_inventory.forward(price) then + take.forward("default:gold_ingot"); + self.label("Thank you for your gold. rolling the dice!") + s=1 + end +elseif s==1 then + roll = math.random(6); + if roll == 6 then + self.label("#YOU WIN!") + say("#WE HAVE A WINNER! get 6 gold in chest!") + insert.forward(reward) + s=2 + else + self.label(":( you rolled " .. roll.. ". Put gold in to try again.") + s=0 + end +elseif s==2 then + if not check_inventory.forward(reward) then s=0 self.label("Please insert one gold to continue playing") end +end \ No newline at end of file diff --git a/scripts/games/connect4.lua b/scripts/games/connect4.lua new file mode 100644 index 0000000..fd3ebd6 --- /dev/null +++ b/scripts/games/connect4.lua @@ -0,0 +1,60 @@ +-- CONNECT, coded in 20 minutes by rnd +if not data then + m=8;n=8;turn = 0; num = 4; + self.spam(1);t0 = _G.minetest.get_gametime(); + spawnpos = self.spawnpos() -- place mines + state = 0; -- 0 signup 1 game + players = {}; + data = {}; + for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end + for i = 1,m do for j = 1,n do -- render game + 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 + + get_count_in_dir = function(dir,x,y) + local r = num; -- num=4? in a row + local snode = data[x][y];local count = 1; + for j = 1,2 do + for i = 1,r-1 do + local x1 = x + dir[1]*i;local y1 = y + dir[2]*i; + if not data[x1] or not data[x1][y1] then break end; if data[x1][y1]~= snode then break end + count = count +1 + end + dir[1]=-dir[1];dir[2]=-dir[2]; + end + return count + end + + get_count = function(x,y) + local c1 = get_count_in_dir({0,1},x,y); local c2 = get_count_in_dir({1,0},x,y) + local c3 = get_count_in_dir({1,1},x,y); local c4 = get_count_in_dir({1,-1},x,y) + if c2>c1 then c1 = c2 end; if c3>c1 then c1 = c3 end; if c4>c1 then c1 = c4 end + return c1 + end + + self.label("CONNECT 4 : GREEN starts play. 2 players punch to join game.") +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 + elseif event.type == 2 then --if event.type == 2 then + if state == 0 then + if #players<2 then players[#players+1] = event.puncher + else state = 1 end + if #players==2 then state = 1 end + end + keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4+turn); + data[x][z] = 4+turn; + if get_count(x,z) == num then say("CONGRATULATIONS! " .. event.puncher .. " has "..num .. " in a row"); self.remove(); goto END end + turn = 1-turn + if state == 1 then + local msg = ""; if turn == 0 then msg = "GREEN " else msg = "BLUE" end + self.label(msg .. " : " .. players[turn+1]) + end + end +end +::END:: \ No newline at end of file diff --git a/scripts/games/fallout_hacking.lua b/scripts/games/fallout_hacking.lua new file mode 100644 index 0000000..54f1c37 --- /dev/null +++ b/scripts/games/fallout_hacking.lua @@ -0,0 +1,95 @@ +if not init then + init = true + + generate_random_string = function(n,m) + local ret = {}; + for i = 1,n do ret[i]=string.char(math.random(m)+96) end --24 + return table.concat(ret) + end + + get_similiarity = function(text1,text2) + local n = string.len(text1); + if string.len(text2)~=n then return 0 end + local ret = 0; + for i = 1,n do + if string.sub(text1,i,i) == string.sub(text2,i,i) then ret = ret + 1 end + end + return ret + end + + get_form = function() + + local n = #passlist; + + local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] " + + for i = 1,10 do + frm = frm .. "button[0,".. (i)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] " + end + + for i = 11,n do + frm = frm .. "button[2,".. (i-11+1)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] " + end + + local form = "size[6," .. 9 .. "]" .. frm; + return form + end + + _G.math.randomseed(os.time()) + intro_msg = minetest.colorize("lawngreen","FALLOUT PASSWORD HACKING GAME\nmatch is both position and character.") + msg = "" --TEST\nTEST\nTEST"; + passlist = {}; passdict = {} + + n = 20; -- how many options + count = 0; + while count< n do + local pass = generate_random_string(5,5); -- password length, charset size + if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end + end + correct = math.random(n) + guesses = 0 + max_guesses = 4 + + rom.data = {}; + if not rom.data then rom.data = {} end + self.spam(1) + + local players = find_player(4); + if not players then say("#fallout hacking game: no players") self.remove() end + pname = players[1]; + say("#fallout hacking game, player " .. pname) + + --if rom.data[pname] then say("password is locked out!") self.remove() end + + self.show_form(pname,get_form()) + self.read_form() + +end + +sender,fields = self.read_form() + if sender and sender == pname then -- form event + local pl = _G.minetest.get_player_by_name(pname); + if pl then + local selected = 0 + for k,_ in pairs(fields) do if k~="quit" then selected = tonumber(k) break end end + + if selected>0 then + guesses = guesses + 1 + if selected == correct then + say("password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.") + self.show_form(pname, "size[1,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]") + self.remove() + --correct: do something with player + else + msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)" + self.show_form(pname, get_form()) + end + if guesses>=max_guesses then + msg = minetest.colorize("red","A C C E S S D E N I E D!") + self.show_form(pname, get_form()) + say("too many false guesses. password locked out!") rom.data[pname] = 1; self.remove() + end + end + if fields.quit then self.remove() end + end + end \ No newline at end of file diff --git a/scripts/games/hacking_game.lua b/scripts/games/hacking_game.lua new file mode 100644 index 0000000..5f2abc8 --- /dev/null +++ b/scripts/games/hacking_game.lua @@ -0,0 +1,97 @@ +-- 'hacking' game from Fallout, by rnd +if not init then + init = true + + generate_random_string = function(n,m) + local ret = {}; + for i = 1,n do ret[i]=string.char(math.random(m)+96) end --24 + return table.concat(ret) + end + + get_similiarity = function(text1,text2) + local n = string.len(text1); + if string.len(text2)~=n then return 0 end + local ret = 0; + for i = 1,n do + if string.sub(text1,i,i) == string.sub(text2,i,i) then ret = ret + 1 end + end + return ret + end + + get_form = function() + + local n = #passlist; + + local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] " + + for i = 1,10 do + frm = frm .. "button[0,".. (i)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] " + end + + for i = 11,n do + frm = frm .. "button[2,".. (i-11+1)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] " + end + + local form = "size[4," .. 9 .. "]" .. frm; + return form + end + + _G.math.randomseed(os.time()) + intro_msg = minetest.colorize("lawngreen","FALLOUT PASSWORD HACKING GAME\nmatch is both position and character.") + msg = "" --TEST\nTEST\nTEST"; + passlist = {}; passdict = {} + + n = 20; -- how many options + count = 0; + while count< n do + local pass = generate_random_string(4,5); -- password length, charset size + if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end + end + correct = math.random(n) + guesses = 0 + max_guesses = 4 + + rom.data = {}; + if not rom.data then rom.data = {} end + self.spam(1) + + local players = find_player(4); + if not players then say("#fallout hacking game: no players") self.remove() end + pname = players[1]; + say("#fallout hacking game, player " .. pname) + + --if rom.data[pname] then say("password is locked out!") self.remove() end + + self.show_form(pname,get_form()) + self.read_form() + +end + +sender,fields = self.read_form() + if sender and sender == pname then -- form event + local pl = _G.minetest.get_player_by_name(pname); + if pl then + local selected = 0 + for k,_ in pairs(fields) do if k~="quit" then selected = tonumber(k) break end end + + if selected>0 then + guesses = guesses + 1 + if selected == correct then + say("password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.") + self.show_form(pname, "size[1,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]") + self.remove() + --correct: do something with player + else + if guesses == 3 then msg = msg .. "\n" end + msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)" + self.show_form(pname, get_form()) + end + if guesses>=max_guesses then + msg = minetest.colorize("red","A C C E S S D E N I E D!") + self.show_form(pname, get_form()) + say("too many false guesses. password locked out!") rom.data[pname] = 1; self.remove() + end + end + if fields.quit then self.remove() end + end + end \ No newline at end of file diff --git a/scripts/games/hide_and_seek.lua b/scripts/games/hide_and_seek.lua new file mode 100644 index 0000000..43d4d9d --- /dev/null +++ b/scripts/games/hide_and_seek.lua @@ -0,0 +1,106 @@ +--HIDE AND SEEK game robot, by rnd +if not gamemaster then + timeout = 10; + gamemaster = "rnd" + player_list = {}; + _G.minetest.chat_send_all("# HIDE AND SEEK .. say #hide to join play") + s=0;t=0; count = 0; + _G.minetest.forceload_block(self.pos(),true) + self.listen(1); self.label(colorize("yellow","HIDE&SEEK")) +end + +speaker,msg = self.listen_msg(); + +if s==0 then + if msg =="#hide" then + player_list[speaker]={}; + _G.minetest.chat_send_all("# HIDE AND SEEK: " .. speaker .. " joined the game") + local player = _G.minetest.get_player_by_name(speaker); + if player then + player:setpos({x=0,y=5,z=0});player:set_properties({nametag_color = "0x0"}) + end + + end + if msg == "#start" and speaker == gamemaster then s = 0.5 _G.minetest.chat_send_all("# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!") end + +elseif s==0.5 then + t=t+1 + if t==timeout then + t=0;s = 1; count = 0; + for pname,_ in pairs(player_list) do + local player = _G.minetest.get_player_by_name(pname); + if player then + player_list[pname].hp = player:get_hp(); + player_list[pname].pos = player:getpos() + player_list[pname].t = 0; + count = count+1 + end + end + if count == 1 then + gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.") + else + _G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS. You are out if: 1.your health changes, 2. leave spawn. If stay in same area for too long or you will be exposed.")) + end + + end +elseif s==1 then + players = _G.minetest.get_connected_players(); + count = 0; + for _,player in pairs(players) do + local name = player:get_player_name(); + local data = player_list[name]; + if data then + count=count+1 + local pos = player:getpos(); + local dist = math.max(math.abs(pos.x),math.abs(pos.y),math.abs(pos.z)); + if dist>50 or (not _G.minetest.get_player_by_name(name)) then + _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " ) + player:set_properties({nametag_color = "white"}) + player_list[name] = nil; + end + if data.hp ~= player:get_hp() then + _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" ) + player:set_properties({nametag_color = "white"}) + player_list[name] = nil; + end + + --expose campers + local p = data.pos; + dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z)); + --say( name .. " dist " .. dist .. " t " .. data.t) + if dist<8 then + data.t = data.t+1; + if not data.camp then + if data.t>15 and not data.camp then + _G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed") + data.camp = true + end + elseif data.t>=20 then + pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z); + _G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z) + data.camp = false; data.t = 0 + end + else + data.t = 0; data.pos = player:getpos(); data.camp = false + end + + end + end + + self.label(count) + + if count<=1 then + if count==1 then + for name,_ in pairs(player_list) do + player0=_G.minetest.get_player_by_name(name) + _G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******")) + player0:set_properties({nametag_color = "white"}) + gamemaster = false; + end + else + _G.minetest.chat_send_all("# HIDE AND SEEK: no players left") + gamemaster = false; + end + end + +end \ No newline at end of file diff --git a/scripts/games/hide_and_seek_1.lua b/scripts/games/hide_and_seek_1.lua new file mode 100644 index 0000000..8582446 --- /dev/null +++ b/scripts/games/hide_and_seek_1.lua @@ -0,0 +1,151 @@ +--HIDE AND SEEK game robot +if not gamemaster then + timeout = 30; + gamemaster = "rnd" + gamepos = {x=0,y=5,z=0} + player_list = {}; + s=0;t=0; count = 0; + prize = "" + + get_players = function() + local msg = ""; + for name,_ in pairs(player_list) do + msg = msg .. " " .. name + end + return msg + end + + init_game = function() + + local msg = get_players(); + _G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK : hide from everyone else who is playing. Winner gets DIAMONDS\nsay join to join play. say #start to start game.".. + " players: " .. msg)) + s=0;t=0; + end + + init_game() + _G.minetest.forceload_block(self.pos(),true) + self.listen(1); self.label(colorize("yellow","HIDE&SEEK")) +end + +speaker,msg = self.listen_msg(); + +if s==0 then + + t = t +1 + if t%30 == 0 then + init_game(); + end + + if msg =="join" then + player_list[speaker]={}; + _G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK: " .. speaker .. " joined the game")); + _G.minetest.chat_send_all("players: " .. get_players()) + + local player = _G.minetest.get_player_by_name(speaker); + count = count + 1 + if player then + player:setpos(gamepos);player:set_properties({nametag_color = "0x0"}) + player:set_hp(20) + local inv = player:get_inventory();inv:set_list("main",{}) + end + + end + if msg == "#start" and count>1 then s = 0.5 _G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!")) end + +elseif s==0.5 then + t=t+1 + if t==timeout then + t=0;s = 1; count = 0; + for pname,_ in pairs(player_list) do + local player = _G.minetest.get_player_by_name(pname); + if player then + player_list[pname].hp = player:get_hp(); + player_list[pname].pos = player:getpos() + player_list[pname].t = 0; + count = count+1 + end + end + if count == 1 then + gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.") + else + prize = "default:diamond " .. (count-1); + _G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS.".. + "You are out if: 1.your health changes, 2. leave game area. If stay in same area for too long or you will be exposed.")) + _G.minetest.chat_send_all(colorize("red","# WINNER WILL GET " .. prize)) + + end + + end +elseif s==1 then + players = _G.minetest.get_connected_players(); + count = 0; + for _,player in pairs(players) do + local name = player:get_player_name(); + local data = player_list[name]; + if data then + count=count+1 + local pos = player:getpos(); + local dist = math.max(math.abs(pos.x-gamepos.x),math.abs(pos.y-gamepos.y),math.abs(pos.z-gamepos.z)); + if dist>100 or (not _G.minetest.get_player_by_name(name)) then + _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " ) + player:set_properties({nametag_color = "white"}) + player_list[name] = nil; + _G.minetest.chat_send_all("remaining players: " .. get_players()) + end + if data.hp ~= player:get_hp() then + _G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" ) + player:set_properties({nametag_color = "white"}) + player_list[name] = nil; + _G.minetest.chat_send_all("remaining players: " .. get_players()) + player:setpos({x=0,y=5,z=0}) + end + + --expose campers + local p = data.pos; + dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z)); + --say( name .. " dist " .. dist .. " t " .. data.t) + if dist<8 then + data.t = data.t+1; + if not data.camp then + if data.t>25 and not data.camp then + _G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed") + data.camp = true + end + elseif data.t>=30 then + pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z); + _G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z) + data.camp = false; data.t = 0 + end + else + data.t = 0; data.pos = player:getpos(); data.camp = false + end + + end + end + + self.label(count) + + if count<=1 then + if count==1 then + for name,_ in pairs(player_list) do + local player0=_G.minetest.get_player_by_name(name) + if player0 then + _G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******")) + local inv = player0:get_inventory(); + inv:add_item("main",_G.ItemStack(prize)) + player0:set_properties({nametag_color = "white"}) + player0:setpos({x=0,y=5,z=0}) + end + s=2 + end + else + _G.minetest.chat_send_all("# HIDE AND SEEK: no players left") + s=2 + end + end + +elseif s==2 then + player_list = {} + init_game() +end \ No newline at end of file diff --git a/scripts/games/minesweeper_game.lua b/scripts/games/minesweeper_game.lua new file mode 100644 index 0000000..7f72bbe --- /dev/null +++ b/scripts/games/minesweeper_game.lua @@ -0,0 +1,83 @@ +-- minesweeper +if not data then + m=24;n=22; minescount = m*n/5; + reward = 30; + + if not find_player(4) then error("minesweeper: no players near") end + + self.spam(1) + 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 + puzzle.set_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},{name = "basic_robot:button808080"}) + end + end end + puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},{name = "basic_robot:button80FF80"}) + + 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:gold_ingot "..reward)) -- diamond reward + else + reward = reward*(1-(count/minescount))^(1.5); reward = math.floor(reward); + say("FAIL! " .. count .. " mines remaining. You get " .. reward .. " gold for found mines") + _G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward + end + self.remove() + end + else --if 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 + puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:button808080"}) + else + puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonFF8080"}) + end + else + if data[x] and data[x][z]==1 then + say("boom! "..event.puncher .. " is dead ");puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:buttonFF8080"}); + local player_ = puzzle.get_player(event.puncher); + player_:setpos({x=spawnpos.x-1,y=spawnpos.y+1,z=spawnpos.z-1}); + self.remove() + else + local count = get_mine_count(x,z); + if count == 0 then puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button80FF80"}) + else puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button"..count}) end + end + end + end +end \ No newline at end of file diff --git a/scripts/games/nonogram.lua b/scripts/games/nonogram.lua new file mode 100644 index 0000000..367440f --- /dev/null +++ b/scripts/games/nonogram.lua @@ -0,0 +1,259 @@ +-- nonogram game, created in 1hr 40 min by rnd + +-- INIT +if not grid then + n=6 + solved = false -- do we render solution or blank? +-- _G.math.randomseed(3) + + self.spam(1) + function get_score_from_string(score) + --say(score) + local scores = {}; + local j=1; --j k l + for i=0,5 do -- 0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999 + j = string.find(score," ", j+1); + local k = string.find(score," ", j+1); + local l = string.find(score," ", k+1); + if i==5 then l = string.len(score)+1 end + scores[i] = {string.sub(score,j+1,k-1),tonumber(string.sub(score,k+1,l-1))}; + j=l + end + return scores + end + + if not rom.score then _,rom.score = book.read(1) end + --rom.score = "0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999" -- default + highscore = get_score_from_string(rom.score) + --self.label(string.gsub(_G.dump(highscore), "\n","")) + + function get_score_string(scores) + local out = "" + for i = 0,5 do + out = out .. i .. " " .. + scores[i][1] .. " " .. + scores[i][2] .. " " + end + return out + end + + t0 = _G.minetest.get_gametime() + local intro ="numbers at beginning of each row (coloumn) tell how many\nred blocks are together in each row ( coloumn )." .. + "\npunch gray blocks to toggle them and reveal hidden red blocks.\npunch green to check solution. If you give up punch blue."; + self.label(intro) + + grid = {} + spawnpos = self.spawnpos(); + offsetx = 10 - math.ceil(n/2); offsetz = math.floor(n/2); + spawnpos.x = spawnpos.x - offsetx; spawnpos.z = spawnpos.z - offsetz; + spawnpos.y = spawnpos.y+3 + + for i=1,n do + grid[i]={}; + for j=1,n do + grid[i][j]=math.random(2)-1 + end + end + + getcounts = function(grid) + local rowdata = {}; + for i=1,n do + rowdata[i]={}; local data = rowdata[i]; + local s=0;local c=0; + for j = 1, n do + if s == 0 and grid[i][j]==1 then s=1;c=0 end + if s == 1 then + if grid[i][j]==1 then + c=c+1 + if j == n then data[#data+1]=c end + else + data[#data+1]=c; s=0 + end + + end + end + end + local coldata = {}; + for j=1,n do + coldata[j]={}; local data = coldata[j]; + local s=0;local c=0; + for i = 1, n do + if s == 0 and grid[i][j]==1 then s=1;c=0 end + if s == 1 then + if grid[i][j]==1 then + c=c+1 + if i == n then data[#data+1]=c end + else + data[#data+1]=c; s=0 + end + + end + end + end + return rowdata,coldata + end + + read_field = function() + local grid = {}; + for i = 1, n do + grid[i]={}; + for j = 1,n do + local typ = keyboard.read({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+i}); + if typ == "basic_robot:button808080" then grid[i][j] = 0 else grid[i][j] = 1 end + end + end + return grid + end + + rowdata,coldata = getcounts(grid) + + check_solution = function() + local rdata,cdata; + rdata,cdata = getcounts(read_field()) + for i = 1,#rdata do + if #rdata[i]~=#rowdata[i] then return false end + for j = 1, #rdata[i] do + if rdata[i][j]~=rowdata[i][j] then return false end + end + end + + for i = 1,#cdata do + if #cdata[i]~=#coldata[i] then return false end + for j = 1, #rdata[i] do + if cdata[i][j]~=coldata[i][j] then return false end + end + end + return true + end + + get_difficulty = function() + local easy = 0; + for k = 1, n do + local sum=0 + for i = 1,#rowdata[k]-1 do + sum = sum + rowdata[k][i]+1; + end + if #rowdata[k]>0 then sum = sum + rowdata[k][#rowdata[k]] else sum = n end + if sum == n then easy = easy + 1 end + end + + for k = 1, n do + local sum=0 + for i = 1,#coldata[k]-1 do + sum = sum + coldata[k][i]+1; + end + if #coldata[k]>0 then sum = sum + coldata[k][#coldata[k]] else sum = n end + if sum == n then easy = easy + 1 end + end + easy = 5-easy; + if easy < 0 then easy = 0 end + return easy + end + + -- render game + for i=1,n do + for j =1,n do + keyboard.set({x=spawnpos.x-n+j,y=spawnpos.y,z=spawnpos.z+i},0) -- clear + keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+2*n-i+1},0) -- clear + local typ; + if grid[j][i]==0 then typ = 2 else typ = 3 end + if not solved then typ = 2 end + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) --board + end + end + + --render counts rows + for i=1,n do + length = #rowdata[i] + for k = 1,length do + keyboard.set({x=spawnpos.x-length+k,y=spawnpos.y,z=spawnpos.z+i},rowdata[i][k]+7) + end + end + --render counts coloumns + for j=1,n do + length = #coldata[j] + for k = 1,length do + keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+k+n},coldata[j][k]+7) + end + end + keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},4) -- game check button + keyboard.set({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},5) -- game check button + + --self.label() + + --self.label(string.gsub(_G.dump(read_field()),"\n","") ) + difficulty = get_difficulty() + reward = 0; limit = 0; + + if difficulty == 5 then limit = 120 reward = 10 + elseif difficulty == 4 then limit = 115 reward = 9 -- 60s + elseif difficulty == 3 then limit = 100 reward = 8 + elseif difficulty == 2 then limit = 80 reward = 7 + elseif difficulty <= 1 then limit = 70 reward = 6 + end + say("nonogram difficulty " .. difficulty .. ". you will get " .. reward .. " gold if you solve it in faster than " .. limit .."s" .. + ". Current record " .. highscore[difficulty][2] .. " by " .. highscore[difficulty][1]) + +end + +event = keyboard.get() +if event then + if event.y == spawnpos.y and event.z == spawnpos.z then + if event.x == spawnpos.x+1 then -- check solution + if check_solution() then + t = _G.minetest.get_gametime(); t = t- t0; + local msg = ""; + keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},2) + msg = n .. "x" .. n .. " nonogram (difficuly " .. difficulty .. ") solved by " .. event.puncher .. " in " .. t .. " seconds. " + + if t < limit then + msg = msg .. " He gets " .. reward .. " gold for quick solve."; + else + reward = reward*2*(1-2*(t-limit)/limit)/2; if reward<0 then reward = 0 end + reward = math.floor(reward); + msg = msg .. " Your time was more than " .. limit .. ", you get " .. reward .. " gold "; + end + + -- highscore + if t0 then + local player = _G.minetest.get_player_by_name(event.puncher); + if player then + local inv = player:get_inventory(); + inv:add_item("main",_G.ItemStack("default:gold_ingot " .. reward)) + end + end + say(msg) + + self.remove() + + else self.label("FAIL") end + elseif event.x == spawnpos.x+2 then -- solve + say("you gave up on game, displaying solution") + for i=1,n do + for j =1,n do + local typ; + if grid[j][i]==0 then typ = 2 else typ = 3 end + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) + end + end + self.remove() + end + else + local i = event.x-spawnpos.x;local j = event.z-spawnpos.z; + if i>0 and i<=n and j>0 and j<=n then + local typ = keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}); + local newtyp; + if typ == "basic_robot:button808080" then newtyp = 3 + else newtyp = 2 + end + keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},newtyp); + end + end +end \ No newline at end of file diff --git a/scripts/games/sliding_puzzle_game.lua b/scripts/games/sliding_puzzle_game.lua new file mode 100644 index 0000000..5e4a3f6 --- /dev/null +++ b/scripts/games/sliding_puzzle_game.lua @@ -0,0 +1,74 @@ +-- sliding unscramble game by rnd, made in 20 minutes +if not init then + init = true + spos = self.spawnpos(); spos.y = spos.y + 1 + board = {}; + size = 3; + + create_board = function(n) + local k = 0; + local ret = scramble(n*n, os.time()) + for i = 1,n do + board[i]={}; + for j = 1,n do + k=k+1 + board[i][j]=7+ret[k] -- 7 numbers, 82 letters + end + end + board[math.random(n)][math.random(n)] = 0 + end + + render_board = function() + local n = #board; + for i = 1,n do + for j = 1,n do + keyboard.set({x=spos.x+i, y = spos.y, z=spos.z+j}, board[i][j]) + end + end + end + + + find_hole = function(i,j) + if board[i][j] == 0 then return i,j end + if i>1 and board[i-1][j] == 0 then return i-1,j end + if i<#board and board[i+1][j] == 0 then return i+1,j end + if j>1 and board[i][j-1] == 0 then return i,j-1 end + if j<#board and board[i][j+1] == 0 then return i,j+1 end + return nil + end + + + scramble = function(n,seed) + _G.math.randomseed(seed); + local ret = {}; for i = 1,n do ret[i]=i end + for j = n,2,-1 do + local k = math.random(j); + if k~=j then + local tmp = ret[k]; ret[k] = ret[j]; ret[j] = tmp + end + end + return ret + end + + create_board(size) + render_board() + +end + +event = keyboard.get(); +if event and event.y == spos.y then + local x = event.x-spos.x; + local z = event.z-spos.z; + if x<1 or x>size or z<1 or z>size then + else + local i,j = find_hole(x,z); + if i then + local tmp = board[x][z]; + keyboard.set({x=spos.x+x, y = spos.y, z=spos.z+z}, board[i][j]) + board[x][z] = board[i][j] + board[i][j] = tmp; + keyboard.set({x=spos.x+i, y = spos.y, z=spos.z+j}, tmp) + end + end + +end \ No newline at end of file diff --git a/scripts/games/sokoban_game.lua b/scripts/games/sokoban_game.lua new file mode 100644 index 0000000..294155c --- /dev/null +++ b/scripts/games/sokoban_game.lua @@ -0,0 +1,175 @@ + -- SOKOBAN GAME, by rnd, robots port + if not sokoban then + sokoban = {}; + local players = find_player(5); + if not players then error("sokoban: no player near") end + name = players[1]; + + self.show_form(name, + "size[2,1.25]".. + "label[0,0;SELECT LEVEL 1-90]".. + "field[0.25,1;1,1;LVL;LEVEL;1]".. + "button_exit[1.25,0.75;1,1;OK;OK]" + ) + state = 1 -- will wait for form receive otherwise game play + + player_ = puzzle.get_player(name); -- get player entity - player must be present in area + player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}) -- reset player + + + self.spam(1) + sokoban.push_time = 0 + sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0; + imax = 0; jmax = 0 + + sokoban.load=0;sokoban.playername =""; sokoban.pos = {}; + SOKOBAN_WALL = "default:brick" + SOKOBAN_FLOOR = "default:stonebrick" + SOKOBAN_GOAL = "default:diamondblock" + SOKOBAN_BOX = "basic_robot:button8080FF" + + load_level = function(lvl) + + local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1; + sokoban.pos = pos; + sokoban.playername = name + + if lvl == nil then return end + if lvl <0 or lvl >89 then return end + + local file = _G.io.open(minetest.get_modpath("basic_robot").."\\scripts\\sokoban.txt","r") + if not file then return end + local str = ""; local s; local p = {x=pos.x,y=pos.y,z=pos.z}; local i,j;i=0; + local lvl_found = false + while str~= nil do + str = file:read("*line"); + if str~=nil and str =="; "..lvl then lvl_found=true break end + end + if not lvl_found then file:close();return end + + sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0; + imax=0; jmax = 0; + while str~= nil do + str = file:read("*line"); + if str~=nil then + if string.sub(str,1,1)==";" then + imax=i; + file:close(); + player_:set_physics_override({jump=0}) + player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0}) + say("games: sokoban level "..sokoban.level .." loaded by ".. name .. ". It has " .. sokoban.blocks .. " boxes to push. "); return + end + i=i+1; + if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions + for j = 1,string.len(str) do + p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j); + p.y=p.y-1; + if puzzle.get_node(p).name~=SOKOBAN_FLOOR then puzzle.set_node(p,{name=SOKOBAN_FLOOR}); end -- clear floor + p.y=p.y+1; + if s==" " and puzzle.get_node(p).name~="air" then puzzle.set_node(p,{name="air"}) end + if s=="#" and puzzle.get_node(p).name~=SOKOBAN_WALL then puzzle.set_node(p,{name=SOKOBAN_WALL}) end + if s=="$" then puzzle.set_node(p,{name=SOKOBAN_BOX});sokoban.blocks=sokoban.blocks+1 end + if s=="." then p.y=p.y-1;puzzle.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;puzzle.set_node(p,{name="air"}) end + --starting position + if s=="@" then + player_:setpos({x=p.x,y=p.y+1,z=p.z}); -- move player to start position + --p.y=p.y-1;puzzle.set_node(p,{name="default:glass"}); + puzzle.set_node(p,{name="air"}) + p.y=p.y+1;puzzle.set_node(p,{name="air"}) + --p.y=p.y+2;puzzle.set_node(p,{name="default:ladder"}) + end + if s~="@" then p.y = pos.y+2;puzzle.set_node(p,{name="air"}); -- ceiling default:obsidian_glass + else --p.y=pos.y+2;puzzle.set_node(p,{name="default:ladder"}) + end -- roof above to block jumps + + end + end + end + + file:close(); + end + + + end + + +if state == 1 then + sender,fields = self.read_form(); -- get fields from form submittal + if sender then + --say(serialize(fields)) + + if fields.LVL then + load_level((tonumber(fields.LVL) or 1)-1) + state = 0 + self.label("stand close to blue box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all boxes pushed on diamond blocks") + end + end +else + + local ppos = player_:getpos() + if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then say(colorize("red", "SOKOBAN: " .. name .. " QUITS ! ")); + player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}); self.remove() end + + event = keyboard.get(); + + if event then + + local pname = event.puncher + if pname ~= name then goto quit end + local pos = {x=event.x, y = event.y, z = event.z}; + local p=player.getpos(pname);local q={x=pos.x,y=pos.y,z=pos.z} + p.x=p.x-q.x;p.y=p.y-q.y;p.z=p.z-q.z + if math.abs(p.y+0.5)>0 then goto quit end + if math.abs(p.x)>math.abs(p.z) then -- determine push direction + if p.z<-0.5 or p.z>0.5 or math.abs(p.x)>1.5 then goto quit end + if p.x+q.x>q.x then q.x= q.x-1 + else q.x = q.x+1 + end + else + if p.x<-0.5 or p.x>0.5 or math.abs(p.z)>1.5 then goto quit end + if p.z+q.z>q.z then q.z= q.z-1 + else q.z = q.z+1 + end + end + + + if minetest.get_node(q).name=="air" then -- push crate + sokoban.moves = sokoban.moves+1 + local old_infotext = minetest.get_meta(pos):get_string("infotext"); + minetest.set_node(pos,{name="air"}) + minetest.set_node(q,{name=SOKOBAN_BOX}) + minetest.sound_play("default_dig_dig_immediate", {pos=q,gain=1.0,max_hear_distance = 24,}) -- sound of pushing + local meta = minetest.get_meta(q); + q.y=q.y-1; + if minetest.get_node(q).name==SOKOBAN_GOAL then + if old_infotext~="GOAL REACHED" then + sokoban.blocks = sokoban.blocks -1; + end + meta:set_string("infotext", "GOAL REACHED") + else + if old_infotext=="GOAL REACHED" then + sokoban.blocks = sokoban.blocks +1 + end + --meta:set_string("infotext", "push crate on top of goal block") + end + end + + if sokoban.blocks~=0 then -- how many blocks left + --say("move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left "); + else + say("games: ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves."); + player_:set_physics_override({jump=1}) + player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0}) + + local i,j; + for i = 1,imax do + for j=1,jmax do + minetest.set_node({x= sokoban.pos.x+i,y=sokoban.pos.y,z=sokoban.pos.z+j}, {name = "air"}); -- clear level + end + end + + sokoban.playername = ""; sokoban.level = 1 + end + ::quit:: + end +end \ No newline at end of file diff --git a/scripts/games/tank_bot.lua b/scripts/games/tank_bot.lua new file mode 100644 index 0000000..8e2356f --- /dev/null +++ b/scripts/games/tank_bot.lua @@ -0,0 +1,85 @@ +-- rnd 2017 +-- instructions: put 7 buttons around bot(top one left empty) +-- clockwise: empty, green, yellow,blue, red, blue,yellow,green. +-- those buttons serve as controls + + if not s then + name = self.name(); + direction = 1; + s=0; + self.label("TANK ROBOT. control with colored buttons") + user=find_player(4); if user then user = user[1] end + + speed = 7 + math.random(7);turn.angle(math.random(360)); + pitch = 0 + + gravity = 1+math.random(2); + if user then + say("TANK ROBOT, ready. ".. user .. " in control") + else + say("no player found nearby. deactivating"); self.remove() + s=-1 + end + pos = self.spawnpos(); + end + + ppos = player.getpos(user); ppos.x=ppos.x-pos.x;ppos.y=ppos.y-pos.y;ppos.z=ppos.z-pos.z; + if ppos.x^2+ppos.y^2+ppos.z^2>10 then + local obj = _G.minetest.get_player_by_name(user); + if obj then say("deserter " .. user .. " killed for abandoning the tank!") obj:set_hp(0) end + self.remove() + else + local obj = _G.minetest.get_player_by_name(user); + if obj then + if obj:get_hp() == 0 then + say("TANK DESTROYED!") + self.remove() + end + end + end + + if s == 0 then + event = keyboard.get(); + if event and event.puncher==user then + --self.label(event.x-pos.x .. " " .. event.y-pos.y .. " " .. event.z-pos.z .. " T " .. event.type) + event.x = event.x-pos.x;event.y = event.y-pos.y;event.z = event.z-pos.z; + if event.x == 0 and event.y == 0 and event.z == 1 then + self.fire(speed, pitch,gravity) + s=1;self.label("BOOM") + _G.minetest.sound_play("tnt_explode",{pos = self.pos(), max_hear_distance = 256, gain = 1}) + elseif event.x == direction*1 and event.y == 0 and event.z == direction*1 then + turn.angle(2) + elseif event.x == -1*direction and event.y == 0 and event.z == 1*direction then + turn.angle(-2) + elseif event.x == 1*direction and event.y == 0 and event.z == 0 then + turn.angle(40) + elseif event.x == -1*direction and event.y == 0 and event.z == 0 then + turn.angle(-40) + elseif event.x == 1*direction and event.y == 0 and event.z == -1*direction then + pitch = pitch + 5; if pitch> 85 then pitch = 85 end + self.label("pitch " .. pitch) + elseif event.x == -1*direction and event.y == 0 and event.z == -1*direction then + pitch = pitch - 5; if pitch<-10 then pitch = -10 end + self.label("pitch " .. pitch) + end + end + end + + if s == 1 then + local pos = self.fire_pos(); + if pos then + self.label("HIT") + msg = ""; + _G.minetest.sound_play("tnt_explode",{pos = pos, max_hear_distance = 256, gain = 1}) + + local objs=_G.minetest.get_objects_inside_radius(pos, 4); + for _,obj in pairs(objs) do + if obj:is_player() then + obj:set_hp(0) + msg = msg .. obj:get_player_name() .. " is dead, " + end + end + s = 0 + if msg~="" then say(msg) end + end + end \ No newline at end of file diff --git a/scripts/math/fractal_bot.lua b/scripts/math/fractal_bot.lua new file mode 100644 index 0000000..7279c44 --- /dev/null +++ b/scripts/math/fractal_bot.lua @@ -0,0 +1,92 @@ +-- robot can construct classic fractals like menger sponge, jerusalem cube, sierpinski triangles,.. +-- use: build a pattern at position 1,1,1 relative to robot. when run robot will analyse pattern and construct fractal +if not init then + minetest.forceload_block(self.pos(),true) + init = true; local spos = self.spawnpos(); + + offsets = {["default:dirt"] = 0, ["default:wood"] = -1, ["default:cobble"] = 1} + + read_form = function(fractal) -- read shape from world + local form = {}; local i = 0; + local spos = self.spawnpos(); spos.x = spos.x+1;spos.y = spos.y+1;spos.z = spos.z+1; + local nx = 0; local ny = 0; local nz = 0; + fractal.form = {} + + for x = 0,fractal.nx-1 do + for y = 0,fractal.ny-1 do + for z = 0,fractal.nz-1 do + local node = _G.minetest.get_node({x=spos.x+x,y=spos.y+y,z=spos.z+z}).name; + local offset = offsets[node] or 0; + if node~= "air" then + form[i] = {x,y,z,offset}; i=i+1 + if nx0 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.sizea^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 \ No newline at end of file diff --git a/scripts/math/perm2cycles.lua b/scripts/math/perm2cycles.lua new file mode 100644 index 0000000..0e6b405 --- /dev/null +++ b/scripts/math/perm2cycles.lua @@ -0,0 +1,83 @@ +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 cycles = " .. arr2string(cycles) ) + +end \ No newline at end of file diff --git a/scripts/pine_tree_harvest.lua b/scripts/pine_tree_harvest.lua new file mode 100644 index 0000000..1b69b6d --- /dev/null +++ b/scripts/pine_tree_harvest.lua @@ -0,0 +1,65 @@ +-- PINE TREE HARVEST by rnd1 +if not harvest then + harvest = {}; + harvest.s = -1 -- -1 idle, 0 expect tree + harvest.tree = "default:pine_tree";harvest.wood = "default:wood";harvest.sapling = "default:pine_sapling"; + harvest.step = function() + local s=harvest.s; + if s == 0 then -- did we bump into tree + local node = read_node.forward(); + if node == harvest.tree then + dig.forward(); move.forward(); harvest.s = 1 -- found tree, moving in + self.label("im digging up tree ") + end + elseif s == 1 then -- climbing up + dig.up(); move.up(); place.down(harvest.wood); + local node = read_node.up(); + if node ~= harvest.tree then + harvest.s = 2 -- top + self.label("i reached top of tree") + end + elseif s == 2 then -- going down + local node = read_node.down(); + if node == harvest.wood then + dig.down(); move.down() + self.label("im going back down") + else + pickup(8); + move.backward();place.forward(harvest.sapling);move.forward(); + harvest.s = -1 -- idle + self.label("i finished cutting tree") + end + end + end +end + +--harvest walk init +if not angle then + sender,mail = self.read_mail(); if sender == "rnd1" then harvest.s = tonumber(mail) or 0 end + wall = "default:cobble"; + angle = 90 +end + +if harvest.s~=-1 then + harvest.step() +elseif harvest.s==-1 then + node = read_node.forward(); + if node==harvest.tree then + harvest.s = 0; + self.label("i found tree") + else + self.label("im walking") + if not move.forward() then + if node == wall then + self.label("i hit wall") + turn.angle(angle); + if move.forward() then + move.forward();move.forward();turn.angle(angle); angle = -angle + else + turn.angle(-angle);angle = - angle + end + end + end + end +end +self.send_mail("rnd1",harvest.s) -- remember state in case of reactivation \ No newline at end of file diff --git a/scripts/programming/brainfuck generator.lua b/scripts/programming/brainfuck generator.lua new file mode 100644 index 0000000..3d910cd --- /dev/null +++ b/scripts/programming/brainfuck generator.lua @@ -0,0 +1,59 @@ +N = 30; -- length of program (without ]) + +desired_frequencies = { + [">"] = 10, + ["<"] = 10, + ["-"]=10, + ["+"]=20, + ["."]=10, + [","]=10, + ["["]=10, -- matching "]" will be inserted automatically! +} +matching_parenthesis = "]"; +routine_lower = 1; routine_higher = 5; -- specify range, how many characters in routine [.....] + +generate_selections = function(desired_frequency) + local sum = 0 + for k,v in pairs(desired_frequency) do sum = sum + v end + local isum = 0; local count = 0; local selections = {} + for k,v in pairs(desired_frequency) do count = count +1 isum = isum + desired_frequency[k]/sum; selections[count] = {isum,k} end + return selections +end + +choose = function(selections, rnd) + local low, mid, high; + low = 1; high = #selections; mid = math.floor((low+high)/2) + local step = 0; + while high-low>1 and step < 20 do + step = step + 1 + if rnd <= selections[mid][1] then high = mid else low = mid end + mid = math.floor((low+high)/2) + end + return selections[mid][2] +end + + +generate_program = function(desired_frequencies,N, routine_lower, routine_higher) + local selections = generate_selections(desired_frequencies); + + local ret = {}; + local count = 0 + local stack = {}; + + for count = 1, N do + local choice = choose(selections, math.random()); + if choice == "[" then + local i = count + math.random(routine_lower,routine_higher) + if i > N then i = N end + stack[#stack+1] = i; + end + ret[count] = choice + end + + for i = 1,#stack do local j = stack[i] ret[j]=ret[j]..matching_parenthesis end + return table.concat(ret) +end + +say(generate_program(desired_frequencies,N, routine_lower, routine_higher)) + +self.remove() \ No newline at end of file diff --git a/scripts/programming/brainfuck.lua b/scripts/programming/brainfuck.lua new file mode 100644 index 0000000..221eb08 --- /dev/null +++ b/scripts/programming/brainfuck.lua @@ -0,0 +1,66 @@ +-- BRAINFUCK interpreter by rnd, 2017 +-- https://en.wikipedia.org/wiki/Brainfuck + +if not ram then + prog = "+++.>++++++.<[->+<]>." + ramsize = 10 + maxsteps = 100; step=0; -- for RUN state only + + n = string.len(prog);ram = {};for i = 1, ramsize do ram[i]=0 end -- init ram + pointer = 1 -- ram pointer + instruction = 1 -- instruction pointer + self.spam(1) + + RUNNING = 1; END = 2; RUN = 3; + state = RUNNING + + get_ram = function() msg = "" for i = 1,ramsize do msg = msg .. ram[i] .. "," end return msg end + + cmdset = { + [">"] = function() pointer = pointer + 1; if pointer > ramsize then pointer = 1 end end, + ["<"] = function() pointer = pointer - 1; if pointer > ramsize then pointer = 1 end end, + ["+"] = function() ram[pointer]=ram[pointer]+1 end, + ["-"] = function() ram[pointer]=ram[pointer]-1 end, + ["."] = function() say(ram[pointer]) end, + [","] = function() ram[pointer] = tonumber(read_text.forward("infotext") or "") or 0 end, + ["["] = function() + if ram[pointer] == 0 then + local lvl = 0 + for j = instruction, n, 1 do + if string.sub(prog,j,j) == "]" then lvl = lvl - 1 if lvl == 0 then + self.label("JMP " .. j ) instruction = j return + end end + if string.sub(prog,j,j) == "[" then lvl = lvl + 1 end + end + end + end, + ["]"] = function() + if ram[pointer] ~= 0 then + local lvl = 0 + for j = instruction, 1, -1 do + if string.sub(prog,j,j) == "]" then lvl = lvl - 1 end + if string.sub(prog,j,j) == "[" then lvl = lvl + 1 if lvl == 0 then + self.label("JMP " .. j ) instruction = j return + end end + end + end + end, + } +end + +-- EXECUTION +if state == RUNNING then + c = string.sub(prog,instruction,instruction) or ""; + if c and cmdset[c] then cmdset[c]() end + self.label("ins ptr " .. instruction .. ", ram ptr " .. pointer .. ": " .. ram[pointer] .. "\n" .. string.sub(prog, instruction).."\n"..get_ram()) + instruction = instruction + 1; if instruction > n then state = END end + +-- RUN THROUGH +elseif state == RUN then + while (step n then self.label("ram : " .. get_ram()) step = maxsteps state = END end + end +end \ No newline at end of file diff --git a/scripts/programming/os_show_demo.lua b/scripts/programming/os_show_demo.lua new file mode 100644 index 0000000..2a5bda6 --- /dev/null +++ b/scripts/programming/os_show_demo.lua @@ -0,0 +1,114 @@ +-- demo bot + +if not s then +-- move.forward();--turn.angle(180) + s=0;t=0 + mag = 3; + + boot = function() + self.display_text("RND technologies\n(inc) 2016\n\n\nmemchk "..(t*1024).. "kb ",16,mag) + t=t+1; if t == 8 then s = 0.5 t = 0 end + end + + boot_memchk = function() + self.display_text("RND technologies\n(inc) 2016\n\n ".. (8*1024).. "kb READY",16,mag) + t=t+1; if t==3 then t=0 s = 1 end + end + + os_load = function() + if t == 0 then self.display_text("============\nrndos v2.5\n============\n\nloading...\nkernel v12.2 ",12,mag) end + t=t+1; if t == 7 then s = 2 t = 0 end + end + + main_menu = function() + if t==0 then self.display_text("MAIN MENU \n\n1) ip lister\n2) kick player\n3) teleport\n4) give fly\n5) kill\n6) turn off\n\n0 to return here from app",16,mag) end + + text = read_text.backward(); + if text and text~="" then + write_text.backward("") + if text == "1" then s = 3 t = 0 + elseif text == "2" then s = 4 t = 0 + elseif text == "3" then s=5 t =0 + elseif text == "4" then s=6 t =0 + elseif text == "5" then s=7 t =0 + elseif text == "6" then self.remove() + end + end + end + + ip_lister = function() + if t%5 == 0 then + players = _G.minetest.get_connected_players(); msg = "IP LISTER\n\n"; + for _, player in pairs(players) do + local name = player:get_player_name(); ip = _G.minetest.get_player_ip(name) or ""; + + msg = msg .. name .. " " .. ip .. "\n"; + end + self.display_text(msg,30,mag) + t=0 + end + t=t+1 + if read_text.backward() == "0" then s=2 t=0 end + + end + + act_on_player = function(mode) + if t==0 then + msg = get_player_list() + local txt = {[1]="KICK WHO?\n", [2] = "TELEPORT WHO HERE?\n", [3] = "GIVE FLY TO WHOM?\n", [4] = "KILL WHO?\n"} + text = txt[mode] or ""; + self.display_text(text..msg,30,mag) t=1 + end + text = read_text.backward(); + if text then + if text=="0" then s=2 t=0 else + write_text.backward(""); + if mode ==1 then + _G.minetest.kick_player(player_list[tonumber(text)] or ""); + elseif mode ==2 then + player =_G.minetest.get_player_by_name(player_list[tonumber(text)] or ""); + if player then player:setpos(self.spawnpos()) end + elseif mode ==3 then + player=_G.minetest.get_player_by_name(player_list[tonumber(text)] or ""); + if player then player:set_physics_override({gravity=0.1}) end + elseif mode ==4 then + player=_G.minetest.get_player_by_name(player_list[tonumber(text)] or ""); + if player then player:set_hp(0) end + end + end + end + end + + player_list = {} + get_player_list = function() + local players = _G.minetest.get_connected_players(); local msg = ""; local i=0; + for _, player in pairs(players) do + local name = player:get_player_name(); + i=i+1;msg = msg .. i ..") " .. name .. "\n"; + player_list[i]=name; + end + return msg + end + +end + +self.label(s) +if s == 0 then + boot() +elseif s==0.5 then + boot_memchk() +elseif s==1 then + os_load() +elseif s==2 then + main_menu() +elseif s==3 then + ip_lister() +elseif s==4 then + act_on_player(1) +elseif s==5 then + act_on_player(2) +elseif s==6 then + act_on_player(3) +elseif s==7 then + act_on_player(4) +end \ No newline at end of file diff --git a/scripts/programming/rndscript.lua b/scripts/programming/rndscript.lua new file mode 100644 index 0000000..b2e304f --- /dev/null +++ b/scripts/programming/rndscript.lua @@ -0,0 +1,271 @@ +-- rnd 2017 +-- call stack, subroutines, recursive subroutines, if jumps, loops, variables + +if not cmd then +prog = "\n".. +""] = function() turn.right() end, + }; + + build.debug =true; + build.stackdepth = 16; + + build.ignored = {["]"]=true,["["]=true}; + +-- RESERVED: Ex(y)[A] : if x == y do A end, N(node)[A] : if read_node.forward()==node then do A end +-- G(label) .. GOTO GR(label) .. CALL SUBROUTINE +a(b),-a(b),=a(b) + + set_routine = function(i) + local jr = string.find(prog,"%[",i+1) + if not jr then say("error, missing [ after position " .. i+1); self.remove() end + build.count = get_var(string.sub(prog,i+1,jr-1)) or 1 + local kr = string.find(prog,"]",jr+1); + if not kr then say("error, missing ] after position " .. jr+1); self.remove() end + return jr,kr --returns routine limits jr,kr + end + + error_msg = function(i) + local callstack = ""; + for _,v in pairs(build.callstack) do + callstack = callstack.. call_marks[v].. ","; + end + + return "ERROR: " ..string.sub(prog,i) .. "\nCALL STACK: " .. callstack + end + + run_routine = function(i,jr,kr) + local count = build.count; + if count > 0 then + if i == kr then + count = count - 1 i = jr+1 + end + --say(" i " .. i .. " kr " .. kr) + if count > 0 then + c=string.sub(prog,i,i) + if c == "G" then i=go_to(i); build.state = 0 else -- exit routine + if cmd[c] then cmd[c]() else + if not ignored[c] then + self.label("run routine: invalid instruction at position " .. i .. "\n" .. error_msg(i)); + self.debug = false; build.state = -1 + end + + end + end + end + else + i=kr-- exit,jump to next instruction + build.state = 0 + if build.debug then self.label("["..build.state .. "]" .. i .. "\nROUTINE EXIT") end + end + + build.count = count + return i -- return next execution address + end + + push_callstack = function(val) -- addresses where to continue after function ends + if #build.callstack > build.stackdepth then say("error: stack depth limit " .. build.stackdepth .. " exceeded "); self.remove() end + build.callstack[#build.callstack+1] = val; + end + + pop_callstack = function() + local val = build.callstack[#build.callstack]; + build.callstack[#build.callstack] = nil; + return val + end + + go_to = function(i) + local j = string.find(prog,"%(",i+1); local k = string.find(prog,"%)",j+1) + local call = false; + if string.sub(prog,i+1,j-1) == "R" then call = true push_callstack(k) end -- function call, save call exit address to return here + + local target = string.sub(prog,j+1,k-1); + if target == "" then -- marking end of routine + i = pop_callstack(); + if build.debug then self.label("["..build.state .. "]" .. i .. "\nEXITING ROUTINE to " .. i .. ", stack level " .. #build.callstack) end + else + i = go_to_mark[target]-1; + if call == false then + if build.debug then self.label("["..build.state .. "]" .. i .. "\nGOTO " .. target .. "=".. i ) end + else + if build.debug then self.label("["..build.state .. "]" .. i .. "\nCALL SUBROUTINE " .. target .. "=".. i .. "\ncall stack = " .. string.gsub(_G.dump(build.callstack),"\n","")) end + end + end + + return i; + end + + -- scan for go_to markers + go_to_mark = {}; + call_marks = {}; -- OPTIONAL for nicer error messages + scan_go_to = function() + local i = 0; + while ( i < string.len(prog)) do + i=i+1; + if string.sub(prog,i,i+1) == "M(" then + local j = string.find(prog,"%(",i+1) + local k = string.find(prog,"%)",j+1) + local name = string.sub(prog,j+1,k-1); + prog = string.sub(prog,1,i-1)..string.sub(prog,k+1) + go_to_mark[name] = i; + end + end + + i=0 -- OPTIONAL call marks scanning + while ( i < string.len(prog)) do + i=i+1; + if string.sub(prog,i,i+2) == "GR(" then + local j = string.find(prog,"%(",i+1) + local k = string.find(prog,"%)",j+1) + local name = string.sub(prog,j+1,k-1); + call_marks[k] = name; + end + end + end + + get_var = function(s) + local ns = tonumber(s); + if _G.tostring(ns)==s then return ns else return build.var[s] end + end + + prog = string.gsub(prog, " ", "") -- remove all spaces + prog = string.gsub(prog, "\n", "") -- remove all newlines + scan_go_to() + build.n=string.len(prog) + build.i=1; + build.count = 1; + build.state = 0; + build.callstack = {}; + build.var = {}; + self.spam(1) +end + +build.run = function() + + local i=build.i; -- get current execution address + + local jr,kr -- routine execution boundaries + local jc, kc + + --i=i+1; if i>build.n then i = 1 end end + + c=string.sub(prog,i,i) + + if build.state == 0 then + if c == "R" then + jr,kr=set_routine(i); i = jr; -- set up execution point i=jr + build.state = 1 + elseif c == "=" then + jc = string.find(prog,"%(",i+1) + kc = string.find(prog,"%)",jc+1) + local var1 = string.sub(prog,i+1,jc-1); + local var2 = get_var(string.sub(prog,jc+1,kc-1)); + build.var[var1]=var2 + i=kc + + elseif c == "+" then + jc = string.find(prog,"%(",i+1) + kc = string.find(prog,"%)",jc+1) + local var1 = string.sub(prog,i+1,jc-1); + local var2 = get_var(string.sub(prog,jc+1,kc-1)); + build.var[var1]=build.var[var1]+var2 + i=kc + elseif c == "-" then + jc = string.find(prog,"%(",i+1) + kc = string.find(prog,"%)",jc+1) + local var1 = string.sub(prog,i+1,jc-1); + local var2 = get_var(string.sub(prog,jc+1,kc-1)); + build.var[var1]=build.var[var1]-var2 + i=kc + elseif c == "E" then + jc = string.find(prog,"%(",i+1) + kc = string.find(prog,"%)",jc+1) + local INOT = string.sub(prog,i+1,i+3) == "NOT"; + local trigger; local var1; local var2; + + if INOT then + var1 = get_var(string.sub(prog,i+4,jc-1)); + var2 = get_var(string.sub(prog,jc+4,kc-1)); + else + var1 = get_var(string.sub(prog,i+1,jc-1)); + var2 = get_var(string.sub(prog,jc+1,kc-1)); + end + trigger = (var1 == var2) + + if (not INOT and trigger) or (INOT and not trigger)then + i=kc; + jr,kr=set_routine(i);i=jr + build.state = 1 + else + kc = string.find(prog,"]",kc+1) + i = kc + end + elseif c == "N" then + jc = string.find(prog,"%(",i+1) + kc = string.find(prog,"%)",jc+1) + local node = string.sub(prog,jc+1,kc-1) or "air"; + local INOT = string.sub(prog,i+1,jc-1) == "NOT"; + local trigger; + trigger = read_node.forward() == node; + + if (not INOT and trigger) or (INOT and not trigger)then + i=kc; + jr,kr=set_routine(i); i = jr + build.state = 1 + else + kc = string.find(prog,"]",kc+1) + i = kc + end + elseif c == "G" then + i=go_to(i); + elseif c == "C" then + jc = string.find(prog,"%(",i+1) + kc = string.find(prog,"%)",jc+1) + var = string.sub(prog,jc+1,kc-1); + i = kc + self.label(var .. "=" .. get_var(var)) + else + if cmd[c] then cmd[c]() else + if not build.ignored[c] then + self.label("run main: invalid instruction at position " .. i.. "\n" .. error_msg(i)); + build.state = -1; self.debug = false; + end + end + end + elseif build.state == 1 then -- routine + jr = build.jr; kr = build.kr; + i=run_routine(i,jr,kr) + end + + + i=i+1; if i>build.n then i = 1 end + + build.i = i -- update execution address + build.jr = jr; + build.kr = kr; + +end + +if build.debug then self.label("["..build.state .. "]" .. build.i .. "\n" .. string.sub(prog,build.i)) end +build.run() \ No newline at end of file diff --git a/scripts/simulators/genetic_trust.lua b/scripts/simulators/genetic_trust.lua new file mode 100644 index 0000000..2e07497 --- /dev/null +++ b/scripts/simulators/genetic_trust.lua @@ -0,0 +1,335 @@ +if not init then + rom.best_player = nil + init = true + depth = 4; -- how many moves does random player evaluate + error_rate = 0.25; -- players make wrong decision with this probability + generation = 10; -- how many times we repeat + steps = 100; -- how many steps each generation + bestc = 0; -- how many times was new best player picked + mode = 2; -- 1, evolution!, 2 play game + + -- game pay offs + rules = { + { + {2., 2.}, -- first player cooperate, second player cooperate: +2,+2 + {-1, 3}, -- first player cooperate, second player cheat: -1,+3 + }, + { + {3,-1}, -- first player cheats, second player cooperate + {0,0}, -- first player cheats, second player cheat + } + }; + + copytable = function(tab) + if type(tab)~="table" then return tab end + local ret = {}; + for k,v in pairs(tab) do ret[k] = copytable(v) end return + ret + end + + copycat = { + rules = { + {0}, -- initial: 0 = cooperate + {0,1}, -- after one move : cooperate if other cooperated last turn, cheat if other cheated last turn + }, + -- encode decision sequence in binary: 01110 = dec 8+4+2=14 + memory = 1, -- how many moves back player consideres? + moves ={}, -- opponent moves + mood = 0, -- probability that player will cheat if he had intention to cooperate + moody = 0.5, -- by how much cheat/cooperate change mood + points = 0, + name = "copycat" + } + + cheater = { + rules = { + {1}, + }, + memory = 0, -- how many moves back player consideres? + moves ={}, -- opponent moves + mood = 0, + moody = 0.5, + points = 0, + name = "cheater" + } + + realplayer = { + rules = { + {0}, + }, + memory = 0, -- how many moves back player consideres? + moves ={}, -- opponent moves + mood = 0, + moody = 0., + points = 0, + name = "real player", + real = true, + out = 0 + } + + + create_random_player = function(memory) + local rules = {}; + for i = 1, memory+1 do + rules[i] = {}; + for j = 1,2^(i-1) do + rules[i][j] = math.random(2)-1 + end + end + return {rules = rules, memory = memory, moves = {}, mood = 0, moody = math.random(), points = 0, name = "randomplayer"} + end + + + -- player makes next move according to his memory of moves so far + play = function(player) + if player.real then return player.out end -- real player + local moves = player.moves; + local n = #moves; + if n > player.memory then n = player.memory end -- there are many moves, examine only what we can + local rules = player.rules[n+1] + local state = bin2dec(player.moves,n); -- examine last n moves + --say("n " .. n .. " state " .. state) + return rules[state+1] + end + + + + group_play = function(playrs) -- each randomplayer plays with every other player in lower group + local n = #playrs; + local m = 10; -- play m games with each opponent, random pair order + for i = 1,10 do + for j = 11,20 do -- i plays with j, randomized order + playrs[i].moves = {}; playrs[j].moves = {}; -- reset remembered moves before paired match! + playrs[i].mood = 0; + for k = 1,m do + if math.random(2) == 1 then + interact(playrs[i],playrs[j]) + else + interact(playrs[j],playrs[i]) + end + end + end + end + + end + + sort_players = function(pl) + table.sort(pl, + function(p1,p2) return p1.points1 then mood = 1 elseif mood<0 then mood = 0 end + player2.mood = mood + end + + if res1 == 0 then -- mood change for player1 + mood = player1.mood; + if res2==1 then + mood = mood + player1.moody; + else + mood = mood - player1.moody; + end + if mood>1 then mood = 1 elseif mood<0 then mood = 0 end + player1.mood = mood + end + end + + dec2bin = function(input) + local ret = {}; + if input == 0 then return {0} end + while input~=0 do + local r=input%2; input = (input -r)/2 + ret[#ret+1] = r; + end + local n = #ret; + local rret = {} + for i = 1, n do + rret[i]=ret[n-i+1] + end + return rret + end + + bin2dec = function(bin, length) -- length= how many last elements we take + if length == 0 then return 0 end + if not length then length = #bin end + local offset = #bin - length; if offset<0 then offset = 0 end + local ret = 0; + for i = 1,#bin-offset do + ret = 2*ret + bin[i+offset] + end + return ret + end + + get_results = function(players) + local ret = {} for i=1,#players do ret[i] = players[i].name .. " " .. players[i].points .. "M " .. players[i].mood .. "(" .. players[i].moody .. ")" end return table.concat(ret,"\n") + end + + + players = {} -- start with 5 cheaters, 5 copycats and 10 randomplayers + + if mode == 1 then + for i = 1,5 do players[i] = copytable(cheater) end + for i = 1,5 do players[5+i] = copytable(copycat) end + + for i = 1,10 do players[10+i] = create_random_player(depth) end -- last 10 players are random + + + age = 0 + rom.best_player = nil; + elseif mode == 2 then + players = {copytable(realplayer), copytable( create_random_player(4) ) } ; + self.listen(1) + end + --players[20] = copytable(rom.best_player) or create_random_player(depth) -- add best player from before + --players[20].name = "randomplayer" + + +end + + +if mode == 1 then + + local bestpoints = 0 + for k = 1, generation do -- repeat experiment generation* + + --rom.best_player = nil + for i = 1,#players do players[i].points = 0 end + + + for j = 1, steps do -- several steps to see who is best long term on average + --for i = 1,#players do players[i].points = 0 end + group_play(players) + end + + bestpoints = 0 + + genetics(players) -- remove 5 worst randomplayers & replace them by new randoms + + + local population = {} + bestrandom = 0 + for i = 11,20 do + if players[i].points >= bestrandom then bestrandom = players[i].points end + population[#population+1] = players[i] + end + --say("randomplayer population size " .. #population) + sort_players(population); + if rom.best_player then + lastbest = rom.best_player.points + if bestrandom >= lastbest then -- PICK NEW BEST + bestc = bestc+1 + rom.best_player = copytable(population[#population]); -- remember best randomplayer for next experiment + end + else + rom.best_player = copytable(population[#population]); -- remember best randomplayer for next experiment + end + + + end + + age = age + generation + + --display results! + msg = "" + msg = msg .. "Planet Earth (galactical name Halfwits), age " .. age .. ", error_rate " .. error_rate .. ", steps " .. steps .. ", generations " .. generation .. ":\n" + msg = msg .."\nlast round\n" + --sort_players(players) + bestpoints = 0 + for i =1,#players do + if players[i].points>bestpoints then bestpoints = players[i].points end + msg = msg .. players[i].name .. ": " .. players[i].points .. " M " .. players[i].mood .. "(" .. players[i].moody .. ")\n" + end + local rules = rom.best_player.rules; + msg = msg .. "BEST: " .. bestpoints .. "\n\n## alltime best random player no. " .. bestc .. ", points " .. rom.best_player.points .. " moody " .. rom.best_player.moody .. + "\ncurrent best random player/max current score: ".. math.floor(100*bestrandom/bestpoints*100)/100 .. "% \nrules " .. serialize(rules) .. " )\n"; + + local msg1 = "{" .. rules[1][1] .. "}\n" + for i = 2,#rules do + local rule = rules[i]; + msg1 = msg1.. "{" + for j = 1,#rule do + local rule_string = table.concat(dec2bin(j-1),""); + rule_string = string.rep("0",i-string.len(rule_string)-1) .. rule_string; + msg1 = msg1 .. rule_string .."="..rule[j].."," + end + msg1 = msg1 .. "}\n" + end + + self.label(msg .. msg1) + +elseif mode == 2 then -- play vs player + + speaker,msg = self.listen_msg(); + if msg then + if msg == "0" or msg == "1" then + local nextmove = tonumber(msg); + if nextmove == 1 and players[1].out == 1 then + say("look buddy, we dont like cheaters around here. dont cheat twice in a row") + else + players[1].out = nextmove + interact(players[1], players[2]) + say("input " .. players[1].out .. ", BOT MOVES: " .. serialize(players[1].moves) .. " BOT MOOD " .. players[2].mood .. "(" .. players[2].moody .. ") SCORE: you " .. players[1].points .. " bot " .. players[2].points) + end + end + end + + +end \ No newline at end of file diff --git a/scripts/simulators/group_assembly.lua b/scripts/simulators/group_assembly.lua new file mode 100644 index 0000000..5088f48 --- /dev/null +++ b/scripts/simulators/group_assembly.lua @@ -0,0 +1,153 @@ +-- rnd's robot swarm assembly algorithm 2017 +-- https://www.youtube.com/watch?v=xK54Bu9HFRw&feature=youtu.be&list=PLC7119C2D50BEA077 +-- notes: +-- 1. limitation: there must be room for diagonal move +-- this is not big limitation: assume bots are circles of radius 1, then to allow diagonal movement +-- just spread them by factor sqrt(2)~1.4 initially +-- 2. initial random placement(not part of move algorithm): due to collision some bots may occupy same place + +if not pos then + n=50; m = 500; + stuck = m; + state = 0; + step = 0 + + pos = {}; tpos = {}; + -- INIT + for i = 1, m do + --local r = i % n;local c = (i-r)/n;pos[i]={n-c,r+1}; -- regular rectangle shape + pos[i]={math.random(n),math.random(n)}; + --tpos[i]={math.random(n),math.random(n)}; -- random shape + local r = i % n;local c = (i-r)/n;tpos[i]={c+1,r+1}; -- regular rectangle shape + end + doswap = true -- use closest swap or not? + + -- initially swap ids so that i-th bot is closest to i-th target + permute2closest = function() + -- swap bot i with one closest to i-th target + free = {}; for i = 1, m do free[i] = i end -- list of available ids for swapping + local opos = {}; + for i=1,m do opos[i] = {pos[i][1],pos[i][2]} end + closest = {}; + + for i = 1,m do + -- find closest bot to i-th point + local dmin = 2*n; + local jmin = -1; + local tp = tpos[i]; + for j = 1,#free do + local p = opos[free[j]]; + local d = math.sqrt((p[1]-tp[1])^2+(p[2]-tp[2])^2); + if d< dmin then dmin = d; jmin = j end + end + if jmin>0 then + local newj = free[jmin]; + pos[i] = {opos[newj][1], opos[newj][2]}; -- reassign id + table.remove(free,jmin); + end + end + end + + if doswap then + permute2closest() + else + for i=1,m do pos[i] = opos[i] end -- just copy positions + end + + data = {}; + + for i = 1,n do data[i]={}; for j=1,n do data[i][j] = {0,0,1} end end -- 0/1 present, id, move status? + for i = 1,#pos do data[pos[i][1]][pos[i][2]] = {1,i,1} end -- 1=present,i = id, 1=move status + + + step_move = function() + local count = 0; + for i = 1, #pos do + local p = pos[i]; + local tp = tpos[i]; + local x = tp[1]-p[1]; + local y = tp[2]-p[2]; + local d = math.sqrt(x^2+y^2); + if d~=0 then + x=x/d;y=y/d + x=p[1]+x;y=p[2]+y; + x=math.floor(x+0.5);y=math.floor(y+0.5); + if data[x][y][1]==0 then -- target is empty + data[p[1]][p[2]][1] = 0; data[x][y][1] = 1 + pos[i]={x,y}; data[x][y][2] = i; data[x][y][3] = 1; + end + else + data[p[1]][p[2]][3] = 0 -- already at position + count = count +1 + end + end + return m-count -- how many missaligned + end + + render = function() + out = ""; + for i = 1,n do + for j= 1,n do + if data[i][j][1]==1 then + local id = data[i][j][2]; id = id % 10; + if data[i][j][3] == 0 then + out = out .. id + else + out = out .. "S" -- didnt move last step + end + else + out = out .. "_" -- empty + end + end + out = out .. "\n" + end + return out + end + + s=1 + self.listen(1) +end + +speaker,msg = self.listen_msg() +if speaker == "rnd" then + if msg == "p" then + say("permute2closest()") + permute2closest() + end +end + +if s == 1 then + step = step + 1 + local c = step_move(); + --state = how many times stuck count was constant; if more than 3x then perhaps it stabilized? + -- stuck = how many robots not yet in position + if c 3 then state = 0 s = 2 end end + self.label(render().. "\nleft " .. stuck .. "="..(100*stuck/m) .. "%") + if stuck == 0 then say("*** COMPLETED! in " .. step .." steps ***") s = 3 end +elseif s == 2 then + -- do swaps of stuck ones.. + for i = 1, #pos do + local p = {pos[i][1], pos[i][2]}; + local tp = tpos[i]; + local x = tp[1]-p[1]; + local y = tp[2]-p[2]; + local d = math.sqrt(x^2+y^2); + if d~=0 then + x=x/d;y=y/d + x=p[1]+x;y=p[2]+y; + x=math.floor(x+0.5);y=math.floor(y+0.5); -- see whats going on in attempted move direction + if data[x][y][1]==1 then -- there is obstruction, do id swap + local x1,y1; + x1 = x; y1 = y; + local idb = data[x][y][2]; -- blocker id, stuck robot id is i + pos[i]={x,y}; -- new position for id-i is position of blocker + pos[idb] = {p[1],p[2]}; -- new position for blocker is position of stuck robot + -- reset stuck status + data[x][y][3]=1;data[p[1]][y][p[2]]=1; + end + end + end + + s=1 +end +--TO DO: if robots stuck do permute2closest again \ No newline at end of file diff --git a/scripts/simulators/layout_designer.lua b/scripts/simulators/layout_designer.lua new file mode 100644 index 0000000..f7e0185 --- /dev/null +++ b/scripts/simulators/layout_designer.lua @@ -0,0 +1,132 @@ +-- rnd 2017 +if not data then + m=50;n=50; minescount = m*n/14; + + t0 = _G.minetest.get_gametime(); + rom.data = {}; rom.rooms = {} -- so we dont make new tables everytime + data = rom.data; spawnpos = self.spawnpos(); + rooms = rom.rooms; + 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 + + -- generate level data + for i = 1,m do rooms[i]={}; for j = 1,n do + if get_mine_count(i,j) > 0 or (data[i] and data[i][j] == 1) then + rooms[i][j] = 1 + else + rooms[i][j] = 0 + end + end end + + + -- find passages + for i = 2,m-1 do for j = 2,n-1 do + if rooms[i][j] == 0 then + local A11 = rooms[i-1][j-1]; local A21 = rooms[i][j-1];local A31 = rooms[i+1][j-1]; + local A12 = rooms[i-1][j]; local A32 = rooms[i+1][j]; + local A13 = rooms[i-1][j+1]; local A23 = rooms[i][j+1];local A33 = rooms[i+1][j+1]; + + if (A12~=1 and A32~=1 and A21 == 1 and A23 == 1) or + (A12==1 and A32==1 and A21 ~= 1 and A23 ~= 1) + then + rooms[i][j] = 2; -- passage + end + end + end end + + read_room = function(i,j) + if i<1 or i > m then return nil end + if j<1 or j > n then return nil end + return rooms[i][j] + end + + render_rooms = function() + for i = 1,m do for j = 1,n do + local tile = rooms[i][j]; + if tile == 0 then + _G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "air"}) + _G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+j}, {name = "air"}) + elseif tile == 1 then + _G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"}) + _G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+j}, {name = "basic_robot:buttonFFFFFF"}) + elseif tile == 2 then -- passage, insert 1 door in it + --determine direction + local dir = {0,0} + if read_room(i+1,j) == 2 then dir = {1,0} + elseif read_room(i-1,j) == 2 then dir = {-1,0} + elseif read_room(i,j+1) == 2 then dir = {0,1} + elseif read_room(i,j-1) == 2 then dir = {0,-1} + elseif read_room(i-1,j) ~= 0 or read_room(i+1,j) ~= 0 then dir = {0,1} + else dir = {1,0} + end + local k1 = 0; local k2 = 0; + for k = 1, 10 do + if read_room(i+dir[1]*k,j+dir[2]*k)~= 2 then k1 = k-1 break + else + if rooms[i+dir[1]*k] then rooms[i+dir[1]*k][j+dir[2]*k] = 0 end + _G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y,z=spawnpos.z+j+dir[2]*k}, {name = "air"}) + end + end + for k = 1, 10 do + if read_room(i-dir[1]*k,j-dir[2]*k)~= 2 then k2 = -(k-1) break + else + if rooms[i+dir[1]*k] then rooms[i+dir[1]*k][j+dir[2]*k] = 0 end + _G.minetest.swap_node({x=spawnpos.x+i-dir[1]*k,y=spawnpos.y,z=spawnpos.z+j-dir[2]*k}, {name = "air"}) + end + end + local k = math.floor((k1+k2)/2); + --place door + local param = 1 + if dir[1]~=0 then param = 2 end + _G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y+1,z=spawnpos.z+j+dir[2]*k}, {name = "air"}) + if param == 1 then + _G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y,z=spawnpos.z+j+dir[2]*k}, {name = "doors:door_wood_a", param2 = 2}) + else + _G.minetest.swap_node({x=spawnpos.x+i+dir[1]*k,y=spawnpos.y,z=spawnpos.z+j+dir[2]*k}, {name = "doors:door_wood_a", param2 = 1}) + end + + + + elseif tile == 3 then + _G.minetest.swap_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}, {name = "default:stonebrick"}) + + end + end end + end + + render_rooms() + + + fill_room = function(x,y, roomIdx) -- room index: 1,2,3,... will be written as -1,-2,.. in rooms + local tile = rooms[i][j]; + if tile ~= 0 then return false end + + rooms[i][j] = -roomIdx; + local stk = {{i,j}}; -- stack to place border tiles + local tmpstk = {}; -- temporary stack + + local free = true; -- are there any free room tiles + + while free do + + -- loop all stack tiles + for i=1,#stk do + local p = stk[i]; + tile = rooms[p[1]][p[2]]; + end + + end + + + end + + +end +self.remove() \ No newline at end of file diff --git a/scripts/simulators/nuclear.lua b/scripts/simulators/nuclear.lua new file mode 100644 index 0000000..7c76aa5 --- /dev/null +++ b/scripts/simulators/nuclear.lua @@ -0,0 +1,70 @@ +-- rnd 2017 +if not data then + data = {FUEL = 1.8,TV=0,T=0,P=0,E=0;} + + generate_nuclear_power = function(CONTROL,COOLING) + if COOLING>1 then COOLING = 1 elseif COOLING<0 then COOLING = 0 end + if CONTROL>1 then CONTROL = 1 elseif CONTROL<0 then CONTROL = 0 end + --data = ... + local FUEL = data.FUEL; + local TV = data.TV; + local T = data.T; + local P = data.P; + local E = data.E; + + -- reactor specifications + local TVmax = 10000; + local FUELC = 2; -- critical mass for fuel + local DECAYRATE = 1-0.01; -- how much fuel decays per time step + local COOLINGCOEF = 1; -- how efficient is cooling per 1 unit of power (how many degrees are cooled) + local PCOEF = 1; -- how efficiently temperature is converted to power + + local TGV = FUEL/(1-(FUEL/FUELC)^2); + if TGV>TVmax then TGV = TVmax end; if FUEL>FUELC then TGV = TVmax end -- basic temperature generation speed generated in core + TV = TV + TGV* CONTROL - P*COOLING*COOLINGCOEF; -- temperature change speed + T = T + TV ; + P = 0.5*P + T*PCOEF; P = P - P*COOLING -- produced power + FUEL = FUEL*DECAYRATE; + if P<0 then P = 0 end if T<0 then T = 0 end E=E+P; + + data.FUEL = FUEL; + data.T = T; + data.TV = TV; + data.P = P; + data.E = E + return E, P, T, FUEL, TV + end + + render = function(data) -- data should be normalized [0,1] + local tname = "pix.png"; + local obj = _G.basic_robot.data[self.name()].obj; + local n = 150; local m = n; + local length = #data; if length == 0 then return end + local hsize = 1; local wsize=hsize; + local tex = "[combine:"..(wsize*m).."x"..(hsize*n); + for i = 1,length do + j=math.floor((1-data[i])*m); + local ix = math.floor((i/length)*n) + tex = tex .. ":"..(ix*wsize).."," .. (j*hsize) .. "="..tname + end + obj:set_properties({visual = "sprite",textures = {tex}}) + end + + tdata = {}; + COOLING = 0.03 + +end + +-- generate_nuclear_power(CONTROL, COOLING) +-- CONTROL = 0.20; -- control rods; 1 = no control, between 0 and 1 +-- COOLING = 0.03; -- how much power assigned for cooling, in ratio of output power P; between 0 and 1 + + +E,P,T,FUEL,TV = generate_nuclear_power(0.2,COOLING) +-- cooling strategy +if TV < 0 then COOLING = 0.0 elseif T>90 then COOLING = 0.03 else COOLING = 0 end + +tdata[#tdata+1]=math.min(T/100,1); +render(tdata) + +self.label( "T " .. T .. "\nTV " .. TV .. "\nP ".. P .. "\nFUEL " .. FUEL .. "\nTOTAL ENERGY PRODUCED " .. E ) \ No newline at end of file diff --git a/scripts/simulators/redstone_emulator.lua b/scripts/simulators/redstone_emulator.lua new file mode 100644 index 0000000..eea67e2 --- /dev/null +++ b/scripts/simulators/redstone_emulator.lua @@ -0,0 +1,448 @@ +-- REDSTONE EMULATOR & EDITOR +--v 10/14a + +if not init then + local players = find_player(5); + if not players then + name = ""; + else + name = players[1] + player_ = puzzle.get_player(name) + local inv = player_:get_inventory(); + inv:set_stack("main", 8, puzzle.ItemStack("basic_robot:control 1 0 \"@\"")) -- add controller in players inventory + --add items for building + inv:set_stack("main", 1, puzzle.ItemStack("default:pick_diamond")) + inv:set_stack("main", 2, puzzle.ItemStack("basic_robot:button_273 999")) -- switch 9 = 273/274 + inv:set_stack("main", 3, puzzle.ItemStack("basic_robot:button_275 999")) -- button 7 = 275/276 + inv:set_stack("main", 4, puzzle.ItemStack("basic_robot:button_277 999")) -- equalizer 61 = 277 + inv:set_stack("main", 5, puzzle.ItemStack("basic_robot:button_278 999")) -- setter 15 = 278 + inv:set_stack("main", 6, puzzle.ItemStack("basic_robot:button_279 999")) -- piston 171 = 279 + inv:set_stack("main", 7, puzzle.ItemStack("basic_robot:button_282 999")) -- delayer 232 = 282 + inv:set_stack("main", 9, puzzle.ItemStack("basic_robot:button_281 999")) -- NOT 33 = 281 + inv:set_stack("main", 10, puzzle.ItemStack("basic_robot:button_280 999")) -- diode 175 = 280 + inv:set_stack("main", 11, puzzle.ItemStack("basic_robot:button_283 999")) -- platform 22 = 283 + + local round = math.floor; protector_position = function(pos) local r = 32;local ry = 2*r; return {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r}; end + local spawnblock = protector_position(self.spawnpos()) + + local meta = puzzle.get_meta(spawnblock); + meta:set_string("shares", name) -- add player to protection! + puzzle.chat_send_player(name,colorize("yellow","#EDITOR: if you need any blocks get them by using 'give me' in craft guide. you can now use controller to make links from pointed at blocks. In addition hold SHIFT to display infos. Reset block links by selecting block 2x")) + end + + init = true + self.spam(1) + self.label(colorize("orange","REDSTONE EMULATOR/EDITOR")) + + + + + -- 1. EMULATOR CODE + + + TTL = 16 -- signal propagates so many steps before dissipate + --self.label(colorize("red","REDSTONE")..colorize("yellow","EMULATOR")) + + + -- DEFINITIONS OF BLOCKS THAT CAN BE ACTIVATED + toggle_button_action = function(mode,pos,ttl) -- SIMPLE TOGGLE BUTTONS - SWITCH + if not ttl or ttl <=0 then return end + if mode == 1 then -- turn on + puzzle.set_node(pos,{name = "basic_robot:button_274"}) + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + else -- turn off + puzzle.set_node(pos,{name = "basic_robot:button_273"}) + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + + + button_action = function(mode,pos,ttl) -- SIMPLE ON BUTTON, TOGGLES BACK OFF after 1s + if not ttl or ttl <=0 then return end + if mode == 0 then return end + puzzle.set_node(pos,{name = "basic_robot:button_276"}) + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + + minetest.after(1, function() + puzzle.set_node(pos,{name = "basic_robot:button_275"}) + for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end) + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + + equalizer_action = function(mode,pos,ttl) -- CHECK NODES AT TARGET1,TARGET2. IF EQUAL ACTIVATE TARGET3,TARGET4,... + if not ttl or ttl <=0 then return end + if mode == 0 then return end + + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}).name + local node2 = puzzle.get_node({x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}).name + + + if node1==node2 then + for i = 3,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + else + for i = 3,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + + delayer_action = function(mode,pos,ttl) -- DELAY FORWARD SIGNAL, delay determined by distance of target1 from delayer ( in seconds) + if not ttl or ttl <=0 then return end + + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local pos1 = {x=meta:get_int("x1"),y=meta:get_int("y1"),z=meta:get_int("z1")} + local delay = math.sqrt(pos1.x^2+pos1.y^2+pos1.z^2); + + if delay > 0 then + minetest.after(delay, function() + if mode == 1 then + for i = 2,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + else + for i = 2,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end) + end + end + + diode_action = function(mode,pos,ttl) -- ONLY pass through ON signal + if not ttl or ttl <=0 then return end + if mode ~= 1 then return end + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + + not_action = function(mode,pos,ttl) -- negate signal: 0 <-> 1 + if not ttl or ttl <=0 then return end + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + for i = 1,n do activate(1-mode,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + + setter_action = function(mode,pos,ttl) -- SETS NODES IN TARGET AREA TO PRESELECTED NODE + if not ttl or ttl <=0 then return end + if mode == 0 then return end + + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + if n ~= 3 then say("#setter: error, needs to be set with 3 links"); return end + local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}) + local pos1 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} + local pos2 = {x=meta:get_int("x3")+pos.x,y=meta:get_int("y3")+pos.y,z=meta:get_int("z3")+pos.z} + + if pos1.x>pos2.x then pos1.x,pos2.x = pos2.x,pos1.x end + if pos1.y>pos2.y then pos1.y,pos2.y = pos2.y,pos1.y end + if pos1.z>pos2.z then pos1.z,pos2.z = pos2.z,pos1.z end + + local size = (pos2.x-pos1.x+1)*(pos2.y-pos1.y+1)*(pos2.z-pos1.z+1) + if size > 27 then say("#setter: target area too large, more than 27 blocks!"); return end + for x = pos1.x,pos2.x do + for y = pos1.y,pos2.y do + for z = pos1.z,pos2.z do + puzzle.set_node({x=x,y=y,z=z},node1) + end + end + end + end + + local piston_displaceable_nodes = {["air"] = 1,["default:water_flowing"] = 1} + + piston_action = function(mode,pos,ttl) -- PUSH NODE AT TARGET1 AWAY FROM PISTON + if not ttl or ttl <=0 then return end + --if mode == 0 then return end + + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + if n < 1 or n>2 then say("#piston: error, needs to be set with at least link and most two"); return end + local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} + + -- determine direction + local dir = {x=pos1.x-pos.x, y= pos1.y-pos.y, z= pos1.z-pos.z}; + + local dirabs = {x=math.abs(dir.x), y= math.abs(dir.y), z= math.abs(dir.z)}; + local dirmax = math.max(dirabs.x,dirabs.y,dirabs.z); + + if dirabs.x == dirmax then dir = { x = dir.x>0 and 1 or -1, y = 0,z = 0 } + elseif dirabs.y == dirmax then dir = { x = 0, y = dir.y>0 and 1 or -1, z=0} + else dir = {x = 0, y = 0, z = dir.z>0 and 1 or -1} + end + + local pos2 = {x=pos1.x+dir.x,y=pos1.y+dir.y,z=pos1.z+dir.z}; + + if mode == 0 then pos1,pos2 = pos2,pos1 end + + local node1 = puzzle.get_node(pos1) + if node1.name == "air" then return end + + + if piston_displaceable_nodes[puzzle.get_node(pos2).name] then + puzzle.set_node(pos2, node1) + puzzle.set_node(pos1, {name = "air"}) + minetest.check_for_falling(pos2) + self.sound("doors_door_open",1,pos) + end + end + + platform_action = function(mode,pos,ttl) -- SPAWN MOVING PLATFORM + + if mode~=1 then return end + local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + if n ~= 2 then say("#platform: error, needs to be set with 2 targets"); return end + local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} + local pos2 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} + + -- determine direction + local dir = {x=pos2.x-pos1.x, y= pos2.y-pos1.y, z= pos2.z-pos1.z}; + + local obj = minetest.add_entity(pos1, "basic_robot:projectile"); + + if not obj then return end + obj:setvelocity(dir); + --obj:setacceleration({x=0,y=-gravity,z=0}); + local luaent = obj:get_luaentity(); + luaent.name = name; + luaent.spawnpos = pos1; + + local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; + local tiles = minetest.registered_nodes[nodename].tiles; tiles = tiles or {}; + local texture = tiles[1] or "default_stone"; + obj:set_properties({visual = "cube",textures = {texture,texture,texture,texture,texture,texture},visual_size = {x=1,y=1}, + collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}}) + end + + + -- HOW TO ACTIVATE TARGET ELEMENT - adds mesecons/basic machines compatibility + activate = function(mode, pos, ttl) + if not ttl or ttl <=0 then return end + local nodename = puzzle.get_node(pos).name; + local active_element = active_elements[nodename]; + if active_element then + active_element(mode,pos,ttl-1) + else -- try mesecons activate + local nodename = puzzle.get_node(pos).name + local table = minetest.registered_nodes[nodename]; + if table and table.mesecons then else return end + + local effector=table.mesecons.effector; + + if mode == 1 then + if effector.action_on then + effector.action_on(pos,node,ttl) + end + else + if effector.action_off then + effector.action_off(pos,node,ttl) + end + end + end + end + + -- THESE REACT WHEN PUNCHED + interactive_elements = { + [275] = {button_action,1}, -- BUTTON, 1 means it activates(ON) on punch + [273] = {toggle_button_action,1}, -- TOGGLE BUTTON_OFF + [274] = {toggle_button_action,0} -- TOGGLE BUTTON_ON, 0 means it deactivates + --TODO + -- inventory checker(taker) : basic_robot:button_63 -> if player has stuff it will activate items after punch + -- inventory give: give item that is at position1 and activate selected machine at position2 + } + + -- THESE CAN BE ACTIVATED WITH SIGNAL + active_elements = { + ["basic_robot:button_275"] = button_action, -- BUTTON, what action to do on activate + ["basic_robot:button_273"] = toggle_button_action, -- TOGGLE BUTTON_OFF + ["basic_robot:button_274"] = toggle_button_action, -- TOGGLE BUTTON_ON + ["basic_robot:button_278"] = setter_action, -- SETTER + ["basic_robot:button_277"] = equalizer_action, -- EQUALIZER + ["basic_robot:button_279"] = piston_action, -- PISTON + ["basic_robot:button_283"] = platform_action, -- PLATFORM + ["basic_robot:button_282"] = delayer_action, -- DELAYER + ["basic_robot:button_280"] = diode_action, -- DIODE + ["basic_robot:button_281"] = not_action, -- NOT + } + + + -- EDITOR CODE -- + + edit = {}; + edit.state = 1; -- tool state + edit.source = {}; edit.sourcenode = ""; -- selected source + + -- blocks that can be activated + edit.active_elements = { + ["basic_robot:button_275"] = "button: now select one or more targets", -- button + ["basic_robot:button_273"] = "switch: now select one or more targets", -- switch OFF + ["basic_robot:button_274"] = "switch: now select one or more targets", -- switch ON + ["basic_robot:button_278"] = "setter: set block at target region {target2,target3} to block at target1", -- equalizer + ["basic_robot:button_277"] = "equalizer: target1 and target2 are for comparison, other targets are activated", -- equalizer + ["basic_robot:button_279"] = "piston: push block at target1 in direction away from piston", -- equalizer + ["basic_robot:button_283"] = "platform: select target1 to set origin, target2 for direction", -- PLATFORM + ["basic_robot:button_282"] = "delayer: distance from delayer to target1 determines delay", -- delayer + ["basic_robot:button_280"] = "diode: only pass through ON signal", -- DIODE + ["basic_robot:button_281"] = "NOT gate: negates the signal", -- NOT + } + + linker_use = function(pos) + if not pos then return end + + --say(serialize(player_:get_player_control())) + if edit.state < 0 then -- link edit mode! + local meta = puzzle.get_meta(edit.source); + local i = -edit.state; + meta:set_int("x" ..i, pos.x-edit.source.x); meta:set_int("y" ..i, pos.y-edit.source.y); meta:set_int("z" ..i, pos.z-edit.source.z) + puzzle.chat_send_player(name, colorize("red", "EDIT ".. " target " .. i .. " changed")) + edit.state = 1 + goto display_particle + end + + if player_:get_player_control().sneak then -- SHOW LINKS + local meta = puzzle.get_meta(pos); + local n = meta:get_int("n"); + local nodename = puzzle.get_node(pos).name; + local active_element = edit.active_elements[nodename] + if active_element and edit.source.x == pos.x and edit.source.y == pos.y and edit.source.z == pos.z then -- gui with more info + local form = "size[4,"..(0.75*n).."] label[0,-0.25; "..active_element .."]" ; + for i = 1,n do -- add targets as lines + form = form .. + "button[0,".. (0.75*i-0.5) .. ";1,1;".."S"..i..";" .. "show " .. i .. "]".. + "button_exit[1,".. (0.75*i-0.5) .. ";1,1;".."E"..i..";" .. "edit " .. "]" .. + "button_exit[2,".. (0.75*i-0.5) .. ";1,1;".."D"..i..";" .. "delete " .. "]".. + "label[3,".. (0.75*i-0.25) .. "; " .. meta:get_int("x"..i) .. " " .. meta:get_int("y"..i) .. " " .. meta:get_int("z"..i) .."]" + end + self.show_form(name,form); + edit.state = 3; + return + end + edit.source = {x=pos.x,y=pos.y, z=pos.z}; + edit.state = 1 + if not active_element then return end + local i = string.find(active_element,":"); + if not i then return end + puzzle.chat_send_player(name,colorize("red","#INFO ".. string.sub(active_element,1,i-1) ..":") .." has " .. n .. " targets. Select again for more info.") + meta:set_string("infotext",string.sub(active_element,1,i-1)) -- write name of element on it! + + for i = 1, n do + minetest.add_particle( + { + pos = {x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z}, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "010.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + end + return + end + + if edit.state == 1 then -- SET SOURCE + local nodename = puzzle.get_node(pos).name; + local active_element = edit.active_elements[nodename] + if not active_element then puzzle.chat_send_player(name,colorize("red","#ERROR linker:").. " source must be valid element like switch"); return end + edit.source = {x=pos.x,y=pos.y, z=pos.z}; + sourcenode = nodename; + puzzle.chat_send_player(name, colorize("yellow","SETUP " ..edit.state .. ": ").. active_element) + edit.state = 2 + else -- SET TARGET + local meta = puzzle.get_meta(edit.source); + local n = meta:get_int("n"); + + if edit.state == 2 and pos.x == edit.source.x and pos.y == edit.source.y and pos.z == edit.source.z then -- RESET LINK FOR SOURCE + local meta = puzzle.get_meta(pos);meta:set_int("n",0) -- reset links + puzzle.chat_send_player(name, colorize("red", "SETUP " .. edit.state .. ":") .. " resetted links for selected source.") + edit.state = 1;return + else + n=n+1; + meta:set_int("x"..n, pos.x-edit.source.x);meta:set_int("y"..n, pos.y-edit.source.y);meta:set_int("z"..n, pos.z-edit.source.z) -- relative to source! + meta:set_int("n",n) + puzzle.chat_send_player(name, colorize("red", "SETUP "..edit.state .. ":") .. " added target #" .. n) + edit.state = 1 + end + end + + -- display + ::display_particle:: + + minetest.add_particle( + { + pos = pos, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "009.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + end + + tools = { + ["basic_robot:control"] = linker_use + } + + ------ END OF EDIT PROGRAM + +end + + +event = keyboard.get() -- handle keyboard +if event then + if event.type == 0 then -- EDITING + if event.puncher == name then -- players in protection can edit -- not minetest.is_protected({x=event.x,y=event.y,z=event.z},event.puncher) + local wield_item = player_:get_wielded_item():get_name() + local tool = tools[wield_item] + if tool then tool({x=event.x,y=event.y,z=event.z}) end + end + else -- EMULATOR + local typ = event.type; + local interactive_element = interactive_elements[typ] + if interactive_element then + interactive_element[1](interactive_element[2],{x=event.x,y=event.y,z=event.z},TTL) + self.sound("doors_glass_door_open",1,{x=event.x,y=event.y,z=event.z}) + end + end +end + + +sender,fields = self.read_form() -- handle gui for editing +if sender then + edit.state = 1 + for k,_ in pairs(fields) do + local c = string.sub(k,1,1); + local i = tonumber(string.sub(k,2)) or 1; + if c == "S" then + + local meta = puzzle.get_meta(edit.source); + minetest.add_particle( + { + pos = {x=meta:get_int("x"..i)+edit.source.x,y=meta:get_int("y"..i)+edit.source.y,z=meta:get_int("z"..i)+edit.source.z}, + expirationtime = 5, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "010.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + elseif c == "E" then + edit.state = -i; + puzzle.chat_send_player(name, colorize("yellow", "#EDIT: select target " .. i)); + elseif c == "D" then + local meta = puzzle.get_meta(edit.source); + local n = meta:get_int("n") + if n > 0 then + for j = i,n-1 do + meta:set_int("x"..j, meta:get_int("x"..(j+1))) + meta:set_int("y"..j, meta:get_int("y"..(j+1))) + meta:set_int("z"..j, meta:get_int("z"..(j+1))) + end + meta:set_int("n",n-1) + end + puzzle.chat_send_player(name, colorize("red", "#EDIT: target " .. i .. " deleted")); + end + --say(serialize(fields)) + end +end \ No newline at end of file diff --git a/scripts/simulators/trust_game.lua b/scripts/simulators/trust_game.lua new file mode 100644 index 0000000..c11b0e3 --- /dev/null +++ b/scripts/simulators/trust_game.lua @@ -0,0 +1,266 @@ +-- cheat trust game by rnd, made in 60 minutes +-- http://ncase.me/trust/ + +--[[ + TO DO: + hierarchies, they determine who plays with who (boss plays only with his direct underlings) + fakers: they give fake +points +--]] + + +if not init then + init = true + if not find_player(6) then error("#TRUST GAME: no players near") end + + rules = { + { + {2., 2.}, -- first player cooperate, second player cooperate: +2,+2 + {-1, 3}, -- first player cooperate, second player cheat: -1,+3 + }, + { + {3,-1}, -- first player cheats, second player cooperate + {0,0}, -- first player cheats, second player cheat + } + }; + + error_rate = 0.0; -- 10% probability we take wrong decision + + grudger = + { + name = "grudger", + opponent = {}, -- list of opponent moves from previous games, + state = 1, -- internal state, for grudger it means how he plays next move + reset = function(self) + self.state = 1; self.opponent = {}; + end, + game = function(self) -- how will we play next move + if self.state == 1 then + local opp = self.opponent; + if #opp>0 and opp[#opp] == 2 then self.state = 2 end -- you cheat me once and we are done, pardner. + end + return self.state + end, + err = error_rate, -- probability we make different decision + } + + cheater = + { + name = "cheater", + opponent = {}, -- list of opponent moves from previous games, + reset = function(self) + self.opponent = {}; + end, + game = function(self) + return 2 + end, + err = error_rate, -- probability we make different decision + } + + cooperator = + { + name = "cooperator", + opponent = {}, + reset = function(self) + self.opponent = {}; + end, + game = function(self) + return 1 + end, + err = error_rate, -- probability we make different decision + } + + copycat = + { + name = "copycat", + opponent = {}, + reset = function(self) + self.opponent = {}; + end, + game = function(self) + local opp = self.opponent; + if #opp>0 then return opp[#opp] else return 1 end -- i do to you what you did to me last move + end, + err = error_rate, -- probability we make different decision + } + + fcopycat = + { + name = "forgiving copycat", + opponent = {}, + reset = function(self) + self.opponent = {}; self.state = 0; self.cheat = 0; + end, + state = 0, + cheat = 0, + game = function(self) + local opp = self.opponent; + if #opp>0 then + if opp[#opp] == 2 then -- you cheat me + if self.state == 1 then self.cheat = self.cheat+ 1 else self.state = 1 end -- cheat in a row + else + self.state = 0 + self.cheat = 0 + end + if self.cheat >= 1 then -- fk you + return 2 + else -- you cheated me less than 2x, its still cool + return 1 + end + else -- first time + return 1 + end + end, + err = error_rate, -- probability we make different decision + } + + + detective = + { + name = "detective", + opponent = {}, + moves = {1,2,1,1}, -- starting 4 moves + step = 0, -- what move we played so far + state = 0, + reset = function(self) + self.state = 0; self.step = 0; self.opponent = {}; + end, + game = function(self) -- how will we play next move + local st = self.step+1; + self.step = st; + local opp = self.opponent; + + if st < 5 then + if self.state == 0 and opp[#opp] == 2 then self.state = 1 end -- i caught you cheating! + return self.moves[st] + end + + if self.state == 0 then -- exploiter + return 2 + else -- copycat + if #opp>0 then return opp[#opp] end -- copycat + end + return self.state + end, + err = error_rate, -- probability we make different decision + } + + -- internal functions + + Player = {} + function Player:new (o) + ret = {}; _G.setmetatable(ret, self); self.__index = self + for k,v in pairs(o) do ret[k] = v end + ret.points = 0 + return ret + end + + gamestep = function(player1,player2) + local res1 = player1:game(); if math.random(1000)<=1000*player1.err then res1 = 3-res1 end + local opponent = player2.opponent; + opponent[#opponent+1] = res1; -- player2 remembers player1 move + local res2 = player2:game(); if math.random(1000)<=1000*player2.err then res2 = 3-res2 end + opponent = player1.opponent; + opponent[#opponent+1] = res2; -- player1 remembers player2 move + local res = rules[res1][res2]; + player1.points = player1.points + res[1]; + player2.points = player2.points + res[2]; + return res1,res2 -- return what players did + end + + paired_match = function(player1,player2, rounds) + player1:reset();player2:reset() + for i = 1, rounds do + gamestep(player1,player2) + end + end + + sort_players = function(players) + table.sort(players, + function(p1,p2) return p1.points " .. players[1].points .. " VS " .. players[2].name .. " " .. res2 .. " -> " .. players[2].points); + -- end + +end \ No newline at end of file diff --git a/scripts/simulators/turtlebot.lua b/scripts/simulators/turtlebot.lua new file mode 100644 index 0000000..c486582 --- /dev/null +++ b/scripts/simulators/turtlebot.lua @@ -0,0 +1,28 @@ +if not cmd then + cmd = { + ["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, + ["a"] = function() activate.forward(1) end, + ["<"] = function() turn.left() end, + [">"] = function() turn.right() end, + } + i=0; + prog = read_text.right(); s=0 + prog = string.gsub(prog,"%s",""); + --say(prog) + self.label("RUNNING PROGRAM: " .. prog);n=string.len(prog); + if string.sub(prog,1,1) == " " then self.label("WRITE A PROGRAM FIRST!") s=1 end + +end + +if s == 0 then + i=i+1; if i > n then self.label("PROGRAM ENDED");s=1 end; + if s == 0 then + c=string.sub(prog,i,i) + if cmd[c] then cmd[c]() else self.label("INVALID PROGRAM INSTRUCTION : " .. c) s=1 end + end +end \ No newline at end of file diff --git a/scripts/spawn_quiz.lua b/scripts/spawn_quiz.lua new file mode 100644 index 0000000..b3f97e3 --- /dev/null +++ b/scripts/spawn_quiz.lua @@ -0,0 +1,62 @@ +if not s then + s=0 + t=0 + option = {"A","B","C","D","E"} + generate_question = function() + local a = math.random(10)+0; + local b = math.random(10)+0; + local c = math.random(20)-10; + local d = a*b+c; + msg = "To get out solve the math problem\n"; + msg = msg .. colorize("LawnGreen",a.." * "..b.." + "..c .. " = ?\n\n") + problem = a.."*"..b.."+"..c .. " = ?"; + correct = math.random(5); + local frm = ""; + + for i =1,5 do + local offset = 0; + if i~=correct then offset = math.random(10)-5; if offset == 0 then offset = -1 end end + frm = frm .. "button_exit[".. -0.1+(i-1)*1.25 ..",0.75;1.25,1;" .. i .. ";".. d + offset .. "]" + end + + local form = "size[6,1.25]" .. "label[0.05,-0.3;".. msg.."] "..frm .. "button_exit[4.9,-0.25;1.2,1;cancel;cancel]"; + return form, correct + end + + selection = 1; + question = ""; + problem = ""; +end + + +if t%4 == 0 then + t = 0; form,selection = generate_question(); + for _,obj in pairs(_G.minetest.get_objects_inside_radius({x=2,y=2,z=0}, 1)) do + if obj:is_player() then + local pname = obj:get_player_name(); + self.show_form(pname,form) + end + end +end +t=t+1; + + +sender,fields = self.read_form() +if sender then + player = _G.minetest.get_player_by_name(sender); + if player then + + answer = 0; + for i = 1,5 do if fields[_G.tostring(i)] then answer = i end end + + if answer == correct then + player:setpos({x=0,y=2,z=3}) + --inv = player:get_inventory(); inv:add_item("main", "default:apple") + --_G.minetest.chat_send_player(sender," congratulations, here is an apple.") + elseif answer ~= 0 then + player:setpos({x=0,y=-6,z=-1}) + say(sender .. " failed to solve the problem " .. problem) + self.show_form(sender, "size[1.25,0.5] label[0,0; WRONG]") + end + end +end \ No newline at end of file diff --git a/scripts/tree_harvest.lua b/scripts/tree_harvest.lua new file mode 100644 index 0000000..242bf2f --- /dev/null +++ b/scripts/tree_harvest.lua @@ -0,0 +1,112 @@ +-- rnd, 2017 +if not tree then + tree = {}; + wood = "default:tree"; + support = "default:tree"; + leaves = "default:leaves"; + sapling = "default:sapling"; + + tree.s = 0 -- searching + tree.st = 0 +end + +tree_step = function() + + if tree.s == 0 then -- search + node = read_node.forward() + if node == wood then tree.s = 1 end + elseif tree.s==1 then -- found + dig.forward(); + move.forward(); + tree.s=2 + elseif tree.s==2 then -- dig up + node = read_node.up() + dig.up() + move.up() + place.down(support) + if node ~= wood then + tree.s=3 tree.st = 0 + end + elseif tree.s==3 then -- on top + if tree.st == 0 then + move.up(); + turn.right();place.forward_down(support); + dig.forward() + turn.angle(180); place.forward_down(support); + tree.st=1 + elseif tree.st == 1 then + dig.forward(); move.forward(); + tree.st=2 + elseif tree.st == 2 then + turn.left(); dig.forward(); turn.angle(180) + tree.st=3 + elseif tree.st == 3 then + dig.forward(); turn.right(); tree.st = 4 + elseif tree.st == 4 then + dig.down();move.forward(); move.forward(); tree.st = 5 + elseif tree.st == 5 then + turn.left(); dig.forward(); turn.angle(180) + tree.st=6 + elseif tree.st == 6 then + dig.forward(); turn.right(); tree.st = 7 + elseif tree.st == 7 then + dig.down();move.forward(); + turn.right(); + tree.st = 0; tree.s = 4 + end + elseif tree.s == 4 then -- going down + node = read_node.down() + if node == wood then + dig.down(); + move.down(); + else + pickup(8); move.forward(); + place.backward(sapling) + tree.s=5 + end + end + +end + + + + +-- walk around +if not s then +wall = "basic_robot:buttonFFFFFF"; +s=0 +if rom.tree and rom.s then + tree.s = rom.tree.s; tree.st = rom.tree.st + s = rom.s; + rom.s = nil; + else + rom.tree = {} + end; + +angle = 90 +end + +if s==0 then -- walk + + if not move.forward() then + node = read_node.forward(); + if node == wood then + s = 1 + else + turn.angle(angle); node = read_node.forward() + if node == wall then + turn.angle(180);move.forward();turn.angle(-angle) + else + move.forward(); turn.angle(angle)angle=-angle; + end + end + end +elseif s==1 then + tree_step(); + if tree.s == 5 then s = 0; tree.s = 0 end +end + +rom.s = s;rom.tree.s = tree.s; rom.tree.st = tree.st -- remember whats it doing + + +--self.label(s .. " " .. tree.s .. " " .. tree.st) \ No newline at end of file diff --git a/scripts/utils/chatlog.lua b/scripts/utils/chatlog.lua new file mode 100644 index 0000000..ee2eaa4 --- /dev/null +++ b/scripts/utils/chatlog.lua @@ -0,0 +1,46 @@ +--rnd 2017 +if not logdata then + self.label("chatlog bot"); + _G.minetest.forceload_block(self.pos(),true) + n = 250; + idx = 1; + logdata = {}; + + insert = function(text) -- insert new message + idx = idx +1; + if idx > n then idx = 1 end + logdata[idx] = text; + end + + last = function(k,filter) -- return last k messages + if k > n then k = 30 end + local i,j,ret; + i=idx;j=0; ret = "" + + for j = 1,k do + if not logdata[i] then break end + if filter and not string.find(logdata[i], filter) then + else + ret = ret .. logdata[i] .. "\n"; + end + i=i-1; if i < 1 then i = n end + end + return ret + end + + self.listen(1) +end + +speaker, msg = self.listen_msg() +if msg then + if string.sub(msg,1,4) == "?log" then + local j = string.find(msg," ",6); + local k = tonumber(string.sub(msg,6) or "") or n; + local text; + if j then text = last(k,string.sub(msg,j+1)) else text = last(k) end + local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. text .. "]" + self.show_form(speaker, form) + else + insert(os.date("%X") .. " " .. speaker .. "> " .. msg) + end +end \ No newline at end of file diff --git a/scripts/utils/language translator.lua b/scripts/utils/language translator.lua new file mode 100644 index 0000000..626e0e4 --- /dev/null +++ b/scripts/utils/language translator.lua @@ -0,0 +1,123 @@ +if not dict then + lang = "german" + dict = {}; + 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 + + while(step c or a b -> d, where data for a = {{[de] = c},{[b] = d} + local found = false + local defaultv = "" + + + for j=1,#data do + + for key,v in pairs(data[j]) do + local keylen = string.len(key) + local pattern = string.sub(input,i1+1,i1+keylen) + if deb then say("pattern '" .. pattern .. "' key '" .. key .. "' len " .. keylen) end + if key == "" then defaultv = v + elseif pattern == key then + found = true; + if deb then say(word .. " " .. pattern .. " -> match key " .. key) end + out = out .. " " .. v; i1 = i1+string.len(key)+1 -- skip to after key + end + end + if found then break end + end + + if not found then out = out .. " " .. defaultv end + + end + else + out = out .. " " .. word + end + i=i1 + if deb then say("next word at i " .. i1 .. " remainder " .. string.sub(input,i1)) end + end + + return out + end + + -- say(translate("hello world")) + self.listen(1) +end + + + +speaker,msg = self.listen_msg() +if msg then + if string.sub(msg,1,1) == "?" then + msg = string.sub(msg,2) + local transmsg = translate(msg); + _G.minetest.chat_send_all("TRANSLATOR> " .. transmsg) + end +end \ No newline at end of file diff --git a/scripts/utils/serverbot.lua b/scripts/utils/serverbot.lua new file mode 100644 index 0000000..28c4981 --- /dev/null +++ b/scripts/utils/serverbot.lua @@ -0,0 +1,76 @@ +--SERVER ROBOT : can send various data to other robots that requested it +if not cmds then + +-- user auth data +auth = {["rnd1"]=2}; + +-- server commands +cmds = { + list = { + run = function() + local ret = ""; for i,_ in pairs(cmds) do ret = ret .. " " .. i end; return ret + end, + help = "list all commands", + level = 0 + }, + + help = { + run = function(words) + local arg = words[2]; + if not arg then return "help: missing argument" end + local cmd = cmds[arg]; + if not cmd then return "help: nonexistent command" end + return cmd.help or "" + end, + help = "display help for command", + level = 0 + }, + + chat = { + run = function(words) + words[1] = "";_G.minetest.chat_send_all("#server bot : " .. table.concat(words," ") or ""); return true; + end, + help = "prints text globally", + level = 2 + }, + + minetest = { + run = function() return minetest end, + help = "returns minetest namespace", + level = 3 + } + +}; + +LISTENING = 0; --states +state = LISTENING; -- init +_G.minetest.forceload_block(self.pos(),true) +end + + + +if state == LISTENING then + sender,mail = self.read_mail() + if mail then + if type(mail)~="string" then mail = "" end + self.label("received request " .. mail); + local words = {}; + for word in string.gmatch(mail,"%S+") do words[#words+1]=word end -- get arguments + if not words or not words[1] then + self.send_mail(sender,"error: nil request") + else + local cmd = cmds[words[1]]; + if not cmd or not cmd.run then + self.send_mail(sender,"error: illegal command") + elseif (auth[sender] or 0) < cmd.level then + self.send_mail(sender,"error: auth level " .. (auth[sender] or 0) ..", need level " .. cmd.level) + else + self.send_mail(sender,cmd.run(words)); + self.label("sending data to " .. sender .. " ...") + end + end + else + self.label("listening...") + end + +end \ No newline at end of file diff --git a/scripts/utils/simple_house_builder.lua b/scripts/utils/simple_house_builder.lua new file mode 100644 index 0000000..78f38ca --- /dev/null +++ b/scripts/utils/simple_house_builder.lua @@ -0,0 +1,34 @@ +-- rnd 2017 +if not pos then + pos = self.spawnpos(); + n = 6; -- width + m = 4; -- height + door = math.floor(n/2)+1; -- door place + + plan = {}; + build_cube = function(x,y,z) + plan[#plan+1] = {c= math.random(10)+6, pos={x=pos.x+x,y=pos.y+y,z=pos.z+z}}; + end + + --floor + y=0;for z=1,n do for x=1,n do build_cube(x,y,z) end end --bottom + + z=1;for y=1,m do for x=1,n do build_cube(x,y,z) end end --wall 1 + z=n;for y=1,m do for x=1,n do build_cube(x,y,z) end end --wall2 + + x=n;for y=1,m do for z=2,n-1 do build_cube(x,y,z) end end -- wall3 + x=1;for y=1,m do for z=2,n-1 do if z~=door then build_cube(x,y,z) end end end -- wall4 + x=1;z=door;for y=3,m do build_cube(x,y,z) end -- door hole + + + y=m;for x = 2,n-1 do for z = 2,n-1 do build_cube(x,y,z) end end -- ceiling + s=0 + --self.remove() +end + +s=s+1; +if plan[s] then + keyboard.set(plan[s].pos,plan[s].c) +else + self.remove() +end \ No newline at end of file diff --git a/textures/face-back.png b/textures/face-back.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3db4d3a59f8730931de474ec2370826ee6f2e7 GIT binary patch literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPE^3h)VWJ$dq^mX?;TuCBVe`q{H* zj~_oSU-)ekP}bMe#W95AdhCgVybT6C&VlWE0p(3X2W78t?)o2Oak5uGvdZYrTtB@# zC!!j7d{!J;_KNpBD L)z4*}Q$iB}*~vS~ literal 0 HcmV?d00001 diff --git a/textures/face3.png b/textures/face3.png new file mode 100644 index 0000000000000000000000000000000000000000..e408585babaad5f6378adb225424dda1b0d28e8a GIT binary patch literal 4740 zcmeHLc{r8Z+g^LfJSAh&Au_WwcF4SqnZg#*fMP2NJCB0Klm}e02yeSpCeLF95*41DiOIc25WZHk;FA@}Wasz5%`$ynOw{%*kXi zzw^GHr_Xu-aA`Qho=UNw70_85Uoo_Z3AAucZLCcmu$$91+r<|*+9aS;z=uD{#(81m4&zk2O+2l8S-ccy4LqpXv; zmW3n9Ox0FBj3eK0_x2AF&5cbTr%lsPPF7>jr zfLg2c+I_pCQSPsXsYh{P*KoA~3BOyggz*9W;5fs_K;HxxmXRW553-2Bwe6()Bsio9 zT&1sk?ge2P>!~3);BrTtAD5K?#CCYZ7y+*Xps4fkbz|VP5Ab6d6+B{Z5wTshYX#jkIe z&^_+%lgM;up`Shg9|JCDO${DwG<0!$=Xe>R)}N?#q@Vo}s=c|k`r-8_$#9ip;ikm(5B2RUmN~mR zZ?j%=AN?F}x>>=FZi@EqX*74QbduwnwB_}WGfKO;tdg@Q*6_CM+r2&LsZSFM8wA}6 zb@23zE-oZNG4I$(!gmX49;blXPip`yRr%Jvmti4rlRL+(3USCwL*48U3(T_{1-r*&ejvx0=HqCpGG4ma`P7@YfcCy6)~ z!PLlg@j5di?+r<@))+q9Jr)V-X3BQgDPj(0qDwmG@tV;`O0XemG6X? z+UmULctNhr+rRUg$piMTQvu3p@d8=tUFVecoijY94!%F#|{3KIRi@jFO7Au;5}{gS+~WEJVk zTX^$lk0?F7JtjTp>?CFt^3&DKc1k3+4xjQaW!Ai-AST}uIh8ThFr_*rG9|v`!j|E# z@Be7s{*#Q|yE6xcr+KE;rkOlaR16#*rrC|eL+y_=&5j5G zbBmnf2X}jSdI@^1oqqBq;MMlAySuHEyBvGkW=+2&e<7>{a&SiRM>NM!WrWj(n1!gq zfkIDGEFTN^-Vko4?AxzAmok&eo$Bl0Bv&r?Cx5~_wz~)KO5GKaBT@1T9v6%kusbX} zv{B;okK11^F>z3}pUh`{pH%2jka1|vUc{c7e;_ZqfSTt^(R&o?Opj%9Zm?{yAwROs zdUj&6x=D=vq;T0K1#xfA!l*yO)f=UrbqxJ^QLZY5E?RX%ryxIDldO7B)v?TO`HrU^ z^JI$u`n>>a))|@Hs}Dl{v!Ei|30(I}bE&3Ig6 zT;;qG&SlViQ2MB}DK(z9En~)h$bKxBEmy!wM{Bi2slYo&B)iHe@Xfj0g_O+K$HOu$ zeRIcyYG!`!x=f-|=srJlS4}ybn94XLnMOEhLfUWD{e87XNzEgF+meqp=#z8Y?z$y^ zyp~Yet^KgiYPFDNXI1%F_VEGB1D4Gl?Hvy~ z`cu`@b(9BGX3}TU2ht}hT#sBjqEMb!{?fy|bn3`s=Y{g>bCE|hopYQVjvOm9Db6U~ z+h0{&mAgH+(mK()FteZ9@bvHU*7E!c;iuDsOd}=bKINC)sP2N)ink>V3yC#}mtS4} zi>|}W89l^RD0)OR^uqJ=d|HRK#?xr`7W6K^T}hfHQJp$@ItNuJ%XhTNHlEI~PwzFQ z3Q{XCL@dYLh_$%drj)F!lrGXV)>1QdFksdH*vH_W)r|DA^!oX{ktYip3#S*NNAI_* z-IvXDR=v>G&_e!TJKe3ve*f-1?}?&N8?sF}^~HWiU-x@bPJTuXbz0WDb$^5LvE=?u55{+|bUr%h#FGqjM_{2ULBA7yIIp!CXO{-st%oWI2;#JT} z(zlGU{{7Cs40z=71a9tK+7Y-njxDHOw4S<$)UDASGe|xvx7&KjOwKC!_P2y;^&rW& zsyPbNmDi;6Z&h1zT8^b_WeTKS6!THh(W+67xSepz->^X3DzTjUmHOH9Vq1|%WGV9Y z+>Hv-?z_yb-etFnwmB=Zl^Z_(LOCKcAje!v^>Flj(-HelGpR6BzGJ?aBS*q)u_hdx7(wV|QSij=VfKIxv6C#);%S@xtwARECHyf9&-Vz7fG>{wsqrqnD;d zqJ7`JNgciOJioB9@>Hj1kLT>?am7p*uB3wG&&gcpIERLxb50z7KEL|ic!ZSPAv01H zQBJOW_(uC>wMT9%t>b_m6t@{^*m&@=YJZD9Re_ku`B^>?*zeIqW_YjoY#*CP%% z2dh5MK93Fajj}bplGuXT^`s4-W5w1!{`1X*9s-?Nf~$O>@_nCZj}FByzF&UMl&kr- z*GR@Go+G zF77U`Y}$3Z`@fC{Z<>z$#(hss+d1gqGNtwO@a)yzMCwxK&5XMlmqV3)jJl3IC=Z!A zNt-ys?YsDjC*Wg`F?wU>K=lFI(WRFGOQj>zI~-p+rajR*;qx`%t2UC@YtvkHuPUvi z@~C&^LU7HC5YM$)k<9(Hv6Y&jzXj0#zD!M>v6|&z-?g`Es}llaq35OPw6?0DK%9+~0tmW=e(>12Zh)yJ7HJ+QiEj~5AEs8U0F7tO? zP0dew=jpSXx#Z4Mol=>C$OELUL{NYfy52W8Kq?VRwFk*4~l z-LG3OsVW~|IlM7n7dRRyy7+K{+h*!du`4pD*Np2;h(<#dv6FXmD_FU|^uX zzrU}q@BRDty}i9XJw4ss-CbQ>ot>RD8m*(FqrJVot*x!KwY8 zo6xS~01$)BjSMK4aDWfYp+u2QKqrDF6i=jYZW4$%!X^=d!Mne;D}>Mi@%vA}MBLm! z@I*Wg;2FD;UYy$ux;2{)DL;)nM1cE?`2o51&4nSFS z9QcNbCP0NmfP)W&jzeLWP%wdrup)R^g#CF8GYKf*!7U<05kQB$2#N=runCw3{--(w zvlMDU0j3v4@vyBexzJ`P0)^rr6{Zej#vq_Ma1ECX{Q!X}Fd4RpLa`_vqTpZvE7=bF zftJG%G#pmLB`>T(aVUWZb;2UR3xn7WO2m)>)*#%*-iQznmf~Rn#K2mH0tov_(eK21pqq63R9rXTNei&H(X&c0xZK^f&+F0 zC#DShfimF4N-)#lz&5doB{EX5WK0oEz+SO!#v4|LAz<5BJxsuWkY76B4&3@j0hS2w zC$^0RF(O7fV;f_}CSyPfEX-iUI{zmfJDsf)V)%v;VNw|> z*clLhVQf)g;jK^i78Zs_*s9(-EUXI#2}3(W6TB0@$e`6Mzv?l5Mo$(ze)wso5c)I$ztaD0e F0swMzFMt35 literal 0 HcmV?d00001 diff --git a/textures/legs.png b/textures/legs.png new file mode 100644 index 0000000000000000000000000000000000000000..86191a3c3776f72372688fd23c6f88a80df4c0c5 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPGa3-AeXJ$dq^wzjsqy1J&O=9x2R z&YwT8tE;Q8uYdXSWj#GT(_r-(K$QueE{-7_*OLX->xlc9h=e80IKaT>!^3Le#?&*B zDS3(k2bTd?n}MNEqJTg`3Qu8@2dlBuTnU~7Jz?o=j2wqKPcUfav3fQ#tTvu<@X?tC p42BaVk0qo87=@Xcd5Z|MFg)hqIkR3pz!qp1gQu&X%Q~loCICf7H_HG3 literal 0 HcmV?d00001 diff --git a/textures/right-hand.png b/textures/right-hand.png new file mode 100644 index 0000000000000000000000000000000000000000..f88947895d83691740326e01385e377466ccf307 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPE^4e$wZJ$dq^wzjs0hK8P=-kCFJ zjvYI8;=~C7dA9pN1)-iUjv*Y^ZBHHKZBSrfIp`|=UkAdBF0O*dxg#eikIRT(Gzl27gJ?r7^=Y8G7+lSW>jpp^a=IwsziW>j{ zW7$?XjMWmm8ewWv+c-Atfw8wG8#S+`c6=CXk`RiIhS?~VFK?91qUo5fE|R+=Hbgdk)nTzI5-u99CZ}-^i}+rD~+6Pc@LWkPo9T z(LQzTb3}V<+swR#W;m^&&jIghSHA0e0tm1js;Vl=-$?ZaAOZ&Hs6nG?R-@F(7$VcM zc7_8YEE3V^m-MGGbr>tq3`)>01e$t?uj$JO!bKtBva1_4vWdMX_Z)XG} zfc*nLHbia`;63gZs{=e#KzXmt9bJHx0<7-Ftx8}-97q}3x*LFJHK4JFnXU>@(*r4s z*r+oA84Mgd1Ox&=WCmc>TensHSG1CO5p~e1j4IVyK_%UAdunlCDqCBLQzAWv9HOiW zPDh+FH6@w@Gq_~J<(NKid;lOfiS3};jlF;##>$=^g#3p2-stAts~sI(n4cdp zYSp%P>a-2n$2DJUwB5V%BUE*lu=V-PtYEmz`EY&Y_UGmgn=sG(swnL#lEf1buR!0OUi5H)D zq4;hr%#8JGoIQ9`>vi5upG2rB!aO2odi{ytH1_m!PlGUb!~4DfT+k7ez;sq?v{M71 zlN*kDrp3M4#3|NDd9vx~L=)Yvqe7&%U~98Bvo@`3*vV^-XUcDDi$r%n<3Tw}e$wQT zYqao)PWEAxXtsXEsOH1;(}_B#Nu)lU2C3CSecXv}EEeH>2P=?3&5;qc&DUgrWQiB# zeILte!Ec--Zy;@X2g7S^z_YG)?TAv0gWl^Tl@1_tdpEjXU;06Km4(_T`j_b1V#O1Y zdiiu8UHzmp6WMdKK3;oqLhY_X-r~nH52mq1b#%K_<5x~9+14vk5^XKo&;JyoNp|Ua z&R4Mn6y zvoL>hvGEI`*T;UeayrXM$6yV_1cq1=k4rKK$D&&m1zF1+0DBaR!w5%UzT22<{VUEcsp3|<#!&^SJh$%iU_gAJ;cLckkaY03X%D@Q^4iCbm zr(gYEADc`$WtR5Qw!dph|7+S;icNodhG@2k_E?+Z zQFqwtN^!;B7>y^Pc6)J@b}c3?=IAFDxz8`o)wl7|xo}kni1B$c zltup)F5fEjynFQK4OCtF9#37oT4_n15?a@?4%%0&clryQ7c60D7O-)P1mKl^;lzr9+tmY(zGLRgN8chOW}!{X1AA(DHTJ+Gfd zTl(}^%4&K+%5nN0PStnvzTT=llI`RFTAFeB9m(SR38uO{jJAz_fng~ zoZy^Sn^)UoAxa@WA!E~J)3r=X)7ru_g(@a0ChgrHy7Rk-GUT(=q=#h|vlg?4v!-ht z&jp+l!x!UUxfxc?pDVOm!`EMpI;Uh;fVDn%zFMy$yW;dvT}55dv7%bDWV5oIAzaI| zfAR0}B{f{n7Dgz?EAd|V5GR~72d?H_Wy@M}LvqOLkiYlTs2E~K8OwOi@q}J~ag9~k zMtLf;=i!=0$h*zdB}p8%m$`RDb`F2M>rCsVY^$sReH;g__IkudY#i(iYEdin{F^Jd)io#AKa1?RjTH*FbcD93V^1V&aP#$;tUNyR^bvL(Gi%l!> zcE#<5rV2ifj(_e|@DUc%dy;xCF-DyY{W|oK%IS+!d`l?3a8yi`ZL~&$Pb~ICpLCkM zf8PZ9`n1kZom8EvES$8t(o=CGd6aso#AVen#n*DLHQFTH?LG`*v$2L(j87^_pehxv zUU@UUySfoGn>|Z67tE0^*>T<{Ft4Y1GpdNaM1n<3IaSjn*6hy*|EI-_DrUcXdi}Wn z=>*!qW}apozhs|6U+f6l0d>l3-2i18bpM~Edig-Xcd`Xy3$>BLCHLx07)&O!lylfK zZ}57_s3|u{N8C@k=c`@HXPS)1E#tnp-{>lLi>lgxcQw97@>B}dd(Y~7syoN;DL(x6h`7qq%rbKyUl46i}bv66PvFP(lyXLGBw#oei79p^}6yEP&*5wO4=` z1e$5WDF)@S8+U`tXQ&4LxWR+tk-V&(xt^()WtF*O?`;3t{DXSm(DGE!uKxHROb_K$ z?E;N9Fz+voE!`eS#;y0>%}&V<36=OU;W(a;4_w(7mp`LOC4!`TJd! z9PE;z+k-2^J$>nYd1>ou$#1=G7VIrIu5djaZ)aFqOC#UmPMS76z`2kO z@Z%+y@q6v?al;{#!C?%k2ab-8q#lqB_Ng BPUips literal 0 HcmV?d00001 From 1225ca7ecaeb8d5878b0089fd71fb790ca5a7731 Mon Sep 17 00:00:00 2001 From: rnd Date: Thu, 28 Jun 2018 17:38:46 +0200 Subject: [PATCH 03/39] puzzle: meta safety redstone emulator fix --- commands.lua | 4 +- init.lua | 12 +-- scripts/simulators/redstone_emulator.lua | 97 +++++++++++++++++++----- 3 files changed, 88 insertions(+), 25 deletions(-) diff --git a/commands.lua b/commands.lua index 8b80c8f..ac9b871 100644 --- a/commands.lua +++ b/commands.lua @@ -1320,7 +1320,9 @@ basic_robot.commands.puzzle = { if minetest.is_protected(pos,data.owner) then return end if not is_same_block(pos,spos) then return end if minetest.get_node(pos).name == "basic_robot:spawner" then return end - return minetest.get_meta(pos) + local meta = minetest.get_meta(pos); + if not meta then error("get_meta in puzzle returned nil"); return end + return meta end, get_gametime = function() return minetest.get_gametime() end, diff --git a/init.lua b/init.lua index 38d0ade..0adfbf2 100644 --- a/init.lua +++ b/init.lua @@ -7,7 +7,8 @@ basic_robot.call_limit = 48; -- how many execution calls per script run allowed basic_robot.entry_count = 2 -- how many robots ordinary player can have basic_robot.advanced_count = 16 -- how many robots player with robot privs can have basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches -basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications + +basic_robot.password = "randompassword"; -- IMPORTANT: change it before running mod, password used for authentifications basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses ["craft_guide:sign_wall"] = true, @@ -18,7 +19,7 @@ basic_robot.dig_require_energy = true; -- does robot require energy to dig? basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/02/06a"; +basic_robot.version = "2018/06/28a"; basic_robot.data = {}; -- stores all robot related data --[[ @@ -292,14 +293,15 @@ function getSandboxEnv (name) grab = function(target) return basic_robot.commands.grab(name,target) end, - say = function(text, owneronly) - if not basic_robot.data[name].quiet_mode and not owneronly then + say = function(text, pname) + if not basic_robot.data[name].quiet_mode and not pname then minetest.chat_send_all(" " .. text) if not basic_robot.data[name].allow_spam then basic_robot.data[name].quiet_mode=true end else - minetest.chat_send_player(basic_robot.data[name].owner," " .. text) + if not pname then pname = basic_robot.data[name].owner end + minetest.chat_send_player(pname," " .. text) -- send chat only to player pname end end, diff --git a/scripts/simulators/redstone_emulator.lua b/scripts/simulators/redstone_emulator.lua index eea67e2..7fc4447 100644 --- a/scripts/simulators/redstone_emulator.lua +++ b/scripts/simulators/redstone_emulator.lua @@ -1,5 +1,5 @@ -- REDSTONE EMULATOR & EDITOR ---v 10/14a +--v 06/28/2018a if not init then local players = find_player(5); @@ -21,6 +21,9 @@ if not init then inv:set_stack("main", 9, puzzle.ItemStack("basic_robot:button_281 999")) -- NOT 33 = 281 inv:set_stack("main", 10, puzzle.ItemStack("basic_robot:button_280 999")) -- diode 175 = 280 inv:set_stack("main", 11, puzzle.ItemStack("basic_robot:button_283 999")) -- platform 22 = 283 + inv:set_stack("main", 12, puzzle.ItemStack("basic_robot:button_284 999")) -- giver 23 150/284 + inv:set_stack("main", 13, puzzle.ItemStack("basic_robot:button_285 999")) -- checker 24 151/285 + local round = math.floor; protector_position = function(pos) local r = 32;local ry = 2*r; return {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r}; end local spawnblock = protector_position(self.spawnpos()) @@ -42,18 +45,22 @@ if not init then TTL = 16 -- signal propagates so many steps before dissipate --self.label(colorize("red","REDSTONE")..colorize("yellow","EMULATOR")) - + opcount = 0; -- DEFINITIONS OF BLOCKS THAT CAN BE ACTIVATED toggle_button_action = function(mode,pos,ttl) -- SIMPLE TOGGLE BUTTONS - SWITCH if not ttl or ttl <=0 then return end if mode == 1 then -- turn on puzzle.set_node(pos,{name = "basic_robot:button_274"}) - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end else -- turn off puzzle.set_node(pos,{name = "basic_robot:button_273"}) - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); for i = 1,n do activate(0,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end end end @@ -63,7 +70,9 @@ if not init then if not ttl or ttl <=0 then return end if mode == 0 then return end puzzle.set_node(pos,{name = "basic_robot:button_276"}) - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); minetest.after(1, function() puzzle.set_node(pos,{name = "basic_robot:button_275"}) @@ -72,11 +81,44 @@ if not init then for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end end + giver_action = function(mode,pos,ttl) -- GIVER: give block below it to player and activate targets + local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; + if nodename == "air" then return end + local objects = minetest.get_objects_inside_radius(pos, 5);local player1; + for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end + if player1 then + player1:get_inventory():add_item("main", puzzle.ItemStack(nodename)) + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + + checker_action = function(mode,pos,ttl) -- CHECKER: check if player has block below it, then remove block from player and activate targets + local nodename = puzzle.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; + if nodename == air then return end + local objects = minetest.get_objects_inside_radius(pos, 5);local player1; + for _,obj in pairs(objects) do if obj:is_player() then player1 = obj; break end end + if player1 then + local inv = player1:get_inventory(); + if inv:contains_item("main", puzzle.ItemStack(nodename)) then + inv:remove_item("main",puzzle.ItemStack(nodename)) + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); + for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end + end + end + end + equalizer_action = function(mode,pos,ttl) -- CHECK NODES AT TARGET1,TARGET2. IF EQUAL ACTIVATE TARGET3,TARGET4,... if not ttl or ttl <=0 then return end if mode == 0 then return end - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}).name local node2 = puzzle.get_node({x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z}).name @@ -90,8 +132,10 @@ if not init then delayer_action = function(mode,pos,ttl) -- DELAY FORWARD SIGNAL, delay determined by distance of target1 from delayer ( in seconds) if not ttl or ttl <=0 then return end - - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + + local n = meta:get_int("n"); local pos1 = {x=meta:get_int("x1"),y=meta:get_int("y1"),z=meta:get_int("z1")} local delay = math.sqrt(pos1.x^2+pos1.y^2+pos1.z^2); @@ -109,13 +153,17 @@ if not init then diode_action = function(mode,pos,ttl) -- ONLY pass through ON signal if not ttl or ttl <=0 then return end if mode ~= 1 then return end - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); for i = 1,n do activate(1,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end end not_action = function(mode,pos,ttl) -- negate signal: 0 <-> 1 if not ttl or ttl <=0 then return end - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); for i = 1,n do activate(1-mode,{x=meta:get_int("x"..i)+pos.x,y=meta:get_int("y"..i)+pos.y,z=meta:get_int("z"..i)+pos.z},ttl) end end @@ -123,7 +171,9 @@ if not init then if not ttl or ttl <=0 then return end if mode == 0 then return end - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); if n ~= 3 then say("#setter: error, needs to be set with 3 links"); return end local node1 = puzzle.get_node({x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z}) local pos1 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} @@ -150,7 +200,9 @@ if not init then if not ttl or ttl <=0 then return end --if mode == 0 then return end - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); if n < 1 or n>2 then say("#piston: error, needs to be set with at least link and most two"); return end local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} @@ -184,7 +236,9 @@ if not init then platform_action = function(mode,pos,ttl) -- SPAWN MOVING PLATFORM if mode~=1 then return end - local meta = puzzle.get_meta(pos); local n = meta:get_int("n"); + local meta = puzzle.get_meta(pos); + if not meta then return end + local n = meta:get_int("n"); if n ~= 2 then say("#platform: error, needs to be set with 2 targets"); return end local pos1 = {x=meta:get_int("x1")+pos.x,y=meta:get_int("y1")+pos.y,z=meta:get_int("z1")+pos.z} local pos2 = {x=meta:get_int("x2")+pos.x,y=meta:get_int("y2")+pos.y,z=meta:get_int("z2")+pos.z} @@ -214,6 +268,9 @@ if not init then if not ttl or ttl <=0 then return end local nodename = puzzle.get_node(pos).name; local active_element = active_elements[nodename]; + opcount = opcount + 1 + if opcount > 64 then say("#puzzle error: opcount 64 exceeded. too many active connections."); error("#puzzle: abort") end + if active_element then active_element(mode,pos,ttl-1) else -- try mesecons activate @@ -239,10 +296,9 @@ if not init then interactive_elements = { [275] = {button_action,1}, -- BUTTON, 1 means it activates(ON) on punch [273] = {toggle_button_action,1}, -- TOGGLE BUTTON_OFF - [274] = {toggle_button_action,0} -- TOGGLE BUTTON_ON, 0 means it deactivates - --TODO - -- inventory checker(taker) : basic_robot:button_63 -> if player has stuff it will activate items after punch - -- inventory give: give item that is at position1 and activate selected machine at position2 + [274] = {toggle_button_action,0}, -- TOGGLE BUTTON_ON, 0 means it deactivates + [284] = {giver_action,0}, -- GIVER: give player item below it when activated and activate targets after that + [285] = {checker_action,0}, -- CHECKER: check if player has block below it in inventory, remove it and activate targets after that } -- THESE CAN BE ACTIVATED WITH SIGNAL @@ -271,13 +327,16 @@ if not init then ["basic_robot:button_275"] = "button: now select one or more targets", -- button ["basic_robot:button_273"] = "switch: now select one or more targets", -- switch OFF ["basic_robot:button_274"] = "switch: now select one or more targets", -- switch ON - ["basic_robot:button_278"] = "setter: set block at target region {target2,target3} to block at target1", -- equalizer + ["basic_robot:button_278"] = "setter: target1 defines what material wall will use, target2/3 defines where wall will be", -- setter ["basic_robot:button_277"] = "equalizer: target1 and target2 are for comparison, other targets are activated", -- equalizer ["basic_robot:button_279"] = "piston: push block at target1 in direction away from piston", -- equalizer ["basic_robot:button_283"] = "platform: select target1 to set origin, target2 for direction", -- PLATFORM ["basic_robot:button_282"] = "delayer: distance from delayer to target1 determines delay", -- delayer ["basic_robot:button_280"] = "diode: only pass through ON signal", -- DIODE ["basic_robot:button_281"] = "NOT gate: negates the signal", -- NOT + + ["basic_robot:button_284"] = "GIVER: give player item below it when activated and activate targets after that", + ["basic_robot:button_285"] = "CHECKER: check if player has block below it in inventory, remove it and activate targets after that", } linker_use = function(pos) @@ -386,7 +445,7 @@ if not init then end - +opcount = 0 event = keyboard.get() -- handle keyboard if event then if event.type == 0 then -- EDITING From 7767b89f2b9db9844139f704570623ac4c7b1ea0 Mon Sep 17 00:00:00 2001 From: rnd Date: Fri, 29 Jun 2018 19:38:34 +0200 Subject: [PATCH 04/39] coroutine mode added and command pause(), start program with '--coroutine' to use it --- init.lua | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/init.lua b/init.lua index 0adfbf2..dc51e38 100644 --- a/init.lua +++ b/init.lua @@ -7,8 +7,7 @@ basic_robot.call_limit = 48; -- how many execution calls per script run allowed basic_robot.entry_count = 2 -- how many robots ordinary player can have basic_robot.advanced_count = 16 -- how many robots player with robot privs can have basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches - -basic_robot.password = "randompassword"; -- IMPORTANT: change it before running mod, password used for authentifications +basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses ["craft_guide:sign_wall"] = true, @@ -19,7 +18,7 @@ basic_robot.dig_require_energy = true; -- does robot require energy to dig? basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/06/28a"; +basic_robot.version = "2018/06/29a"; basic_robot.data = {}; -- stores all robot related data --[[ @@ -80,6 +79,11 @@ function getSandboxEnv (name) return commands.craft(item, mode, idx, name) end, + pause = function() -- pause coroutine + if not basic_robot.data[name].cor then error("you must start program with '--coroutine' to use pause()") return end + coroutine.yield() + end, + self = { pos = function() return basic_robot.data[name].obj:getpos() end, spawnpos = function() local pos = basic_robot.data[name].spawnpos; return {x=pos.x,y=pos.y,z=pos.z} end, @@ -680,8 +684,10 @@ local function initSandbox (name) end local function setCode( name, script ) -- to run script: 1. initSandbox 2. setCode 3. runSandbox - local err; + local cor = false; + if string.sub(script,1,11) == "--coroutine" then cor = true end + if basic_robot.data[name].authlevel<3 then -- not admin err = check_code(script); script = preprocess_code(script); @@ -691,6 +697,12 @@ local function setCode( name, script ) -- to run script: 1. initSandbox 2. setCo local bytecode, err = CompileCode ( script ); if err then return err end basic_robot.data[name].bytecode = bytecode; + + if cor then -- create coroutine if requested + basic_robot.data[name].cor = coroutine.create(bytecode) + else + basic_robot.data[name].cor = nil + end return nil end @@ -709,6 +721,14 @@ local function runSandbox( name) setfenv( ScriptFunc, data.sandbox ) + cor = basic_robot.data[name].cor; + if cor then -- coroutine! + local err,ret + err,ret = coroutine.resume(cor) + if err then return err end + return nil + end + local Result, RuntimeError = pcall( ScriptFunc ) if RuntimeError then return RuntimeError From b09700c22929fcce597df7c6df88dbd6c9ab152f Mon Sep 17 00:00:00 2001 From: rnd Date: Fri, 29 Jun 2018 21:19:31 +0200 Subject: [PATCH 05/39] coroutine fixes --- init.lua | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/init.lua b/init.lua index dc51e38..b92c7da 100644 --- a/init.lua +++ b/init.lua @@ -549,13 +549,11 @@ end check_code = function(code) --"while ", "for ", "do ","goto ", local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall","\\\""} - for _, v in pairs(bad_code) do if string.find(code, v) then return v .. " is not allowed!"; end end - end @@ -659,7 +657,15 @@ preprocess_code = function(script) found = true; end end - --minetest.chat_send_all("code rem " .. string.sub(script,i1)) + + i2=string.find (script, "pause()", i1) -- fix pause ? + if i2 then + if not is_inside_string(i2,script) then + script = script.sub(script,1, i2-1) .. "_ccounter = 0;" .. script.sub(script,i2) + i1=i2+14+7; -- insert + skip _ccounter & pause + found = true; + end + end end @@ -691,6 +697,8 @@ local function setCode( name, script ) -- to run script: 1. initSandbox 2. setCo if basic_robot.data[name].authlevel<3 then -- not admin err = check_code(script); script = preprocess_code(script); + elseif cor then + script = preprocess_code(script); -- coroutines need ccounter reset or 'infinite loops' fail after limit end if err then return err end @@ -721,7 +729,7 @@ local function runSandbox( name) setfenv( ScriptFunc, data.sandbox ) - cor = basic_robot.data[name].cor; + local cor = data.cor; if cor then -- coroutine! local err,ret err,ret = coroutine.resume(cor) From 03b0d2a3ddb879beeb74f6f920414952c7b585cf Mon Sep 17 00:00:00 2001 From: rnd Date: Sat, 30 Jun 2018 12:53:45 +0200 Subject: [PATCH 06/39] buttons - black on white now craft cache fix --- commands.lua | 5 +++-- init.lua | 2 +- scripts/games/blackbox_game.lua | 27 +++++++++++++++++------- scripts/games/hacking_game.lua | 8 +++---- scripts/games/nonogram.lua | 15 ++++++++----- scripts/games/sliding_puzzle_game.lua | 29 +++++++++++++++++++++++++- scripts/games/sokoban_game.lua | 23 +++++++++++++++----- textures/000.png | Bin 88 -> 116 bytes textures/001.png | Bin 125 -> 127 bytes textures/002.png | Bin 127 -> 130 bytes textures/003.png | Bin 110 -> 141 bytes textures/004.png | Bin 110 -> 126 bytes textures/005.png | Bin 117 -> 122 bytes textures/006.png | Bin 107 -> 122 bytes textures/007.png | Bin 101 -> 119 bytes textures/008.png | Bin 107 -> 118 bytes textures/009.png | Bin 104 -> 119 bytes textures/010.png | Bin 104 -> 118 bytes textures/011.png | Bin 110 -> 123 bytes textures/012.png | Bin 110 -> 123 bytes textures/013.png | Bin 110 -> 122 bytes textures/014.png | Bin 110 -> 123 bytes textures/015.png | Bin 125 -> 130 bytes textures/016.png | Bin 107 -> 122 bytes textures/017.png | Bin 107 -> 123 bytes textures/018.png | Bin 112 -> 124 bytes textures/019.png | Bin 98 -> 113 bytes textures/020.png | Bin 109 -> 120 bytes textures/021.png | Bin 126 -> 127 bytes textures/022.png | Bin 93 -> 113 bytes textures/023.png | Bin 119 -> 128 bytes textures/024.png | Bin 108 -> 121 bytes textures/025.png | Bin 110 -> 120 bytes textures/026.png | Bin 110 -> 124 bytes textures/027.png | Bin 109 -> 123 bytes textures/028.png | Bin 111 -> 125 bytes textures/029.png | Bin 106 -> 118 bytes textures/030.png | Bin 107 -> 125 bytes textures/031.png | Bin 107 -> 124 bytes textures/032.png | Bin 88 -> 112 bytes textures/033.png | Bin 98 -> 118 bytes textures/034.png | Bin 98 -> 114 bytes textures/035.png | Bin 108 -> 118 bytes textures/036.png | Bin 123 -> 131 bytes textures/037.png | Bin 119 -> 128 bytes textures/038.png | Bin 123 -> 129 bytes textures/039.png | Bin 97 -> 114 bytes textures/040.png | Bin 113 -> 122 bytes textures/041.png | Bin 114 -> 120 bytes textures/042.png | Bin 107 -> 120 bytes textures/043.png | Bin 102 -> 120 bytes textures/044.png | Bin 91 -> 111 bytes textures/045.png | Bin 95 -> 114 bytes textures/046.png | Bin 95 -> 113 bytes textures/047.png | Bin 109 -> 122 bytes textures/048.png | Bin 122 -> 128 bytes textures/049.png | Bin 109 -> 119 bytes textures/050.png | Bin 125 -> 130 bytes textures/051.png | Bin 122 -> 127 bytes textures/052.png | Bin 114 -> 127 bytes textures/053.png | Bin 118 -> 133 bytes textures/054.png | Bin 119 -> 128 bytes textures/055.png | Bin 108 -> 123 bytes textures/056.png | Bin 109 -> 124 bytes textures/057.png | Bin 117 -> 128 bytes textures/058.png | Bin 100 -> 120 bytes textures/059.png | Bin 98 -> 117 bytes textures/060.png | Bin 116 -> 127 bytes textures/061.png | Bin 98 -> 113 bytes textures/062.png | Bin 116 -> 125 bytes textures/063.png | Bin 116 -> 125 bytes textures/064.png | Bin 118 -> 128 bytes textures/065.png | Bin 110 -> 121 bytes textures/066.png | Bin 112 -> 120 bytes textures/067.png | Bin 117 -> 121 bytes textures/068.png | Bin 106 -> 116 bytes textures/069.png | Bin 107 -> 124 bytes textures/070.png | Bin 104 -> 122 bytes textures/071.png | Bin 115 -> 124 bytes textures/072.png | Bin 103 -> 119 bytes textures/073.png | Bin 105 -> 118 bytes textures/074.png | Bin 104 -> 117 bytes textures/075.png | Bin 112 -> 122 bytes textures/076.png | Bin 96 -> 114 bytes textures/077.png | Bin 109 -> 121 bytes textures/078.png | Bin 110 -> 123 bytes textures/079.png | Bin 111 -> 116 bytes textures/080.png | Bin 108 -> 118 bytes textures/081.png | Bin 114 -> 120 bytes textures/082.png | Bin 109 -> 118 bytes textures/083.png | Bin 121 -> 125 bytes textures/084.png | Bin 100 -> 114 bytes textures/085.png | Bin 101 -> 114 bytes textures/086.png | Bin 104 -> 118 bytes textures/087.png | Bin 108 -> 122 bytes textures/088.png | Bin 116 -> 123 bytes textures/089.png | Bin 103 -> 118 bytes textures/090.png | Bin 114 -> 128 bytes textures/091.png | Bin 103 -> 117 bytes textures/092.png | Bin 109 -> 122 bytes textures/093.png | Bin 101 -> 115 bytes textures/094.png | Bin 111 -> 118 bytes textures/095.png | Bin 92 -> 111 bytes textures/096.png | Bin 100 -> 113 bytes textures/097.png | Bin 112 -> 126 bytes textures/098.png | Bin 113 -> 124 bytes textures/099.png | Bin 111 -> 123 bytes textures/100.png | Bin 113 -> 124 bytes textures/101.png | Bin 114 -> 127 bytes textures/102.png | Bin 105 -> 125 bytes textures/103.png | Bin 108 -> 124 bytes textures/104.png | Bin 108 -> 120 bytes textures/105.png | Bin 102 -> 116 bytes textures/106.png | Bin 108 -> 119 bytes textures/107.png | Bin 114 -> 128 bytes textures/108.png | Bin 100 -> 113 bytes textures/109.png | Bin 111 -> 120 bytes textures/110.png | Bin 104 -> 117 bytes textures/111.png | Bin 104 -> 119 bytes textures/112.png | Bin 115 -> 124 bytes textures/113.png | Bin 113 -> 124 bytes textures/114.png | Bin 108 -> 120 bytes textures/115.png | Bin 116 -> 128 bytes textures/116.png | Bin 105 -> 121 bytes textures/117.png | Bin 105 -> 119 bytes textures/118.png | Bin 109 -> 120 bytes textures/119.png | Bin 108 -> 120 bytes textures/120.png | Bin 122 -> 124 bytes textures/121.png | Bin 110 -> 122 bytes textures/122.png | Bin 117 -> 128 bytes textures/123.png | Bin 114 -> 122 bytes textures/124.png | Bin 102 -> 117 bytes textures/125.png | Bin 112 -> 120 bytes textures/126.png | Bin 105 -> 114 bytes textures/127.png | Bin 111 -> 124 bytes textures/128.png | Bin 110 -> 125 bytes textures/129.png | Bin 109 -> 120 bytes textures/130.png | Bin 128 -> 120 bytes textures/131.png | Bin 94 -> 111 bytes textures/132.png | Bin 101 -> 113 bytes textures/133.png | Bin 105 -> 117 bytes textures/134.png | Bin 136 -> 139 bytes textures/135.png | Bin 114 -> 122 bytes textures/136.png | Bin 117 -> 127 bytes textures/137.png | Bin 121 -> 132 bytes textures/138.png | Bin 104 -> 123 bytes textures/139.png | Bin 112 -> 119 bytes textures/140.png | Bin 121 -> 133 bytes textures/141.png | Bin 123 -> 129 bytes textures/142.png | Bin 125 -> 131 bytes textures/143.png | Bin 123 -> 127 bytes textures/144.png | Bin 98 -> 113 bytes textures/145.png | Bin 112 -> 118 bytes textures/146.png | Bin 112 -> 124 bytes textures/147.png | Bin 113 -> 125 bytes textures/148.png | Bin 117 -> 127 bytes textures/149.png | Bin 109 -> 120 bytes textures/150.png | Bin 104 -> 118 bytes textures/151.png | Bin 102 -> 118 bytes textures/152.png | Bin 113 -> 123 bytes textures/153.png | Bin 113 -> 126 bytes textures/154.png | Bin 112 -> 124 bytes textures/155.png | Bin 111 -> 116 bytes textures/156.png | Bin 122 -> 127 bytes textures/157.png | Bin 116 -> 127 bytes textures/158.png | Bin 103 -> 120 bytes textures/159.png | Bin 114 -> 126 bytes textures/160.png | Bin 109 -> 122 bytes textures/161.png | Bin 110 -> 126 bytes textures/162.png | Bin 110 -> 124 bytes textures/163.png | Bin 106 -> 123 bytes textures/164.png | Bin 130 -> 120 bytes textures/165.png | Bin 120 -> 120 bytes textures/166.png | Bin 119 -> 128 bytes textures/167.png | Bin 120 -> 128 bytes textures/168.png | Bin 121 -> 127 bytes textures/169.png | Bin 126 -> 131 bytes textures/170.png | Bin 99 -> 117 bytes textures/171.png | Bin 104 -> 121 bytes textures/172.png | Bin 109 -> 118 bytes textures/173.png | Bin 111 -> 123 bytes textures/174.png | Bin 111 -> 125 bytes textures/175.png | Bin 116 -> 129 bytes textures/176.png | Bin 102 -> 112 bytes textures/177.png | Bin 105 -> 113 bytes textures/178.png | Bin 114 -> 113 bytes textures/179.png | Bin 93 -> 107 bytes textures/180.png | Bin 100 -> 112 bytes textures/181.png | Bin 102 -> 113 bytes textures/182.png | Bin 100 -> 118 bytes textures/183.png | Bin 97 -> 124 bytes textures/184.png | Bin 102 -> 119 bytes textures/185.png | Bin 107 -> 117 bytes textures/186.png | Bin 93 -> 107 bytes textures/187.png | Bin 103 -> 119 bytes textures/188.png | Bin 112 -> 120 bytes textures/189.png | Bin 102 -> 114 bytes textures/190.png | Bin 104 -> 115 bytes textures/191.png | Bin 96 -> 122 bytes textures/192.png | Bin 99 -> 115 bytes textures/193.png | Bin 106 -> 114 bytes textures/194.png | Bin 102 -> 116 bytes textures/195.png | Bin 99 -> 114 bytes textures/196.png | Bin 95 -> 112 bytes textures/197.png | Bin 101 -> 112 bytes textures/198.png | Bin 101 -> 114 bytes textures/199.png | Bin 99 -> 115 bytes textures/200.png | Bin 104 -> 124 bytes textures/201.png | Bin 102 -> 123 bytes textures/202.png | Bin 116 -> 121 bytes textures/203.png | Bin 110 -> 120 bytes textures/204.png | Bin 105 -> 120 bytes textures/205.png | Bin 99 -> 112 bytes textures/206.png | Bin 114 -> 122 bytes textures/207.png | Bin 110 -> 117 bytes textures/208.png | Bin 103 -> 114 bytes textures/209.png | Bin 105 -> 116 bytes textures/210.png | Bin 102 -> 114 bytes textures/211.png | Bin 99 -> 117 bytes textures/212.png | Bin 104 -> 117 bytes textures/213.png | Bin 101 -> 119 bytes textures/214.png | Bin 100 -> 117 bytes textures/215.png | Bin 100 -> 112 bytes textures/216.png | Bin 104 -> 118 bytes textures/217.png | Bin 101 -> 113 bytes textures/218.png | Bin 95 -> 115 bytes textures/219.png | Bin 86 -> 107 bytes textures/220.png | Bin 92 -> 109 bytes textures/221.png | Bin 90 -> 107 bytes textures/222.png | Bin 91 -> 107 bytes textures/223.png | Bin 94 -> 112 bytes textures/224.png | Bin 97 -> 113 bytes textures/225.png | Bin 107 -> 120 bytes textures/226.png | Bin 116 -> 127 bytes textures/227.png | Bin 96 -> 117 bytes textures/228.png | Bin 103 -> 119 bytes textures/229.png | Bin 102 -> 123 bytes textures/230.png | Bin 105 -> 122 bytes textures/231.png | Bin 107 -> 124 bytes textures/232.png | Bin 106 -> 118 bytes textures/233.png | Bin 109 -> 116 bytes textures/234.png | Bin 88 -> 110 bytes textures/235.png | Bin 88 -> 107 bytes textures/236.png | Bin 88 -> 107 bytes textures/237.png | Bin 88 -> 107 bytes textures/238.png | Bin 88 -> 107 bytes textures/239.png | Bin 88 -> 107 bytes textures/240.png | Bin 104 -> 114 bytes textures/241.png | Bin 112 -> 120 bytes textures/242.png | Bin 118 -> 126 bytes textures/243.png | Bin 121 -> 127 bytes textures/244.png | Bin 88 -> 115 bytes textures/245.png | Bin 88 -> 107 bytes textures/246.png | Bin 108 -> 120 bytes textures/247.png | Bin 113 -> 118 bytes textures/248.png | Bin 106 -> 116 bytes textures/249.png | Bin 94 -> 118 bytes textures/250.png | Bin 94 -> 113 bytes textures/251.png | Bin 111 -> 122 bytes textures/252.png | Bin 101 -> 118 bytes textures/253.png | Bin 110 -> 126 bytes textures/254.png | Bin 95 -> 122 bytes textures/255.png | Bin 88 -> 107 bytes 263 files changed, 84 insertions(+), 25 deletions(-) diff --git a/commands.lua b/commands.lua index ac9b871..8a4c0ab 100644 --- a/commands.lua +++ b/commands.lua @@ -740,7 +740,7 @@ basic_robot.commands.craft = function(item, mode, idx, name) local cache = basic_robot.commands.craftcache[name]; if not cache then basic_robot.commands.craftcache[name] = {}; cache = basic_robot.commands.craftcache[name] end local itemlist = {}; local output = ""; - if cache.item == item then-- read cache + if cache.item == item and cache.idx == idx then -- read cache itemlist = cache.itemlist; output = cache.output; else @@ -759,6 +759,7 @@ basic_robot.commands.craft = function(item, mode, idx, name) itemlist[item]=(itemlist[item] or 0)+1; end cache.item = item; + cache.idx = idx; cache.itemlist = itemlist; cache.output = output; @@ -866,7 +867,7 @@ basic_robot.technic = { -- data cache compressor_recipes = { --[in] ={fuel cost, out, quantity of material required for processing} ["default:snow"] = {1,"default:ice"}, - ["default:coalblock"] = {16,"default:diamond"}, + ["default:coalblock"] = {41,"default:diamond"}, -- to smelt diamond dust to diamond need 25 coal + 16 for grinder }, } diff --git a/init.lua b/init.lua index b92c7da..39c1297 100644 --- a/init.lua +++ b/init.lua @@ -18,7 +18,7 @@ basic_robot.dig_require_energy = true; -- does robot require energy to dig? basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/06/29a"; +basic_robot.version = "2018/06/30a"; basic_robot.data = {}; -- stores all robot related data --[[ diff --git a/scripts/games/blackbox_game.lua b/scripts/games/blackbox_game.lua index d7f0d1c..9ac9753 100644 --- a/scripts/games/blackbox_game.lua +++ b/scripts/games/blackbox_game.lua @@ -2,18 +2,25 @@ --https://en.wikipedia.org/wiki/Black_Box_(game) if not data then - m=8;n=8;turn = 0; - attempts = 1; + m=16;n=16; + atoms = 32 + attempts = 1;turn = 0; + spawnpos = self.spawnpos(); spawnpos.x = spawnpos.x-m/2; spawnpos.y = spawnpos.y+2; spawnpos.z = spawnpos.z-n/2 + + local players = find_player(5,spawnpos); + if not player then self.remove() else pname = players[1] end 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 + for i=1,atoms do -- put in atoms randomly data[math.random(m)][math.random(n)] = 1 end + atoms = 0 + for i = 1,m do for j = 1,n do if data[i][j]==1 then atoms = atoms + 1 end end 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 @@ -95,7 +102,7 @@ if not data then 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"; + local nodename = "basic_robot:button_"..(65+count); _G.minetest.set_node( {x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]}, {name = nodename, param2 = 1}) @@ -167,7 +174,9 @@ if not data then --render board render_board(0) - keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},5) + keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},4) + keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z-1},5) + self.label("BLACKBOX with " .. atoms .. " atoms") end @@ -175,13 +184,17 @@ 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 event.type == 4 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 + elseif event.type == 5 then + say("#BLACKBOX : DISPLAYING SOLUTION",pname) + render_board(1) + self.remove() end else -- interior punch nodetype = 2; diff --git a/scripts/games/hacking_game.lua b/scripts/games/hacking_game.lua index 5f2abc8..10d2aaa 100644 --- a/scripts/games/hacking_game.lua +++ b/scripts/games/hacking_game.lua @@ -58,7 +58,7 @@ if not init then local players = find_player(4); if not players then say("#fallout hacking game: no players") self.remove() end pname = players[1]; - say("#fallout hacking game, player " .. pname) + minetest.chat_send_player(pname,"#fallout hacking game, player " .. pname) --if rom.data[pname] then say("password is locked out!") self.remove() end @@ -77,8 +77,8 @@ sender,fields = self.read_form() if selected>0 then guesses = guesses + 1 if selected == correct then - say("password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.") - self.show_form(pname, "size[1,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]") + minetest.chat_send_player(pname,"password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.") + self.show_form(pname, "size[2,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]") self.remove() --correct: do something with player else @@ -89,7 +89,7 @@ sender,fields = self.read_form() if guesses>=max_guesses then msg = minetest.colorize("red","A C C E S S D E N I E D!") self.show_form(pname, get_form()) - say("too many false guesses. password locked out!") rom.data[pname] = 1; self.remove() + minetest.chat_send_player(pname,"too many false guesses. password locked out!") rom.data[pname] = 1; self.remove() end end if fields.quit then self.remove() end diff --git a/scripts/games/nonogram.lua b/scripts/games/nonogram.lua index 367440f..111766f 100644 --- a/scripts/games/nonogram.lua +++ b/scripts/games/nonogram.lua @@ -23,7 +23,7 @@ if not grid then end if not rom.score then _,rom.score = book.read(1) end - --rom.score = "0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999" -- default + if not rom.score then rom.score = "0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999" end highscore = get_score_from_string(rom.score) --self.label(string.gsub(_G.dump(highscore), "\n","")) @@ -179,6 +179,11 @@ if not grid then keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},4) -- game check button keyboard.set({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},5) -- game check button + local players = find_player(4,spawnpos) + if not players then error("minesweeper: no players near") end + local pname = players[1]; + + --self.label() --self.label(string.gsub(_G.dump(read_field()),"\n","") ) @@ -191,7 +196,7 @@ if not grid then elseif difficulty == 2 then limit = 80 reward = 7 elseif difficulty <= 1 then limit = 70 reward = 6 end - say("nonogram difficulty " .. difficulty .. ". you will get " .. reward .. " gold if you solve it in faster than " .. limit .."s" .. + minetest.chat_send_player(pname, "nonogram difficulty " .. difficulty .. ". you will get " .. reward .. " gold if you solve it in faster than " .. limit .."s" .. ". Current record " .. highscore[difficulty][2] .. " by " .. highscore[difficulty][1]) end @@ -216,7 +221,7 @@ if event then -- highscore if t= size*size-2 then + minetest.chat_send_player(name, "CONGRATULATIONS! YOU SOLVED PUZZLE. REWARD WAS DROPPED ON TOP OF ROBOT.") + pos = self.pos(); pos.y = pos.y+2; + reward = "default:gold_ingot" + minetest.add_item(pos, _G.ItemStack(reward)) + self.remove() + end end end \ No newline at end of file diff --git a/scripts/games/sokoban_game.lua b/scripts/games/sokoban_game.lua index 294155c..dfa0099 100644 --- a/scripts/games/sokoban_game.lua +++ b/scripts/games/sokoban_game.lua @@ -23,10 +23,10 @@ imax = 0; jmax = 0 sokoban.load=0;sokoban.playername =""; sokoban.pos = {}; - SOKOBAN_WALL = "default:brick" - SOKOBAN_FLOOR = "default:stonebrick" - SOKOBAN_GOAL = "default:diamondblock" - SOKOBAN_BOX = "basic_robot:button8080FF" + SOKOBAN_WALL = "moreblocks:cactus_brick" + SOKOBAN_FLOOR = "default:desert_sandstone" + SOKOBAN_GOAL = "moreblocks:wood_tile_center" + SOKOBAN_BOX = "basic_robot:buttonFFFFFF" load_level = function(lvl) @@ -89,6 +89,18 @@ file:close(); end + clear_game = function() + local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1; + for i = 1, 20 do + for j = 1,20 do + local node = minetest.get_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}).name + if node ~= "default:desert_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:desert_sandstone"}) end + node = minetest.get_node({x=pos.x+i,y=pos.y,z=pos.z+j}).name + if node ~= "air" then minetest.set_node({x=pos.x+i,y=pos.y,z=pos.z+j}, {name = "air"}) end + end + end + end + end @@ -99,6 +111,7 @@ if state == 1 then --say(serialize(fields)) if fields.LVL then + clear_game() load_level((tonumber(fields.LVL) or 1)-1) state = 0 self.label("stand close to blue box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all boxes pushed on diamond blocks") @@ -107,7 +120,7 @@ if state == 1 then else local ppos = player_:getpos() - if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then say(colorize("red", "SOKOBAN: " .. name .. " QUITS ! ")); + if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then minetest.chat_send_player(name,colorize("red", "SOKOBAN: " .. name .. " QUITS ! ")); player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}); self.remove() end event = keyboard.get(); diff --git a/textures/000.png b/textures/000.png index ca46b047d9508488e01856392eb2f619a77a985b..8f76139930010b44ce84fb7aec38c6b42d09f3d0 100644 GIT binary patch delta 97 zcmazjnII9v$N&WQ^(M>#QcOwS?k)@rt9q4}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0MuU<7XSbN diff --git a/textures/001.png b/textures/001.png index 449ca8ddee34a362c83d8e0bbbbba67488bbc07f..8b0d87c4fabac36dccf11c0180b908b03421417b 100644 GIT binary patch delta 108 zcmb=epCFOW$N&WQ^(M>#QcOwS?k)@rt9q4p|37TV5Vb)of^D&qFi<&zr>mdKI;Vst E00Y||)&Kwi delta 106 zcmb=gogk6Q!N$PA@I7>`A&_$Mba4!^=uJ*YNch2j_y|MF*Nln{k}gYfBXp&m*d7MC zgjDe6HM7MhTt1=@bhLefW3&DSPqU60B0ft}n{O&`eN-^!V0f}@ZSBi%ckO}ZF?hQA KxvXs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRP%Ik z45_%4Tydbjfw4_e;{V}|K8j4r-KXna5}vuSxS2iwUvI(2FkyoFdX{g$V}UvtJYD@< J);T3K0RWs%A_@Qi delta 109 zcmZo-te+r}&cVjO!0fA|PP%GZpF4U#TX@{)LD@31}O z(oZ_VzSD6@lVXa%yeIy33H*G`1)55hJ#6ic&Fd2ucRGqZa!eCoh!OF7mCv_03}_;Q Mr>mdKI;Vst00j9Zk^lez diff --git a/textures/003.png b/textures/003.png index ad1fce19c01b8239726013c3559743aaf9c1d833..d69095a7b6f2d5086bfce359c2684894f00d5f92 100644 GIT binary patch delta 123 zcmd1nWt<>U%E$l&_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvH1u?F z45_%4{Nw+BtHb~Q|3CPn(4NWI;Lry{6*H~61N?`sH~vsKJpb4KfEwwnd+7qa4E5Xe Vnm@hrHUnyA@O1TaS?83{1OQhyEDHbt delta 92 zcmeBW%$pz)#=*wG!0lFzvFJ@sNJ#kMuWB=c*NN>&2Y*rXzhDM8UMbU# vS8NZx4!UH?X|YY5__%$MW6A@^Gyw)5)yM8mjQ?K)4Pfwe^>bP0l+XkKT*DtO diff --git a/textures/004.png b/textures/004.png index f6e155bf36a8e3dd4c5f6143873804996784dd56..c985cb0116844dbdfbae0533fd0686adb299935e 100644 GIT binary patch delta 107 zcmd0+n;?#QcOwS?k)@rt9q4`A&@flba4!^=uJ*YNciEeY7@fa#CG_ALec>NqYjO`M#QcOwS?k)@rt9q4tDnm{r-UW|l4Bof delta 98 zcmb;_ogfj*!N$PA@I7>`A&|21ba4!^=uJ*YNchputIQmeb>TyTS%-vy3$Ij7qwhpV z7gip_ho)HqvOxaJ4mGxkf_lzJ6}Ubs7;`Y3eYUpNT`OxR&>RL&S3j3^P6#QcOwS?k)@rt9q4UXNph^Z$S3j3^P6`A&@fiba4!^=uJ*YNciEeY7@fa#CG_ALec>NqYjO`MR2& diff --git a/textures/007.png b/textures/007.png index e5cc15c4a96cf0d50c1c0edd3f74dda602f37b54..8936264e0224dbba24663562382ed8b2fbafeac3 100644 GIT binary patch delta 100 zcmYc-pCA#>$N&WQ^(M>#QcOwS?k)@rt9q4`A&}DXba4!^=uJ*YNciEeYLj)LLAr&TC$QOrbuk++TQgg{ k)uK+vlm~&$FH!{<`qqEcUL7YrAE=MP)78&qol`;+02vz^rT_o{ diff --git a/textures/008.png b/textures/008.png index 0a48eede1a59904910440cc0d3259bce118ad3a5..637495efefabac8597a07c00ca55f3b1fd7018db 100644 GIT binary patch delta 99 zcmc~Un;;R#$N&WQ^(M>#QcOwS?k)@rt9q4gTe~DWM4f@|zpj delta 88 zcmXTRo*)s-!N$PA@I7>`A&@fiba4!^=uQ6NuX?~B>wzqAU3C1;dUIgDe4AUM2?48D$sVGJdTA>SpkC^>bP0l+XkK9GxCZ diff --git a/textures/009.png b/textures/009.png index c4f6ab01cc3505243ec62be59452f6ceaf01633f..4b49dacd069314f6dc5cc589326824313ebe2ea5 100644 GIT binary patch delta 100 zcmc~OpCA#>$N&WQ^(M>#QcOwS?k)@rt9q4?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNciEeYLj)LLAr&TC$PEWVuSnH6^_hF n7an-Kn;3TR*klRpW<4DK&=d(u6{1-oD!M diff --git a/textures/010.png b/textures/010.png index 2ebecd96cfee52610f9ba05ed5db1508be51881a..9c318d73ce116f7219f0b803fa96a13e17b91254 100644 GIT binary patch delta 99 zcmc~On;;R#$N&WQ^(M>#QcOwS?k)@rt9q4gTe~DWM4f>2(|| delta 85 zcmXTRm>?0r!N$PA@I7>`A&}Ddba4!^=uQ6NuX?~B>w#QcOwS?k)@rt9q4FVdQ&MBb@0D^oT A3jhEB delta 91 zcmb=`A&@flba4!^=uQ6N&zO*q;P4=@nXS?BL|0p*qlS>h2LpqS ukdKae0xJ`)ENC?9m~q8NMJ|Pnk-@upSKZsMJ0=1RVDNPHb6Mw<&;$UPdm!ro diff --git a/textures/012.png b/textures/012.png index 44464a5d34d9c8628e0643cbc2cba3fc91cc2d02..2bae39e8dc3ccdd4ff418f4e9a03398248ab2adc 100644 GIT binary patch delta 104 zcmd0+o*#QcOwS?k)@rt9q4=c5@M?U|8Gxbc+sK~#2I891XRl4>FVdQ&MBb@0I|Uy A=Kufz delta 91 zcmb=`A&@flba4!^=uQ6N&zO*q;P4=@neBu^3TM#K_6d$VJ_wk0 u%qWm#V-sX{#QcOwS?k)@rt9q4`A&@flba4!^=uQ6N&zO*q;P4=@nXS=Lqiezk0n-kNv(8r( tf_P;Yu?aG}@k(hl7q}WrR!op&Vd&=DRmZt+i8#;z22WQ%mvv4FO#lG~8<+q9 diff --git a/textures/014.png b/textures/014.png index 2662f10bc04b4f55e1896eec159d31ec6511def4..3a6e9852a2d86f48090f78ff0f6972f872210020 100644 GIT binary patch delta 104 zcmd0+o*#QcOwS?k)@rt9q4`A&@flba4!^=uQ6N&zO*q;P4=@neBvv2&ak-50BE-3yLWh u9&qQ(IwED5aAiTG5l8rx$&KtY9T`|FcGYREeER`t0E4HipUXO@geCw`avxv- diff --git a/textures/015.png b/textures/015.png index c63b46a354c7f3da83c569608381384d54cfa5e9..1f32b4178978421e3e4675dc22320e6aff317b62 100644 GIT binary patch delta 112 zcmbs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRP%Ik z45_%4{NwQd`HXF;Ki)k2!T7k|BJE#7zo_zR$7{da|1aPad538)Dp%<@$DkF$aU`*ZDIU)deMh<}rA> L`njxgN@xNA#QcOwS?k)@rt9q4`A&@fiba4!^=uJ*YNciEeYIB3#QcOwS?k)@rt9q4pJ3FVdQ&MBb@0Qwjr AU;qFB delta 88 zcmb=`A&@fiba4!^=uJ*YNciEeYIB9xiOuR^9~JJ=gSKY qir+9X&-jokI7_;Py#QcOwS?k)@rt9q4$Z%+;W~%P}*L^^>44$rjF6*2UngFae B9|Zsa delta 93 zcmb;^m>?0s!N$PA@I7>`A&@fnba4!^=uJ*YNchputIQmeb>TyTS%-u{3y+eQxnUch vT{By`m6U0Qi@JxA3BPDFo4eJbX^ss4;!Y%`{tG(@G=jm?)z4*}Q$iB}EwCL$ diff --git a/textures/019.png b/textures/019.png index fe43a9c875dbd2af0ae2eb2afeeb3ba417b20a8d..349feb1ee021d87df69541f59f907a3b8fb755aa 100644 GIT binary patch delta 94 zcmYc)oFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz qskoI~@#iB0^Ufdt>sgK|@-b{!Yj-!*dCgX!0tQc4KbLh*2~7ZiP8-Gm delta 79 zcmXR+njqoD!N$PA@I7>`A&}Daba4!^=uJ*YNch2j_yj}b91qjQY`sz@Sr=9~iaa`) ibKwE^n~M>LyBOE&f7I@5IlBg^iNVv=&t;ucLK6T^*B$Qw diff --git a/textures/020.png b/textures/020.png index a8e651ff5f995d69e6fc4cefbb9c07e54a5cb122..fe488978ea9cca16fa7057e7d8e1dbc26680829a 100644 GIT binary patch delta 101 zcmd0;m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4Jk~x8*ETNg_}kgQu&X%Q~loCICTi9l-zq delta 90 zcmb=(ogfj)!N$PA@I7>`A&@fhba4!^=uJ*YNch2jcnMR=UY_Rj3Nb=Uy85MD{F^&E t|0N~}Ts@R=WkF!`id+H7iV2b|3`#QcOwS?k)@rt9q4zopr E08Wk|RR910 delta 107 zcmb=gn;?`A&_$Qba4!^=uJ*YNch2jcnMR=UY_Rj3QD<$y8^H9+ckUe zD%-H}n>BY_beOVmG26w93l2{2%b29ld2)`$RiHdud_wSP1%@XfEnB1+b_)OvWbkzL Kb6Mw<&;$SyuO}n` diff --git a/textures/022.png b/textures/022.png index cf539a84b941fce75d2324f42212bd3c6ac3c48f..f7bf76c96efb1c01e1fdbebf162bf47191b947c3 100644 GIT binary patch delta 94 zcmazooFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz qskoK=`A&^q>ba4!^=uJ*YNciEeYLj(gh2vtjUMUwX3DXP%36md} c1XU#%K3r>^zUEST7f?Ner>mdKI;Vst0A6PpmjD0& diff --git a/textures/023.png b/textures/023.png index 8ccffc011e0c5c30de72e351f3243bac6a7fdf3e..1958a70d10d7ce8a2e71fd26dd5424591928c5b5 100644 GIT binary patch delta 110 zcmXSKV4NV4!N>pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4{Nr$a17n-K!N2xoHnwVue~16e_f0%H@v8FdW)=p!MA_N9j`EiR)iZdy`njxg HN@xNAeL*4j delta 101 zcmZo*ET13|&%wsP!0#QcOwS?k)@rt9q4lFz yskoK=Be=eSu}%JivH`O)lk#dsw*UY6k24g`He|T{_5D`A&@fmba4!^=uJ*YNchputIQmeb>TyTS%-u{3y+eQxnUch rT{By`m6U15l?4y;1tcpbNU|_=we!edxqD6osGY&n)z4*}Q$iB}*&Q5L diff --git a/textures/025.png b/textures/025.png index 0f3caf3f56cfd3601bb690c9e2bbf326efcca0ff..99442e01f4f2d3bbe4dde12c280c78ad56e5af7f 100644 GIT binary patch delta 101 zcmd0+m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4zB delta 91 zcmb=(n;;R!!N$PA@I7>`A&@flba4!^=uJ*YNchputIQmeb>YJb$Hi>DQZ4NNmoxAb uH5asUPn^}><=Ct&d-0Bf(FX-%4u-IH9(lnNidTUKFnGH9xvX#QcOwS?k)@rt9q4`A&@flba4!^=uJ*YNciEeY7@fa#CG_Gf)cN64%@>kjw}*S u*m(CHShm`+S-(3VML@FRib9nn3q$^=Yi`f&^q&F^VDNPHb6Mw<&;$T4#U5t> diff --git a/textures/027.png b/textures/027.png index c63cf6cfdd44e7ce4ec9167f2b22b00326c9f19b..65abf617ada2721b0307d21d07d17ce6f56b032d 100644 GIT binary patch delta 104 zcmd0;o*#QcOwS?k)@rt9q4mdKI;Vst0CUD1 AbpQYW delta 90 zcmb=`A&@fhba4!^=uJ*YNciEeY7@fa#CG_ALQF!3R7#QcOwS?k)@rt9q4`A&@fjba4!^=uJ*YNciEeYIB3^TK}Xvs vI5z8V&@}1@G03{$AnD>WMYxlFrXxe9>_45T<#SF0ObP0l+XkKOv4^c diff --git a/textures/029.png b/textures/029.png index f635fe7510babb3a38214e151e507d0a965f97ac..ba01b59e2905ad78a81bd86776224f9be50200e9 100644 GIT binary patch delta 99 zcmc~Qn;;R#$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fkba4!^=uJ*YNciEeYLj)LL867HAoIZDv<;G9=6fXw qNSXX#nytphyHCd`>w#QcOwS?k)@rt9q4 delta 88 zcmb=;o*)s-!N$PA@I7>`A&@fiba4!^=uJ*YNciEeY7@fa#I~4?H?6sY5lFE$cU#QcOwS?k)@rt9q4`A&@fiba4!^=uJ*YNciEeYIB3^$;O*WNS%bu)On`njxgN@xNAEwLVG diff --git a/textures/032.png b/textures/032.png index ca46b047d9508488e01856392eb2f619a77a985b..a95a40c4c54db707970036afe94dcf9978fdfb67 100644 GIT binary patch delta 93 zcmazjm>?0s$N&WQ^(M>#QcOwS?k)@rt9q4}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0Mknq6951J diff --git a/textures/033.png b/textures/033.png index 31102145d405836137076f59aa0b16e90130ebc0..c64aad8ddf89777fe4e35f30b4db1ff763b566c9 100644 GIT binary patch delta 99 zcmYc)n;;R#$N&WQ^(M>#QcOwS?k)@rt9q4bP0l+XkK0D~NJ delta 79 zcmXR*njqoD!N$PA@I7>`A&}Daba4!^=uJ*YNchputIQmeb>YJb$Hi>DQYKjftraUI hOnxv`n_PP!!Ej=s|JjqC{_a3c44$rjF6*2Ung9xs8$#QcOwS?k)@rt9q4`A&}Daba4!^=uJ*YNchputIV8qMAD5{N}>701)C6_MFRa& hCRrC&I4WOKV7U9t|7^~Dv0$Jk22WQ%mvv4FO#si*8QTB= diff --git a/textures/035.png b/textures/035.png index 78eadff3d470d1a098f7e1a79aadeb660dfdb27f..17dec8848517d5e62ca2414d14345242c71a8a8c 100644 GIT binary patch delta 99 zcmc~Pn;;R#$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fmba4!^=uJ*YNch2j_(X$5p@~hFz+IkS{)`e&*m(CH okn1%)1g1?pOqw;0G@nsmkb8R~X`Y6{JfL<4Pgg&ebxsLQ06AJ7!~g&Q diff --git a/textures/036.png b/textures/036.png index a50d5b09f5fdc0a15a12791250c9471c3b4491c7..a00cc92e4370bc673a631f194562313347c1c43d 100644 GIT binary patch delta 113 zcmbs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRQGgo z45_%4{3E!&fw4{AKxxCDdX~Ba{RjTrD;Y4`Gbzvh{NG-NnPGmS{*uWZlVgBd7(8A5 KT-G@yGywp&k0HVU delta 105 zcmZo>tezl|%)!RM!0}q1!3o|&$;_D* zA0%C}BzOaxI}TRx$mXyKiW(egx%j}iqsKw^72Cu{_L+_huge`@3ZAeH2b#v<>FVdQ I&MBb@07={=d;kCd diff --git a/textures/037.png b/textures/037.png index b8f6e230dc08ef931988ef0aaa0a0abbe3d0cf1c..3bcc88f960732d27ed63adfe444b0c068816eca5 100644 GIT binary patch delta 110 zcmXSKV4NV4!N>pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4JmLS3`HX$P1%KEJ{R{3t(EnfgVLwMt&bj~p3wRh@s;#bP0 Hl+XkK*1aTO delta 101 zcmZo*ET13|&%wsP!0Y`t%oC8D)jrv=;tG#hvj^{V#S{U_i#`^=*jpVLzII%5o00J44$ve9Pgg&ebxsLQ E0H*#UmjD0& diff --git a/textures/038.png b/textures/038.png index 9fc73138a2ceed81d54b35f657f040f8cae6ca11..6ac004c5a4d3f6a87dce802bf842336e0f6de46b 100644 GIT binary patch delta 111 zcmbs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRP}Um z45_%4{3E!&fw4{gyRyNiAI(lI(;m7f{H$Yf3;X=vUWS?B=STe|zaIBU0yQvry85}S Ib4q9e0Ih{1Z~y=R delta 105 zcmZo2!9LTG;bOVt%hIDZ8-b=Vc)I$z JtaD0e0sw{sCTIWv diff --git a/textures/039.png b/textures/039.png index 37eec80ddad8536cb5ce04ec97b2ec62cdf26b09..753019be7977c641cca3ed641597e61129f17589 100644 GIT binary patch delta 95 zcmYc+njjIy$N&WQ^(M>#QcOwS?k)@rt9q4Yuqx8Sv{saI2Pwr)u5wcz3{NTlFpb7?0S3j3^P6`A&}DWba4!^=uJ*YNciE;q0DUa%8gg5pt<9UjR>z2bH9{H g)`b<0%9j)vrce3L@4r-gGf)qMr>mdKI;Vst0F;CndH?_b diff --git a/textures/040.png b/textures/040.png index db03697208b48df398989f7aa78ab2a850227f0a..734c92ef6193950dfd4be41350e32a2127f864ea 100644 GIT binary patch delta 103 zcmXS{njn$H$N&WQ^(M>#QcOwS?k)@rt9q4`A&|20ba4!^=uJ*YNciE;@t7&&Ra=*1g~@|M%{LW{O!}ov yvIO4pEV^)EVUmEJ^T!AS5#B{n5*M#3Fl^FTQ1j^4f}cP$7(8A5T-G@yGywoXbR(4j diff --git a/textures/041.png b/textures/041.png index 781247c2af2621b58cf9db119dcf72c6bd0e9a7f..bd4ed1abc84e2c54ece4b66e005579db8931ec3b 100644 GIT binary patch delta 101 zcmXS_m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4`A&|24ba4!^=uJ*YNch2jcn8BKDVqud=@xF`%ohPB9Uji^ yyi%qec5J-+beuLh?qHbs(0m5(A_0D*W`>tL7S!;0@G$`mVeoYIb6Mw<&;$UDCmqWG diff --git a/textures/042.png b/textures/042.png index f47e2590437853ff00281c6ed7cb24e082a508da..6c189a5251c9bad64bae21f0f96d39ae1aa1f33f 100644 GIT binary patch delta 101 zcmd0`kB$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fiba4!^=uJ*YNciEeYLj)LL867HAoIZDGy~}tZ)QU= qo}y;9_=MoI3S1voI4))j>|y-5(f{n!d-4fD-3*?telF{r5}E+-H67jn diff --git a/textures/043.png b/textures/043.png index f4b2160358e199e0324d8d2151f6057bee1e9a9c..202f2f47b14e34b96724d76b7975eef89127dab6 100644 GIT binary patch delta 101 zcmYeRm>`kB$N&WQ^(M>#QcOwS?k)@rt9q49)|BDz|{@7(8A5T-G@yGywokIUW%J delta 83 zcmb`A&}Dbba4!^=uJ*YNciEeY7@fa#I~4?*R1(pFaz68$7cNv l?z~F(6^uTtaQtG#!B910LCr0jih7_%22WQ%mvv4FO#mmK8u0)C diff --git a/textures/044.png b/textures/044.png index db732e32cfc50f0796ab7fb4b8fcca9f37b15f25..c4fb951706c5eb3596793fbe5058caa3f3e054ea 100644 GIT binary patch delta 92 zcma#fpCA#=$N&WQ^(M>#QcOwS?k)@rt9q4`A&^q?ba4!^=uJ*YNciEeYLj(gh2vtjUa6Lzi3dTfi>DPB Y-empf=S$fw4^++I>FVdQ&MBb@0A3{+=Kufz diff --git a/textures/045.png b/textures/045.png index 1eda2e2b5499c95e80aafccff27ac751cf3a8504..25773639d6526e5765e93b3da5130276f4d6a069 100644 GIT binary patch delta 95 zcmazqnjjIy$N&WQ^(M>#QcOwS?k)@rt9q4`A&^q@ba4!^=uJ*YNciEeYLj(gh2xaR|3w-c^DaE#eiPX2 f#w#_Ajgg^crOAt#m*PGEbuf6k`njxgN@xNA=*Js| diff --git a/textures/046.png b/textures/046.png index a1dd5f3587c5533d4d5cb0c20566b66f2d6b2449..d3320f5a7adc2f35bfd66a2e5052595eee6713b7 100644 GIT binary patch delta 94 zcmazqoFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz qskoK=`A&^q@ba4!^=uJ*YNciEeYLj(gh2vtjUa6Lzi3e9WIv-V7 f<@&!tl7(USrzcvA&AFxnbuf6k`njxgN@xNA(-j)Y diff --git a/textures/047.png b/textures/047.png index 575703d478f4e294704d0f0c0f3173c34b5dada2..b1a7df83679ccd48e126b51a88a5f6f96f09692c 100644 GIT binary patch delta 103 zcmd0;njn$H$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fhba4!^=uJ*YNciE;<;Ip#pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4TydbjfwAqWz0yNBmT8(U3D5dDde+F@ob&(xLkWfe6|IQ#p)pf{>KQy;{an^L HB{Ts5ZayK_ delta 104 zcmZo*tePN^#KFeE!0fA|PP%GZpF4U#TPawBx5+1La{ z1ZL!Tie{`C#HBxAt}gU;xyj0W{o4wXA~G7rmd}Qvs+*fG>pO1)z4*} HQ$iB}>l-5R diff --git a/textures/049.png b/textures/049.png index 775c41993fe966a0a5d48a0d5dbe62331f4400d9..5f410ac1a8bf659297ca6e1b05fe6b4a7d64c3be 100644 GIT binary patch delta 100 zcmc~TpCA#>$N&WQ^(M>#QcOwS?k)@rt9q4FVdQ&MBb@0RI~tCjbBd delta 90 zcmXTVogfj)!N$PA@I7>`A&@fhba4!^=uJ*YNchputIQmeb>TyT8Hcz88yoMw19GMr tR~9@>zEIC~K*7A@M1zX`2KF-w453jUwT%+Bw*mDtc)I$ztaD0e0st|q9!3BF diff --git a/textures/050.png b/textures/050.png index a343a7c2ead6eba637ef7694b71d18c27aa281a7..766f709ecd31f6485b978dbb849342ba35a4ac9a 100644 GIT binary patch delta 112 zcmbs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRP%Ik z45_%4TydbjfwAqWz0!wzl|SwBjq?B96H>nN^l<$7Z!g2l@cO_0p)6-ZkPZefA|PP%GZpF4U#TPawBx5pRfro zuACw1QuKvCui3+wtND&Xkm=(P`yISWqC7>-Cp`3*q)ucs=3wZ*8FQ{|%d`7H^B6o` L{an^LB{Ts5%#|jK diff --git a/textures/051.png b/textures/051.png index 2b5e54ec9fbeba63eaa9452395acddb7411dc34b..dc771f4b454e0f6afbdc604b62da035f1a488b9d 100644 GIT binary patch delta 108 zcmb=bpCFOW$N&WQ^(M>#QcOwS?k)@rt9q4`A&_$Pba4!^=uJ*YNch2j_y|MF*Nln{k}gYfBXp&oun8`% zoFVB_^o3uxnJqqHa*n{nfAU>yg2GQ-qReLSE)w83YG#Q4sm`Kk|Fs@y7=x#)pUXO@ GgeCwXgd*So diff --git a/textures/052.png b/textures/052.png index 9d406174aa2403ad43d8038f0b0ce5d042713435..f4e28322e7499296f41016f67f3f9b6bc336567b 100644 GIT binary patch delta 108 zcmXS_pCFOW$N&WQ^(M>#QcOwS?k)@rt9q4`A&|24ba4!^=uJ*YNciE;@t7&&Ra=+i4ilch<_{WDU2KAj xDs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`Ev)bw<5 z45_%4oRIM6Jj3Bn|M?FXGXJl)`1fDXdfNXAhCX?Qs|P>)|6jnvAW>;J!F$&0uRu)< Mp00i_>zopr0GAgh7ytkO delta 100 zcmZo=ESn$^$HB(H!0lFzvFJ_y;m??mkl^s(V6H$H%gTxZX_ulMJbBF? zyxcZzeNryw%^f|r7G?pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4{Nr$a17n-~fA@q+l`{wW4}6l}_?Z0<$DjZ9GRzGA=XJ}bt-iGmsGh;o)z4*} HQ$iB}wHPD` delta 101 zcmZo*ET13|&%wsP!0DgV diff --git a/textures/055.png b/textures/055.png index f4052c5bd90a0a4e3df610bc8f65bb072d4f6da4..5b2ea476c9227394dfe3d70e1485070f8ae188eb 100644 GIT binary patch delta 104 zcmd0)o*#QcOwS?k)@rt9q4`A&@fmba4!^=uQ6N&zO*q;PBvJu0R(HpG^yolG|qkvyKx5 sGFogCXX&`{N||;nW1HB>KGTt5hWtnE-E(J}0kt!Dy85}Sb4q9e0OGJ5pa1{> diff --git a/textures/056.png b/textures/056.png index efd2f5fd75d52458e9f8f69fdea41092f0aeab92..07cf2bb2dd0f3c5ec1ef22e737294116aa952680 100644 GIT binary patch delta 105 zcmd0;nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fhba4!^=uJ*YNch2j_y|MF*Nln{k}gYfBXp%rGZyej t)x69waqP=zW{bC4)afYl$T3ZTA+4-@^Ouiz<^c6Gc)I$ztaD0e0swaaAZ-8u diff --git a/textures/057.png b/textures/057.png index 120d8bfc4a06488b32bfead7e0140767a41b5378..f76ed664af7e5b6e0299a27bd6f8efd4ed88d4fc 100644 GIT binary patch delta 110 zcmXSIV4NV4!N>pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4TydbjfwAqWebRA$3Be!#-Bs@NAK-rlWYt@+F)TiBl(a4RV+>F|gQu&X%Q~lo FCIE>OBJcnJ delta 99 zcmZo*ES(?`%fZIL!0fA|PP%GZpF4U#TPawBx5O*0no zD4G6hU*QbBC6y;Lb7#TkAaeNujed;>U90pHUKbLh*2~7Za CZz4wk diff --git a/textures/058.png b/textures/058.png index a237d6d52e06a77ca6b3e4142e59e5f34135a80f..c09ff63f727c6aef1c019f3ef973a7589c4f2724 100644 GIT binary patch delta 101 zcmYePm>`kB$N&WQ^(M>#QcOwS?k)@rt9q4`A&}Dcba4!^=uJ*YNciEeY7@fa#I~4?SG0MB*x$u$7cVY2 kIK9t}SE}n}M+gst!$v9QU#&;K0ktuBy85}Sb4q9e03-q%$^ZZW diff --git a/textures/059.png b/textures/059.png index d5de0ef1fb418906280d29725024b121fc14aea7..d7836ea41fa51dc5911411ea56c8ab070a8add3a 100644 GIT binary patch delta 98 zcmYc)ogfj*$N&WQ^(M>#QcOwS?k)@rt9q4pl&%(5re0zpUXO@geCyu`A&}Daba4!^=uJ*YNciEeY7@fa#I~4?SG0MB*x$u$7cVY2 iIK9t}SBjI3k)cr2#QcOwS?k)@rt9q4YuW842L${!y8=g{eInRs;KRpr^A|J%zjGnAgNxSnymvKOeF!PC{xWt~$( F69B!CB&YxY delta 97 zcmb;~nII9v!N$PA@I7>`A&|26ba4!^=uJ*YNciE;q0DUas;$e>B}-0=ZQ{a818J9q z+H9L0oAozjCJD5*@rgE{FeuTGa@lIIXtg6lt6kQn$7cJ=fW|O*y85}Sb4q9e0OQFZ AE&u=k diff --git a/textures/061.png b/textures/061.png index d673f355ce489dec7a9f6201bf5361ca1bf95a3c..dd4110a45a19bdb004287ca203f4e1c714442819 100644 GIT binary patch delta 94 zcmYc)oFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz qskoK=ru=f#A0fVQjpUXO@geCxQEgHQ5 delta 79 zcmXR+njqoD!N$PA@I7>`A&}Daba4!^=uJ*YNciEeYLg{!m*mdKI;Vst0OGb9)&Kwi diff --git a/textures/062.png b/textures/062.png index e3c57fa8397de797f5b94722f355cbf85ef9fe29..d8a568108e9bca4c3e31c42e65dff492b9fa2d48 100644 GIT binary patch delta 106 zcmXS^ogk6Q$N&WQ^(M>#QcOwS?k)@rt9q4`A&|26ba4!^=uQ67&mtiqapHLMd4&|d);7MhW(|{)6%sCO z3!RTHI4FKYMAGEPCJtA&z!zNya|Bvvv2S!_@LiX+X+x=hAJ7;EPgg&ebxsLQ0Alna A&j0`b diff --git a/textures/063.png b/textures/063.png index 5c3803c9e2d4076d58a698e35434699a897904c6..eb38f4083f793c3a220713c65df55b70f8d89537 100644 GIT binary patch delta 106 zcmXS^ogk6Q$N&WQ^(M>#QcOwS?k)@rt9q41iWxjz{an^LB{Ts5 DGNK|X delta 97 zcmb;|nII9v!N$PA@I7>`A&|26ba4!^=uJ*YNch2j_y|MF*Nln{k}gYfBXp&oun8`% zl#q5wvEUPJ_V5*RKA~XrA;2)>1M9bpH3tnDF0EKlQ>*br6KD*Br>mdKI;Vst085!7 AGynhq diff --git a/textures/064.png b/textures/064.png index 0f8e5a0340bcd615d6b14c65b904c8402d7e10de..fa030a4549d9a2f3f918ccff976f1fb834ae1ffa 100644 GIT binary patch delta 110 zcmXSGV4NV4!N>pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4TydbHfwArBf5QVCdF1)dG#vQ!pZ|d2Cd>c-4;wOAh{`zSX~%^D)iZdy`njxg HN@xNAma!r~ delta 100 zcmZo*ESn$^$HB(H!0lFzvFJ@sNJ#j>e|QN~%3hx4^9nX*0!K8e+WQ=j zw47ey=zLURju?+@v&S)@RM(-@7ZNU84WvZa7#S?hOjzYNeV+t0h{4m<&t;ucLK6V3 C<{~cu diff --git a/textures/065.png b/textures/065.png index 16e3671cf79f6f32af9ed1a0b25ee23f3c323264..19e1c7901c60df6d7ea6f7a623ec60565549249a 100644 GIT binary patch delta 102 zcmd0+oFI|N$N&WQ^(M>#QcOwS?k)@rt9q4lFz yskoI~aiG3|vF)k7k^!?alXACk^Z)`A&@flba4!^=uJ*YNch2j_y|MF*Nln{k}gYfBXp&m*d}td uO>k`1-{8(GWt#Cr!lfro>7#-%2gCNfNi$T`Tb=+7VDNPHb6Mw<&;$Tv#~zUY diff --git a/textures/066.png b/textures/066.png index 543458bf7a560d10c42d55b4ad3ec9e4f8cb9179..90565ea8afbd287fd575cc675f5a417ea2dbba09 100644 GIT binary patch delta 101 zcmXS@m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4hQpua1tq3i==H4m_rKnPjlr}?0s!N$PA@I7>`A&@fnba4!^=uQ6NuauCGaG;NUyQ77uV&a@vKt>4n;)CfI v9&o=2Y`o*(J!Odl8o}V{>gTe~DWM4f@K_-$ diff --git a/textures/067.png b/textures/067.png index dfb4bd92c38cbebb80980b05ef9ee77211f0daba..d43d262aea0e97579b961f526ce3c5fd447dd9d9 100644 GIT binary patch delta 102 zcmXS|oFI|N$N&WQ^(M>#QcOwS?k)@rt9q4lFz yskoI~aiG3|vF)k7(!>8Q2`XPtJp1#X|2TtEkskB+H~9@fg$$mqelF{r5}E*~ARw0j delta 98 zcmb;{ogfj*!N$PA@I7>`A&|21ba4!^=uJ*YNch2j_y|MF*Nln{k}gYfBXp(H*aCHX z+<2u-JEVZD)XVo2V!8?#ZKm<0HESGcKBK_En9d`=aUx?a&>RL&S3j3^P6#QcOwS?k)@rt9q4hQpuaPaZ$*`A&@fkba4!^=uQ6NuauCGaG;NUyQ77uV&a@v?z~c_8CMnr pHg_;v=t|bG2@31EJaSAEU|6p?X-1A-LlsangQu&X%Q~loCIF#QcOwS?k)@rt9q4`A&@fiba4!^=uQ6N&zO*q;PBvJu0R*d%8CMMmyie^-)6S> qgnlWLMZ8L)JVnhKT+{iq(Oo59o7&t;ucLK6VJWE>~} diff --git a/textures/070.png b/textures/070.png index 4ab4124f35c9e14a5837c0716fd4861003492207..189715893d28ae7ffe808b586c8c99f37aa6cb6e 100644 GIT binary patch delta 103 zcmd0(njn$H$N&WQ^(M>#QcOwS?k)@rt9q4FVdQ&MBb@0Nh+6 A5C8xG delta 85 zcmb=*m>?0r!N$PA@I7>`A&}Ddba4!^=uQ6N&zO*q;PBvJu0R*d%8CMMmyie^-)6S> ngnlWLtP39uJDkoo-%wy+n#3cozaU2msFlIf)z4*}Q$iB}jI|qe diff --git a/textures/071.png b/textures/071.png index adb87d5dd84131f93eb39c3c12c65bc7ef9d50bc..db24a8dd6e5fc17f690e5231d433d4cdb07f9c73 100644 GIT binary patch delta 105 zcmXS}nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4%#`A&|22ba4!^=uJ*YNch2jcnMR=UY_Rj3N~gF+S->nhHx)F zm@c5_?9MA?+VP4_aB@@UmK+0V7e%=gHbw?5?efi@=@$xsrZ9NA`njxgN@xNA>b)NW diff --git a/textures/072.png b/textures/072.png index cd775ae435967cb1b25f0ffa3b9055257e0b20e9..14da18e713afc12a846af42620c7469b2e747cb2 100644 GIT binary patch delta 100 zcmYc$N&WQ^(M>#QcOwS?k)@rt9q4IWOzXDxfk3Pgg&ebxsLQ05=*P`~Uy| delta 84 zcmXR`A&}DZba4!^=uQ67&mtiq!I3oQl{>GLLbC^}^N9rq#cwQT n>y(zKl7*qbtbFt9?Zx|nIvG4&{an^LB{Ts5I>H+D diff --git a/textures/073.png b/textures/073.png index 980bbaf495e5605d1283e0fbda23b346c643eb9c..e3891af7814910e0ed18304471c8932fb59f9843 100644 GIT binary patch delta 99 zcmc~Sn;;R#$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fgba4!^=uJ*YNch2j_y|MF*Nh4S>6X(0HnZG$rA#xf pEC_5a$UMf{w!%^5kz<+w!`F^$ZV!_;?f~j#@O1TaS?83{1ON+AAAJA- diff --git a/textures/074.png b/textures/074.png index ba2fe11054d8dca669021959fa1e10e19604538d..605693a77bbbc0e5cd0063f35cc263c8b2c238d2 100644 GIT binary patch delta 98 zcmc~Oogfj*$N&WQ^(M>#QcOwS?k)@rt9q4FSP=aClH*1clfVa1SiWody{an^LB{Ts55~Chz delta 85 zcmXTTm>?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNciE;<;Ip#w3w||$|UQ;2@{TfUgx_C nFr diff --git a/textures/075.png b/textures/075.png index 3ba848ed21c629f78486c1260c454ccf4d4544a9..71b5e49f9c4dbda021320f7d7be79bd840b4dca3 100644 GIT binary patch delta 103 zcmXS@njn$H$N&WQ^(M>#QcOwS?k)@rt9q4mdKI;Vst0O~&> Aga7~l delta 93 zcmb;_m>?0s!N$PA@I7>`A&@fnba4!^=uQ67&mtiq!I3oQRr@kWmq@c6yoXpjICned xU3kEqGfUd!$EFi291mYpNHP)bWS{BCU~|Ut<;V3$yMRV8c)I$ztaD0e0s!arB6$D+ diff --git a/textures/076.png b/textures/076.png index 2cc684cbab5f8268e3b6743b4123489d840e9be1..160b9c0c8a32791c7239d17106c5f91802744fb2 100644 GIT binary patch delta 95 zcmYc&njjIy$N&WQ^(M>#QcOwS?k)@rt9q4}W7!N$PA@I7>`A&^q{ba4!^=uQ67&mtiqapL%5wq7ZdtP3j~58qM#QcOwS?k)@rt9q4lFz yskoIq;s1~MjBPWe1tXRTG)}xK(ER^@don{TuOV9k4_6>iA%mx@pUXO@geCxGZXF^3 delta 90 zcmb=-ogfj)!N$PA@I7>`A&@fhba4!^=uQ67&mtiq!I3oQ75jF_9U_8@()cB3}xHOH)l7t1_Jdnc)I$ztaD0e0ssYD9sU3S diff --git a/textures/078.png b/textures/078.png index 2c0f036ce9f36694fe7750771146ca9dd6ae0766..ded4bd2f4593eccb4883adc6830eb1b6e2e285fa 100644 GIT binary patch delta 104 zcmd0+o*#QcOwS?k)@rt9q4u)pQw|NjL%3^l!mY_E$?p93mo@O1TaS?83{1OV`+ BA&&q6 delta 91 zcmb=`A&@flba4!^=uQ67&mtiq!I3oQ75jF_BNA3Fyh?6|4GcJ@ uKTAxzU~nRPF&nRFbH_y=6}c2PMuq~j^3AjT#QcOwS?k)@rt9q4`A&@fjba4!^=uJ*YNch2j_y|MF*Nln{k}gYfBXp%rGp;NM vY7Y|#d3@ZXaa+$tDnm{r-UW|-F_hx diff --git a/textures/080.png b/textures/080.png index e9bfe0eb10e445bcfd3569fc44d8a4bacb96762e..e89747a095acdfab93c661868d73aadd5cd35fa7 100644 GIT binary patch delta 99 zcmc~Pn;;R#$N&WQ^(M>#QcOwS?k)@rt9q4hQpua1tnbn*k}H*XJTe(-mZ6b>b5^;fT|cgUHx3vIVCg!03n?oP5=M^ delta 89 zcmXTRnII9u!N$PA@I7>`A&@fmba4!^=uQ6NuauCGaG;NUyQ77uV&a@vKt>4n;)Ce| qhKVLw7gjhfW}DcpdC8`Omx`kB$N&WQ^(M>#QcOwS?k)@rt9q4kT$KO7aA{{QDc&hU#}x0Oxd#TlSF22WQ%mvv4FO#oj`9-jaJ delta 95 zcmb;@njjIy!N$PA@I7>`A&|24ba4!^=uJ*YNch2j_y|MF*Nln{k}gYfBXp%rGp;NM yY+jKq5IBw3uDKxdz*5Qnh0Qk=xIQWvb1-Q9i#Zn-%G&}ogu&C*&t;ucLK6T#K_TG) diff --git a/textures/082.png b/textures/082.png index a6216eb89dc540d6d6fc44fb322648a2a112a575..384e948d19c0537d1ea2d505ea7839b30c809261 100644 GIT binary patch delta 99 zcmc~Tn;;R#$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fhba4!^=uQ6NuauCGaG;NUyQ77uV&a@vKt>4n;)CfI s9&o=2YF#NzF+!I^D%)0QEC?y85}Sb4q9e08Nn|N&o-= diff --git a/textures/083.png b/textures/083.png index 6c1d3c8f402532c79bfd250b98e719997ed90152..f332a744506d0377dc5873e7c29910ca7533d36e 100644 GIT binary patch delta 106 zcmb=dogk6Q$N&WQ^(M>#QcOwS?k)@rt9q4hiWxjz{an^LB{Ts5 DF<~M5 delta 102 zcmb=eoFI|N!N$PA@I7>`A&_$Lba4!^=uJ*YNch2jcnMR=UY_Rj3N~gF+SuniE@`_c zVA>(kbNoVJ^NTcriGkK4yoZ)@h#yEknPAr8WOIUtiQ(lR$Crm(9SVSEF?hQAxvX#QcOwS?k)@rt9q4`A&}Dcba4!^=uQ6N&zO*q;PBvJx_}gW-^Yvtt?s;1rWscj j1U9>MIHo*sOcP*`nSRYpbNN~)pf(0iS3j3^P6#QcOwS?k)@rt9q4`A&}DXba4!^=uQ67&mtiq!I3oQl{>GLX~vZWfz54m9e0R4 kWUA1SJi`{)!9LTG;o)}2mzJ&fK-w8RUHx3vIVCg!057x~LjV8( diff --git a/textures/086.png b/textures/086.png index cd70246b12d0d68f1d8c5b6e1c17d787744ef7f5..6fefbd3083142ffecab44cfa4e9e38fb9d4fe659 100644 GIT binary patch delta 99 zcmc~On;;R#$N&WQ^(M>#QcOwS?k)@rt9q4FVdQ&MBb@0BYGF!~g&Q delta 85 zcmXTRm>?0r!N$PA@I7>`A&}Ddba4!^=uQ67&mtiq!I3oQl{>GLX~q?WJ#2!DJ3LOi n@k$9apSWNn!mGs0Z`90C<<27?)qAQ4sFlIf)z4*}Q$iB}FXkCK diff --git a/textures/087.png b/textures/087.png index 2c79a40091df20f0f9d5d4debe969bdcc412318e..b2170bf728b519b1c645394505ad5fa6f6717b4d 100644 GIT binary patch delta 103 zcmd0)njn$H$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fmba4!^=uQ67&mtiq!I3oQl{>GLX~q?WO*~4WH4~&= qdY&m}U3lQy&fw|l=d#Wzp$P!ZW*eRW diff --git a/textures/088.png b/textures/088.png index 16046eb297638f4fe345d40bd376febc837f871b..543b606d8576449d6f645d25f9cff506714e031d 100644 GIT binary patch delta 104 zcmXS^o*#QcOwS?k)@rt9q4`A&|26ba4!^=uQ67&mtiq!I3oQ75jY0BN|GVHXY0nket=t z<=CvhLDQ(?%7z0g>#i&aY+jKlU^uJuNb?y5hFo_ZdC%E06M)7rc)I$ztaD0e0sw<< BBPaj> diff --git a/textures/089.png b/textures/089.png index cec54dbd300d7aaf450884bf3a7ad6d67cf610c7..34d8b5d248a11eebe983a01b74c9d5cd9e74488f 100644 GIT binary patch delta 99 zcmYc#QcOwS?k)@rt9q4FVdQ&MBb@0A*nyZ~y=R delta 84 zcmXR*pCIAS!N$PA@I7>`A&}DZba4!^=uQ67&mtiq!I3oQ75jY0BN|GVHXY0nkeubt nD`lE-WkFzbM~DdvFB3!8jRiH0v3gs9IvG4&{an^LB{Ts5VFDXi diff --git a/textures/090.png b/textures/090.png index df0cddbcac61aaf07f7a8ed9617b5d4abe6ec2ff..132769c0e34a07d2f59106b2a403b89959216a72 100644 GIT binary patch delta 110 zcmXSFV4NV4!N>pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4oRIM6Jj3Dt_DT=ySpEg~AL#$Dyun7u&G5Os3^T*!B-1O?t(;?l>KQy;{an^L HB{Ts5g()Gv delta 96 zcmZo*ESew@#lgnF!0#QcOwS?k)@rt9q4E{TvAk^Zf1Vjo5re0zpUXO@geCyquN`A&}DZba4!^=uJ*YNch2j_y|MF*Nlo45-skBS2!+a>ySXYA^>bP0l+XkKp*0+| diff --git a/textures/092.png b/textures/092.png index 5ae68dd2327323f04c31c4f99771cd13758e7b4a..01b4feae883e6ee966fe8243cb0aed1fd12c2ba7 100644 GIT binary patch delta 103 zcmd0;njn$H$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fhba4!^=uQ67&mtiqapHLMd4&|d*0w$=69u-wj28ih s9X!tNyiy@-f|)M@%sa9YU5c9-{?E+XBsl4lK2Se{r>mdKI;Vst01LMrj{pDw diff --git a/textures/093.png b/textures/093.png index 3a14443e73a8f05f7b91551fafec9a45fd04a332..6a62a60508d5df5844cfa5e884b6b62f141dae1b 100644 GIT binary patch delta 96 zcmYc-o*)s;$N&WQ^(M>#QcOwS?k)@rt9q4mdKI;Vst0LjuEu>b%7 delta 82 zcmXR;ogm@M!N$PA@I7>`A&}DXba4!^=uJ*YNch2j_y|MF*Nh4S=@#yODU+-VD;yWI k@!o9e&0-7eV4vy8u)OJ-Tb%Z01E4+zPgg&ebxsLQ09;ZVr~m)} diff --git a/textures/094.png b/textures/094.png index 61afe833625627ccc1b2e7b51ddd94217a0f297b..4c7dc97c70b1f94c2536dfc33b3e60aec6e122dc 100644 GIT binary patch delta 99 zcmc~Vn;;R#$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fjba4!^=uJ*YNchputIQmeb>TyT8OQWAC0^Mawuw?= v7FYT0nmc-;K3%@J!f`QMuawCdUM7ZxFHK&gO6y+*n!w=c>gTe~DWM4fa#|l; diff --git a/textures/095.png b/textures/095.png index 64ecae6ca260f7e7979b4725136c6d587f4ceb3c..cfd2cd0b831fb23d952680c6303b6642e4f86831 100644 GIT binary patch delta 92 zcma#apCA#=$N&WQ^(M>#QcOwS?k)@rt9q4`A&^q`ba4!^=uJ*YNciEeYLj(gh2vtjUMZ8T3ju~N>{$~E bq*)l8SDL(NuoHR+RLgTe~DWM4fK$jOy diff --git a/textures/096.png b/textures/096.png index 9880561f8067f393dd4cfc6b5c7428abb3795c23..26f188f6b22a8a451e95419c2d2b620208aebe63 100644 GIT binary patch delta 94 zcmYc(oFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz qskoK=Be=eSQTpHg{+9ofdl^}SZSzg5U;P3qVDNPHb6Mw<&;$T|9~;F0 delta 81 zcmXR+nIPfA!N$PA@I7>`A&}Dcba4!^=uJ*YNchputIQmeb>YJbN9H^M$u>UM<`rUY ji`jamOtLN*m~b%MivQ1Vch@@ssExtX)z4*}Q$iB}1Fjk4 diff --git a/textures/097.png b/textures/097.png index b57b20977454cb9c7b47d858d87b5e952f282342..e21c1d7ead5bff107745328758ccadf43db8498c 100644 GIT binary patch delta 107 zcmXS@n;?#QcOwS?k)@rt9q4;u%JU?0s!N$PA@I7>`A&@fnba4!^=uJ*YNciEeYLj)LLBfScViB8Q^G6A*8N5oO wJVnhPZa73q@Y*%A#V1@oqQLc0!I*>L>3wyUgoo#50gYhrboFyt=akR{0Ac?gssI20 diff --git a/textures/098.png b/textures/098.png index 619c708672129ae14d7545a332a5839121228006..48c94f04e5ce7e2c3e84f3f6571a959875bff196 100644 GIT binary patch delta 105 zcmXS{nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4`A&|20ba4!^=uQ67&mtiqapL%5HeROYf=0zG0Yj;RRg!nu v1clE6#mt&J7*&?PX*BMbQJ^TqZ`8~n_a)|BMVN>+&#QcOwS?k)@rt9q4`A&@fjba4!^=uJ*YNciEeYLj)LLBfSc!ih~VQ#$N)+f2t0 u?!^bwFFfGRv3bQFEw-@JQRI#QcOwS?k)@rt9q48WpAraX$j8W$<+Mb6Mw<&;$Su CEg>EN delta 94 zcmb;^oFEa&!N$PA@I7>`A&|20ba4!^=uJ*YNciE;<;Ip#w3v;z!*Ry}0aK2CUX@4# w7v4ot61`F`s?8mpX)y^Lk}isJDQt`kAAiQ2W9Yde2Q-7h)78&qol`;+04;kQivR!s diff --git a/textures/101.png b/textures/101.png index c9d8dabf944731cb8dcd35854dd8635e84ad2182..604bb6c50573d02deeafc2e1dc814059bc259b7d 100644 GIT binary patch delta 108 zcmXS_pCFOW$N&WQ^(M>#QcOwS?k)@rt9q4mdKI;Vst E04uH^^8f$< delta 95 zcmb;~njjIy!N$PA@I7>`A&|24ba4!^=uJ*YNciEeYLj)LLBfSc!ih~VQ#$N)+XTlY yO^PW3uA0>!Bweyj9ZUY0VAhe9XrjTx#IU{G@ul*eW#@s0FnGH9xvX#QcOwS?k)@rt9q4`A&@fgba4!^=uJ*YNciE;@t7&&Ra=*1g~@|M%{LW-4hk4$ pd|>^y!f`PhZ+-KL1I=d?7>`jAGiPj diff --git a/textures/103.png b/textures/103.png index 5752eef02bf81e2e966ba3b04b84d79a7e078b9f..ca5242becd0c1918fa74680039174808bf467e38 100644 GIT binary patch delta 105 zcmd0)nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fmba4!^=uJ*YNciEeYLj)LLBd5#;tAWsjf~oIrWpnj sCO<9-`tmG_lHlcQ*5H}$m?OZz`MZ2`_}6bcf!Y~7UHx3vIVCg!0OT`kB$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fmba4!^=uQ67&mtiqapL%5HeROYf=0zG0Yj;RRg!nu r1clEoX6u!532yGV=%XT+!p6v8cwe2x_U{1;pmqjNS3j3^P6#QcOwS?k)@rt9q4%w@O1TaS?83{1OUC38&?1T delta 83 zcmXR(n;_xG!N$PA@I7>`A&}Dbba4!^=uJ*YNchputIQmeb>TyRVF!;*2#-`vqixoO l6^@J9cnzCR9B4kHz_5Fv|5@b-_i&&_22WQ%mvv4FO#or%8|(l8 diff --git a/textures/106.png b/textures/106.png index ad949595c7404a60522fe97c008a1cfec28c5ece..b14ec88e363f1a2b780bfa58a19a934c908cd7d5 100644 GIT binary patch delta 100 zcmc~PpCA#>$N&WQ^(M>#QcOwS?k)@rt9q4mdKI;Vst01QPOJOBUy delta 89 zcmXTVnII9u!N$PA@I7>`A&@fmba4!^=uJ*YNciE;<;Ip#}+ s1>~gNc%`pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4T=C~W19R9PLC){WADXLJ>hAoPZ?sqXP`{u~gN?zJ#inWBx}VuV^$eb_elF{r G5}E*Uvmwj? delta 96 zcmZo*ESew@#lgnF!0fA|DLgJ$>szP6GQ5(kJ<`ZbvuBDFnGH9xvX#QcOwS?k)@rt9q4lFz qskoK=Be=eyQR+v%z0g1Y;|#J1=H3pg+|7Ur7(8A5T-G@yGywotg&Cdz delta 81 zcmXR+nIPfA!N$PA@I7>`A&}Dcba4!^=uJ*YNchputIQmeb>YJb$Hi>DQYKj!I1D>@ joZA;UhHxwIl44=lb1rL>o(NAXP#c4%tDnm{r-UW|5tJFd diff --git a/textures/109.png b/textures/109.png index 32ef8086ece281673c3dbc10cd6e847832354982..00d0869b39610dd9ca54d13524c04102f72b427a 100644 GIT binary patch delta 101 zcmd0=m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fjba4!^=uJ*YNciEeYLg{!m*-bMvx4>^Rl61*B{yR~ vXLnvH7Pg0n7PIlPHFsR}QIShwV`R8Gmq%Xm?fa`h6Bs;Q{an^LB{Ts53Ar4H diff --git a/textures/110.png b/textures/110.png index d594ce4a371368521a54b515b75649a1e2fe69e6..7214cb6f36cee085ef2668190e946f0839edafde 100644 GIT binary patch delta 98 zcmc~Oogfj*$N&WQ^(M>#QcOwS?k)@rt9q4?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNciEeYLg{!m*j90ktxCy85}Sb4q9e0CS!kH~;_u diff --git a/textures/111.png b/textures/111.png index b3e350ac610a53314523a3e0f3b202a1ef160850..b67c879fdb69a50d44e00ac6550e264edd824d57 100644 GIT binary patch delta 100 zcmc~OpCA#>$N&WQ^(M>#QcOwS?k)@rt9q4?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNciEeYLj)LLBfSc!ih~VQ#$Ol8?TgU n$1678eL7CU%^F9V&nPhP@PE`kEw=e5P%DF{tDnm{r-UW|Kdc&q diff --git a/textures/112.png b/textures/112.png index 45de829e57926da4ac74e09bbcd528a6906ef06e..a511d7f35390fc6e69b75af37419352be61c4de7 100644 GIT binary patch delta 105 zcmXS}nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4`A&|22ba4!^=uJ*YNciEeYLg{!m*-bM%LLVeM#U@vL#cvQ zl6Tkyh0i))QAlDqdC}g2XOZVJS<{YRY>W)kzN)jRc8mK1O=0kK^>bP0l+XkK(JUU> diff --git a/textures/113.png b/textures/113.png index 1398fa0ce1b95d14a8bfc4263319dd4b65c53252..069c9e92481e24e8e83fd0a019ebc4a03be2643e 100644 GIT binary patch delta 105 zcmXS{nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4LsqYUC9rq*_~FCPKbGI+ZBxvX`A&|20ba4!^=uJ*YNciEeYLj)LL868GL6GqY-b2aEo?<+K x%`3#*zL<8**r1@*!?xWqgnMydb4IoRLl&nx%l|T&sX#LrJYD@<);T3K0RXDo9`kB$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fmba4!^=uJ*YNciEeYLg{!m*-bM%LLVeM#U@vL#cvQ rl6TlHs{C^2l``!x@tOF{F-L%*-ETq7=_I@BKpv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4{Nw+B24=HA4Ik}gPXBj*!2XBh&wphm;}6OPlPnn~{xVCy+PFs$sGh;o)z4*} HQ$iB}x*a2w delta 98 zcmZo*ESVq?!@#QcOwS?k)@rt9q4lFz zskoK=Be=eSQM&Fx|A8G>6dwQo>+!>0hM8f*R@3D_c{Ia;3K=|I{an^LB{Ts5mzy89 delta 86 zcmb=-oFEa%!N$PA@I7>`A&@fgba4!^=uJ*YNchputIQmeb>YJbN9H5}!-NhglOIgg pR~7^|pD-`ckaF2-uxPa-gPre!nlE!&p8@qUc)I$ztaD0e0s!0@9&i8v diff --git a/textures/117.png b/textures/117.png index 94ffc6aae5a54d348ddf224a588aaf1030f15bd4..65727e4c70f6153d4647cb74ebccab5a6f79e257 100644 GIT binary patch delta 100 zcmc~SpCA#>$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fgba4!^=uJ*YNciEeYLg{!m*-bMOUK2A;J+&z7qj(B pwOm(->FQu~KB2(%QNfslp)%{E_U6qyPXqNbc)I$ztaD0e0ssNc9_#=B diff --git a/textures/118.png b/textures/118.png index a87d7d0c91c8af6e64720be61717ddf38e88459b..c0e097163892cdf589682397b972127a1130d644 100644 GIT binary patch delta 101 zcmd0;m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4 delta 90 zcmb=(ogfj)!N$PA@I7>`A&@fhba4!^=uJ*YNciEeYLg{!m*-bMOUK2A;J+&z7qbN( tR!9;N%)FUk*1>bu`J#f+2L)pehU6OyYSNaZiURdBc)I$ztaD0e0swb-AASG; diff --git a/textures/119.png b/textures/119.png index bbe662bd57b92a0bfdfed8794d0460c53c065554..fb50b512fc7dad8245e0dc4c97014684a2c7de14 100644 GIT binary patch delta 101 zcmd0)m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4cz$WSFqaz#-T8rVmgZgQu&X%Q~loCIBnS9CiQz delta 89 zcmb=(nII9u!N$PA@I7>`A&@fmba4!^=uJ*YNciEeYLg{!m*-bMOUK2A;J+&zn-3@i s9d+kjwA^urh#+&@1V@oaj%fl6uV$&U@I~7%1!`yTboFyt=akR{09G&_iU0rr diff --git a/textures/120.png b/textures/120.png index fe425379d718a90f3125fd292df53e3142b1c4a5..f06435d072e29c032936ad939bb40b8c8c17708e 100644 GIT binary patch delta 105 zcmb=bnIMtE$N&WQ^(M>#QcOwS?k)@rt9q4`A&_$Pba4!^=uJ*YNciEeYLg{!m*-bMOUK2A;J*)yJ0x6P zqReLSDmi}+FwFSCYUau|QA*6>D!*NGM^DtJ%NH9YSr}p?7Sz#QcOwS?k)@rt9q40r)ojLF5p`}dP$h$>tDnm{r-UW|i1i)7 delta 91 zcmb=*n;;R!!N$PA@I7>`A&@flba4!^=uJ*YNciEeYLg{!m*-bMOUK2A;J+&z7qbN( uR*329V01pAu&VXq1A~r31(HYD7#a8!9baxS`ZyJ60E4HipUXO@geCyO@*zY3 diff --git a/textures/122.png b/textures/122.png index 83696c2e5dbf18f735f5e40b56ba05295f1b6586..ca469f577c000a8ff9056e9776fef332e51fa8d1 100644 GIT binary patch delta 110 zcmXSIV4NV4!N>pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4{Nw+B24=G}2Y$%+{r@iC@Ywz<$DbeeOp8~z&tPWYy=-Fea1losP(6dEtDnm{ Hr-UW|%Mc@x delta 99 zcmZo*ES(?`%fZIL!0i7#ZB$dE^g8#n}MOVeoYIb6Mw<&;$S^ CRUdr- diff --git a/textures/123.png b/textures/123.png index d4160e9933d1d19e59ed9589aa6a45665e6c6afe..1b0e62a5830dba9730bf90ce59cc7a826d28ec37 100644 GIT binary patch delta 103 zcmXS_njn$H$N&WQ^(M>#QcOwS?k)@rt9q4KmT!tgn1UJU0W~k0#!12y85}Sb4q9e0P6@L AtpET3 delta 95 zcmb;_njjIy!N$PA@I7>`A&|24ba4!^=uJ*YNciE;@t7&&Ra=*1g~@|Mi`jTP98)sn ywAgs}9gs8aFljE3>^k1GFhk(#p^kNIj0`%H7SzZ(n)Uz#QcOwS?k)@rt9q4`A&}Dbba4!^=uJ*YNchputIQmeb>YJb$Hi`kB$N&WQ^(M>#QcOwS?k)@rt9q4?Mt9K22WQ%mvv4FO#pBNAN>FT delta 93 zcmb;@m>?0s!N$PA@I7>`A&@fnba4!^=uJ*YNch2jcn8BKDVqud=@xF`%oi&hn@=ey w@yd3w@$NeyXUeggP05sBv{~aw^BDz({MZFGYmGve0F7YqboFyt=akR{0AO4l<^TWy diff --git a/textures/126.png b/textures/126.png index 523e071eb747fbedbdb3d3bc289688dc04949d89..ce04815076135fc83e3927092b7133d5d7692c59 100644 GIT binary patch delta 95 zcmc~SnjjIy$N&WQ^(M>#QcOwS?k)@rt9q4<*p~W3@W=o9RS66~ORWOcSLONwRWNwE`njxgN@xNAjYJ!G delta 86 zcmXTQoFEa%!N$PA@I7>`A&@fgba4!^=uJ*YNch2jcn5=?>4`@?&F2-8Odb>-O)%r= pXIWWsWkF!G8?TgUMuj8`!>5P-XRXZ^$O82;c)I$ztaD0e0s!vA9jE{R diff --git a/textures/127.png b/textures/127.png index fda05eedf13a927e4520ff9475359ef99e276407..40820a1b54de44bd0b6557e1d663fc24e0315a05 100644 GIT binary patch delta 105 zcmd0=nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fjba4!^=uJ*YNciEeYLj)LLAr&ftOvjPtGYSl!7(Qy>3U$l@n!w=c>gTe~DWM4f;VB`p diff --git a/textures/128.png b/textures/128.png index f0fd572e577d7b462eabc64a24dd480b15f8696a..546d6e9340aa5580aa2f0a61e93bd3fcdbe2f3cf 100644 GIT binary patch delta 106 zcmd0+ogk6Q$N&WQ^(M>#QcOwS?k)@rt9q4`A&@flba4!^=uJ*YNciEeY7@fa#CG_ALec>NqYepP7fGuh uJW@4{zQxTaJf1FQ`kB$N&WQ^(M>#QcOwS?k)@rt9q4o%^lRj2{@fT1XgQu&X%Q~loCICew9n$~+ delta 90 zcmb=(ogfj)!N$PA@I7>`A&@fhba4!^=uJ*YNciEeYBPgZiH%3Gx#N;=&%(uQ7cVNr tTs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`Evl<{fA|DLU@YW0MbS>>@yu1I`ltkub92u2WTXN Mr>mdKI;Vst01Yc8C;$Ke diff --git a/textures/131.png b/textures/131.png index 81d1e83aa53fb047b379257df0c9dc2091e5f937..713cbeb721b10acef47ef7efa4d9e77b2199843e 100644 GIT binary patch delta 92 zcma#cpCA#=$N&WQ^(M>#QcOwS?k)@rt9q40+eR(boFyt=akR{077aT82|tP delta 75 zcmd0gn;_xF!N$PA@I7>`A&^q_ba4!^=uQ6N&zO*q;P4=@*^O7qG~>#Gz~;6-#|#;- d4+_Q{3_F#gU$btW;sVsb;OXk;vd$@?2>^0%7$N`w diff --git a/textures/132.png b/textures/132.png index 9bb3a358e6dae2aa8d90da06c889c5b35ff13345..89b30bc8f9a2749e46c1450a6b482bb05abd565e 100644 GIT binary patch delta 94 zcmYc-oFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz qskoJ#knrcc1IPdW{RdQ5u`^s*sQi9z+UH9^1q`09elF{r5}E*&3mp6a delta 82 zcmXR+ogm@M!N$PA@I7>`A&}DXba4!^=uQ6N&zO*q;P4=@*^PHmn`4HI*M|h-j1R2e lRyZo(T5xc>>xxtXhSuFzlNAncF9Pag@O1TaS?83{1OSNA9qa%A diff --git a/textures/133.png b/textures/133.png index 3030f7a665155dd5c625bdcba4db05a9734f8424..69d8821b9f67eec1c0701e8db43893f180f3407f 100644 GIT binary patch delta 98 zcmc~Sogfj*$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fgba4!^=uQ6N&zO*q;P4=@*^PHmn`4HI*M|h-4vGK5 oqH&AadZk==o7v*U7EW_y(5bqb%qJG;4%Exw>FVdQ&MBb@0FBuiy8r+H diff --git a/textures/134.png b/textures/134.png index 176c2d4a439517be9410bb5b9e5c5f6c935b7a6b..749e2c345743ef72c5173c90ea33dd02842e29b5 100644 GIT binary patch delta 122 zcmeBR>}H%GQOw8y1o!nO%mGqNN#5=*3=FG!m5qQr&H|6fVg?31We{epSZZGe6x8>0 zaSW-rm7MS(IicYp`yciP5&xK(WsJYyJp57q7;}LR^A)zk{nGpg44voN@-Q=m@=7va T(EOJG)XL!L>gTe~DWM4f22Uh+ delta 119 zcmV--0EqvK0f+&R7;p#%0002@Rjn}q002)(L_t(Ijm^_B3IHGsL($y-(z*!>(PZ)q zU8=lLMU8SvTDA3gklRS0qi9f=_6&nV4vqResJ#n^0-#QcOwS?k)@rt9q4`A&|24ba4!^=uQ67&mtiq@nA+sVOwA`+X;oBg91hzu1xH5 yi`W8nd)VeXHtToyNVVKmND4BZSTRA8g+b(w3G2lfrg}g_7(8A5T-G@yGywo|LLEy0 diff --git a/textures/136.png b/textures/136.png index 59f79bb6969a3b9e3c320b926eac1f5aff9b21cf..6e3a090dd230879634c92d20efd8121b33fe2ecf 100644 GIT binary patch delta 108 zcmXS|pCFOW$N&WQ^(M>#QcOwS?k)@rt9q4zopr E00^KTDF6Tf delta 98 zcmb;~ogfj*!N$PA@I7>`A&|21ba4!^=uJ*YNch2jcnU+?!eoJ!VmwaGA8t5IVs$>D zFiBV94x6Cx)5A9u=7{n5Hg|CCKA0=u=jwb(fnkSp*#+^n_ltq%FnGH9xvXs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`Ev)bMn1 z45_%4{Nw*e24#QcOwS?k)@rt9q4c2wGc%V`SPgg&ebxsLQ0IyRX A^Z)<= delta 85 zcmb=?0r!N$PA@I7>`A&}Ddba4!^=uQ67&mtiq!I3oQ75jF_B~6Mc7anlG32b)T eg2dUdik+eU?mwMBLhL#~tqh*7elF{r5}E+(+#c`% diff --git a/textures/139.png b/textures/139.png index c3d45cf444f2508f0ee83ac4d235aae28b5e0153..0b2f6ba9543addcb7e9caed11271106f91f507e3 100644 GIT binary patch delta 100 zcmXRYpCA#>$N&WQ^(M>#QcOwS?k)@rt9q43R-iHlPgg&ebxsLQ00FTZ-2eap delta 93 zcmXRfm>?0s!N$PA@I7>`A&@fnba4!^=uJ*YNciEeYIB3{$ wI4))j%)3yp)##WZaPcW`qD_{-ULGa}ueFnAd={>L1vG-e)78&qol`;+0HyFC8UO$Q diff --git a/textures/140.png b/textures/140.png index c45070fa0cf2ad000db85d3377ebb7884d93c2f6..d20c4e68a9b8644239edfbb797225649678269e5 100644 GIT binary patch delta 115 zcmbs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`Ev)bw<5 z45_%4{Nw+B24)#^g99Ju|6(rqvqQe|slAecvn;dBX>&P;gcd1=+d*2Dk8RJ*0BU0J MboFyt=akR{04U5OKL7v# delta 103 zcmZo=tehZ`$ic?I!0s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRP}Um z45_%4{Nw+B24)^(<%jlPIsWXBZ+vR6^g-T@CGB6w0go((sVnt=`%6k{12r&sy85}S Ib4q9e0G+ZS+yDRo delta 105 zcmZof$&rhJfyL$G&i_j-4_=vYw13l~U?tDe zDJCm?Y#L$`8z(86%Pcas0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRQGgo z45_%4JR$$jd4|J0rQZs4{(rQWv47|O!2T=8pZy2r8vp-4-o~isXRQ6SYdtei3xlVt KpUXO@geCw;gC*nu delta 107 zcmZo>teqf{%E89K!04Z1jz9?g(^QlG=Pz!N`Q)wYfmDi<>8~d4-tUVz$8J3Ji9)9be8}p&bG=kHOQ` K&t;ucLK6UKnj}5| diff --git a/textures/143.png b/textures/143.png index c948e1cab84468cede74613488e94b615769413e..537ef22a2add72e5ddeb7d5e18bfb1bff81d2a93 100644 GIT binary patch delta 108 zcmb=fpCFOW$N&WQ^(M>#QcOwS?k)@rt9q4`A&_$Nba4!^=uQ67&um~|kZ{!EDvw=r$3=%Fsm*5# zIUpy)Cb+o6<22i5$CM1YGi-m4Gj^05O)%@=;a$weyTy^=RuGT;4f{-MplJ-Au6{1- HoD!M< diff --git a/textures/144.png b/textures/144.png index 7064486b0ed95878800e551eec689e65b85982c9..6b37b0319c581ddc388d6c7b3dce615dfaf438da 100644 GIT binary patch delta 94 zcmYc)oFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz oskoIq;s1~MjRMm&kMwn3;AWWpOfP&w&VI02p00i_>zopr0EQqNqW}N^ delta 79 zcmXR+njqoD!N$PA@I7>`A&}Daba4!^=uQ67&mtiq!I3oQl{>GLX~#6SiCT797ZMCI hJ}g}v*zDHi$k4WK(hOz3l5apw44$rjF6*2UngG)U8YTb$ diff --git a/textures/145.png b/textures/145.png index 0d4fe37eb76349e82f0f3eb45b9855bff3980384..f4f2e5e99ddbd0defe02d23ad5c010fdf2d068ac 100644 GIT binary patch delta 99 zcmXRYn;;R#$N&WQ^(M>#QcOwS?k)@rt9q4gTe~DWM4f_9Yx_ delta 93 zcmXRbm>?0s!N$PA@I7>`A&@fnba4!^=uJ*YNciEeYIB3mdKI;Vst0P?3H*Z=?k diff --git a/textures/146.png b/textures/146.png index ac76a294be92295875dbfd670a439e223ce24176..d8d36411de048e42d0f13f06216a83cc04428258 100644 GIT binary patch delta 105 zcmXS@nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4?0s!N$PA@I7>`A&@fnba4!^=uJ*YNciEeYIB39=yKT6KDj3r>mdKI;Vst0I)A16#xJL diff --git a/textures/147.png b/textures/147.png index b18901914d4bc1587cf4a0c6c81d6bdb652e89be..559ee31917d43e0fa2b216ad7a3ce08774ab558a 100644 GIT binary patch delta 106 zcmXS{ogk6Q$N&WQ^(M>#QcOwS?k)@rt9q4`A&|20ba4!^=uQ6N&zO*q;PBvJu0R(HpN$KzlA2CiqvH+} xe&*&5M&~OF4vODcys@o^bGoC;BgZrWhN^SB>XyZZL;=lU@O1TaS?83{1OTL|APoQj diff --git a/textures/148.png b/textures/148.png index 952a07cbcdd378a69ccfc362fb39489be4991a67..d831e2a672e68f53532e0dd85dd3c093a83a552b 100644 GIT binary patch delta 108 zcmXS|pCFOW$N&WQ^(M>#QcOwS?k)@rt9q4_65Bar|A delta 98 zcmb;~ogfj*!N$PA@I7>`A&|21ba4!^=uQ6NuX?~>2X9hB!hx>NKw}qPB`$v1W{+(N z1=22o6})-P9Tyvd|6VZZ&^X<;%+aK?Zk99)!$p-zGpc&4o&e2Z@O1TaS?83{1ORDd BBJltK diff --git a/textures/149.png b/textures/149.png index d1df1db45c4b4471129ef9bcca048b0dd7936a70..90203325778b8ffead3c86da4ce37c6156583235 100644 GIT binary patch delta 101 zcmd0;m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4=0!8qoj% delta 90 zcmb=(ogfj)!N$PA@I7>`A&@fhba4!^=uJ*YNciEeYIB39W;8$~5E3g23hvQZ8E;m0gfzVF=>MJpb|h#7jW^44$rjF6*2UngEGiAr$}s diff --git a/textures/150.png b/textures/150.png index 3a718fcdca5dcaedb99adee019bede50889a3923..faed548f0a5950a3210e716f0ead50653d3a2914 100644 GIT binary patch delta 99 zcmc~On;;R#$N&WQ^(M>#QcOwS?k)@rt9q4?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNchpu`#QcOwS?k)@rt9q4`A&}Dbba4!^=uJ*YNchpu`#QcOwS?k)@rt9q4`A&|20ba4!^=uJ*YNciEeYIB3{OuAYT9g diff --git a/textures/153.png b/textures/153.png index 2392efcc027b8e9526c1ab13556040a09ef4e72b..452538ffae357078aec0f8bbd729291cd4de9c61 100644 GIT binary patch delta 107 zcmXS{n;?#QcOwS?k)@rt9q4{J-Fg#*g-4Jt=AoeOpb~-c~=80jg&3boFyt=akR{ E0Dq<`A&|20ba4!^=uJ*YNciEeY9qpXh}Df(Du?aiMTe*_{Cv$P xF4)ZAJtVo^r6I7H?UaJiCGO0~lmk3W3^QNGoQs*RUJo>b!PC{xWt~$(69B3lALjr7 diff --git a/textures/154.png b/textures/154.png index 97e7dc3c09f32ad768ff9948da064e9fd12421ca..f476d507a9d8230da52257c88f9ddebe262c7099 100644 GIT binary patch delta 105 zcmXS@nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4=zbjZ`M?0s!N$PA@I7>`A&@fnba4!^=uQ6N&zO*q;P7B!vcRTAi3tK-EPOUDyh@@x vip^~C3H?$oHxz;nDwuc7C{PsQH)>{x5Al07A@YSR&#QcOwS?k)@rt9q4CKjlM!PC{xWt~$(69C*)9QgnM delta 92 zcmXTPpCA#=!N$PA@I7>`A&@fjba4!^=uQ6N&zO*q;PBvJuD~Xim5-Y5C#QcOwS?k)@rt9q4`A&_$Pba4!^=uJ*YNchpu`tcJF*g8 zs+$Xr8S@#m@JQ7(+GYuS6;|;(%{JdLg+=S(0XY#iMuuDSW6nM1>JtJQ#^CAd=d#Wz Gp$Py?r6c12 diff --git a/textures/157.png b/textures/157.png index 2d87000baeb4e3aa04ca9a4628b150ac838d3f71..4ca180eff947368f945a2a3a0ebe8b8308b4eedf 100644 GIT binary patch delta 108 zcmXS^pCFOW$N&WQ^(M>#QcOwS?k)@rt9q4*Z5kKj}z_U}0RpoF>0#G@Fr>mdKI;Vst E0N_I%uK)l5 delta 97 zcmb;~nII9v!N$PA@I7>`A&|26ba4!^=uJ*YNciEeYIB3`kB$N&WQ^(M>#QcOwS?k)@rt9q4`A&}DZba4!^=uJ*YNciEeYLj)LL867HLr~s{ZQ{bp6%r;t mm}aYObYqh<&A6h_WysL7(&Pn~!q%BUoeZ9?elF{r5}E*91{);+ diff --git a/textures/159.png b/textures/159.png index 057a2b540c066fad3dc3b6c03c9025bd06382a7c..a8fd738ec5003d2768b66f6d009693b2bc4f3e36 100644 GIT binary patch delta 107 zcmXS_n;?#QcOwS?k)@rt9q4YuW7~fbrH554bqD$noOpC1f{oMuAOCTN$)!e}>jIc|0#!43y85}Sb4q9e E08iB+(f|Me delta 95 zcmb;`njjIy!N$PA@I7>`A&|24ba4!^=uJ*YNciE;q0DUas;$d$hsk5Zk`>Y}TMZVi ycRbRdn04U+_nW}x4aqT5ZH#QcOwS?k)@rt9q4=UdM18Vn#swanXGzV@8!OKK$Q%hu6{1-oD!M`A&@fhba4!^=uJ*YNciEeYIB3#q#~tU&z?p00i_>zopr0R1=}5C8xG diff --git a/textures/161.png b/textures/161.png index e78fd51f92e5b84542faaef1ce67fd65fe1404bd..5652e0dfd282a5520b19f34f0d8abce72d96e608 100644 GIT binary patch delta 107 zcmd0+n;?#QcOwS?k)@rt9q4KPc(Fuu2{Yf(!o<7yG%_tZB2vp7B>FVdQ&MBb@ E09a)rm;e9( delta 91 zcmb=+n;;R!!N$PA@I7>`A&@flba4!^=uJ*YNciEeYIB3BDK<7i;j_+H u6jrrfT;a%^B4C&>LE2^OqOuE;EDW!DO;``j3@8U0z~JfX=d#Wzp$PyIP90DH diff --git a/textures/162.png b/textures/162.png index 22ab3518e1cea7f85a2707291f320935aae41f31..4275e7e995f22607448cd13145e9135d03f94bd1 100644 GIT binary patch delta 105 zcmd0+nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4`A&@flba4!^=uJ*YNciEeYIB3BDboxCX%`$9gj3P<_TQwy5*QI!0?gRgf;9y*;}9i44$rjF6*2UngH)`9HIaK diff --git a/textures/163.png b/textures/163.png index d613df15cb448bda8c08753e7e6977b6b15b2036..6ac873eb393a3307d160422c5a837a187b3cab6a 100644 GIT binary patch delta 104 zcmd0*o*#QcOwS?k)@rt9q4R delta 87 zcmb=`A&@fkba4!^=uJ*YNciEeYIB3BDbtKA3m6SM pW}LBf;Z+jlDQaF3c7@NXnc;h{32W+s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`Evl<{zopr0J-EU(EtDd diff --git a/textures/165.png b/textures/165.png index 77b91aaebad80a5e00b8b74f2326116a747080de..ff46e792e8d1eb29b69f76ebcd8bb147df370df2 100644 GIT binary patch delta 101 zcmb=Zm>`kB$N&WQ^(M>#QcOwS?k)@rt9q4F044$rjF6*2UngDPU9&i8v delta 101 zcmb=Zm>`kB!N$PA@I7>`A&|27ba4!^=uQ67&mtiq!I3oQ75jY0BN|GVHXY0nket=N y$T8)?!8C!ZLEj!2cSyLZEPrz_9VYIWCcv<7!tO-LI2#3^Q4F50elF{r5}E)mxFr?< diff --git a/textures/166.png b/textures/166.png index c9ae92083126a2ed3d89dafec243108ac34ba230..ae146ed0d55e0c7411cd6e28d4e20376aa395d44 100644 GIT binary patch delta 110 zcmXSKV4NV4!N>pv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4{3H1I0fxh${<1&#@zI^dEv#9mZ;f2!+5i996C@ZU{_0K){h5CesGh;o)z4*} HQ$iB}%8MjE delta 101 zcmZo*ET13|&%wsP!0K#2p+ zM;9EN*q4!X;Q@C}7tf*#qRlrK9GnXj5Rj~xAj!fIvQM4m2gCOWph*m#u6{1-oD!M< D+#npv_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRPl6i z45_%4JmLNS^9+aUU%h$w52i5U9-l;o@HqU(k_V= zyk^ZBB}Ws?GCr`H`SL7^IwdE=7AT?Ttm+ul!9LTGA^xX2i*!rgG@wxop00i_>zopr E08-r_mH+?% diff --git a/textures/168.png b/textures/168.png index d540e8dca0cd25294e76cd3add187b82d40ca8c8..838f1fd0ae1d6b5a96315b6e9493b203d3549009 100644 GIT binary patch delta 108 zcmb=dpCFOW$N&WQ^(M>#QcOwS?k)@rt9q4$NvjP$Vt|_JYfIB(UZc*5IxcK%Ez_-4M61#p00i_>zopr E0BuJiH2?qr delta 102 zcmb=goFI|N!N$PA@I7>`A&_$Lba4!^=uJ*YNchputIQmeb>TyRVF!;*2#-`vqwhq= zlnl8uY=JMj9;OKReQiFk5Ytt_IO+1m2L>HZHYa$P7?e5|)J(GcV+%Bk!PC{xWt~$( F695^2B*_2( diff --git a/textures/169.png b/textures/169.png index 87b131e191f478e3c06dcdad495fde7953eb89c5..53b01af65290386e5cef4d7083aee59f19c39c70 100644 GIT binary patch delta 113 zcmbs0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRQGgo z45_%4oRF{~y&+N3j^*F|h65+EB)9}3{>d{Ln=2SFKdzTxW6(Mv9KOrv1_w|JgQu&X J%Q~loCIEf|ABq3~ delta 108 zcmZo>teYT_#=*wG!0lFzvFJ_y;jenYUiOC2wc^+-qA(1`(Unsp=aY09U}`KCDWL;6^<^MZhV^^(*+oMH#QcOwS?k)@rt9q4iWody{an^LB{Ts57fKz~ delta 80 zcmXR-o*?1P!N$PA@I7>`A&}DYba4!^=uJ*YNciEeYLj(gh2xaR|3w-c^8{8V8fIMx iFyY{rGwq0DV`O+WFKd(0{VUo)T@0SCelF{r5}E)hWE&y? diff --git a/textures/171.png b/textures/171.png index 73648390891d6323bf6dbcf19e8186ef6e562341..c5ba9ab3ccb4c81fff77d1cd637a987ee2bc0d7b 100644 GIT binary patch delta 102 zcmd0(oFI|N$N&WQ^(M>#QcOwS?k)@rt9q4lFz zskoK=bP0l+XkKis2s{ delta 85 zcmb=-m>?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNciEeYIB3#QcOwS?k)@rt9q4`A&@fhba4!^=uJ*YNciEeYIB3mdKI;Vst0QY$uaR2}S diff --git a/textures/173.png b/textures/173.png index 6c638b912d556a0277cd0314d2884646925dff9d..6d19056c9366c38446f2d712b610813cdf04c0f9 100644 GIT binary patch delta 104 zcmd0=o*#QcOwS?k)@rt9q4`A&@fjba4!^=uJ*YNciEeYIB3il5@<9dj uju|30Sr-zFJ3BzvGFR7r=5TXV&fx*+&&t;ucLK6V&4jRJ% diff --git a/textures/174.png b/textures/174.png index c60fee1d191632f208481c33af748cb7aa2562ae..7f225b48d97c06c751928b79343170ed2588aa3d 100644 GIT binary patch delta 106 zcmd0=ogk6Q$N&WQ^(M>#QcOwS?k)@rt9q4gTe~DWM4f DJ2oMP delta 92 zcmb=;pCA#=!N$PA@I7>`A&@fjba4!^=uJ*YNciEeY7@fa#CD{Ezo_}d1)CYXi!O*Z v-&}A|{Dz5PN0w%FfwT*ooM}fK8zX~J)$T;~P@}&<6Bs;Q{an^LB{Ts5e@Y&O diff --git a/textures/175.png b/textures/175.png index 20b4017f4fa9acc37fb9eab826d91ded49e963ee..cffd6f45a3790074a5ef6e131a11d0c120594b15 100644 GIT binary patch delta 111 zcmXSEWSk(8$;bc%_w^>s0a8p!-tI0846AyTjetDP0*}aI1_nK45N51cYF`EvRP}Um z45_%4{Nw+B24;*~wa#=GypvJ(niv4%5vm4Ys=aBM!Qz+k&|cVhF2Pl`Ze7(8A5T-G@yGywo{ Cry~pi diff --git a/textures/176.png b/textures/176.png index 6284767ccbc8978a294f772a3b443e2516da1f24..45c51d0456ba241f67729ac388a58d4b215982b0 100644 GIT binary patch delta 93 zcmYc*m>?0s$N&WQ^(M>#QcOwS?k)@rt9q4u;Y!pKu)mK$_Q+6pMo;OXk;vd$@?2>@O$8ifD= delta 83 zcmXR&n;_xG!N$PA@I7>`A&}Dbba4!^=uQ67&mtiq!I3oQ75jY0l#Bzd?6V!4^*6XD l=bBt&3r*-hcFUvBltGREqxL3=3)w)844$rjF6*2UngCh98`=N> diff --git a/textures/177.png b/textures/177.png index ade87ac6bb7617826d5e717dee9ab7a106d9d657..7f6726fc0a06698398dd12a172d580fc568b1e15 100644 GIT binary patch delta 94 zcmc~SoFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz qskoIqLDS{{!{PqlZywI@dc-hKN%?N|UYiu40tQc4KbLh*2~7ZlMH}Y; delta 86 zcmXTSoFEa%!N$PA@I7>`A&@fgba4!^=uQ67&mtiq@nA+sVcWrU0o7R-kFd{n6xo$z ma?LF{7fgI$HQUO=#BfH&g!Sm9ZL@)T89ZJ6T-G@yGywqO^c~~? diff --git a/textures/178.png b/textures/178.png index 195d719fba85bffa7ddce809045b662ffb9fecac..e7b1a564c4af5ce14937c95b100df6d219c8a4ad 100644 GIT binary patch delta 94 zcmXRaoFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz qskoIqA<^al!(krQZv{T8g$%Vfg(f-%{Wu6zz~JfX=d#Wzp$Py{HX0KE delta 95 zcmXRcnjjIy!N$PA@I7>`A&|24ba4!^=uQ67&mtiq@nD8T!zopr0R2fJBme*a diff --git a/textures/179.png b/textures/179.png index 44f809c6e41b485dc1b08b76ae94af141b83bf3a..6055ebe102efb63a9acc6871332071833eceaf13 100644 GIT binary patch delta 88 zcma#eo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4R9R)z4*}Q$iB}n++Cb delta 74 zcmd0fogm@L!N$PA@I7>`A&^q>ba4!^=uJ*YNchputIQmeb>YJb$Hi>DQYKj!Kn&h3 ajtmA0t?0s$N&WQ^(M>#QcOwS?k)@rt9q4@Yp8(#nb delta 81 zcmXR&nIPfA!N$PA@I7>`A&}Dcba4!^=uJ*YNchputIQmeb>YJb$Hi=cc^B%H4hWcL hd|>?s;`4epGZZ)MPFx=S`8-e?gQu&X%Q~loCIDd_9U=e# diff --git a/textures/181.png b/textures/181.png index ca5a3d9f3a0537682b2bcb8cbe676903e84f469e..ba18cfb9e8f9908cdd84eb0c63c31c4a11be46d3 100644 GIT binary patch delta 94 zcmYc*oFEa&$N&WQ^(M>#QcOwS?k)@rt9q4lFz qskoK=Be=eSQ98}x(2`A&}Dbba4!^=uJ*YNchputIQmeb>YJb#}eZg_JRo=QYJr` ls!gu7B_;GrnM~qkV%YZJqqeU8uk%2S44$rjF6*2UngD~z9O(c6 diff --git a/textures/182.png b/textures/182.png index 20bffbdd38f28f650962d26d40ea90a159087704..8602b28429e7e0b9b269bfd369de5d24e215467f 100644 GIT binary patch delta 99 zcmYc(n;;R#$N&WQ^(M>#QcOwS?k)@rt9q4`A&}Dcba4!^=uJ*YNch2j_(X$5p@~h_g%yrdq`%0sOR%x= k?mHl7nsH@8Vx|DYC7$xlOOmf|18QUNboFyt=akR{06s+<*#H0l diff --git a/textures/183.png b/textures/183.png index a49939be918d1a8e0519d8025310a80e124b6e39..d0ddb8141ef71b70e7eba55f8df9c1e7bb8bcebe 100644 GIT binary patch delta 105 zcmYeSnIMtE$N&WQ^(M>#QcOwS?k)@rt9q4`A&}DWba4!^=uJ*YNciEeYLj(gh2xaR|5ch4Qv{6Ir?&+* gyYWhyb|f`3lrR0Lo!u|n3Dm>j>FVdQ&MBb@0O__G7ytkO diff --git a/textures/184.png b/textures/184.png index cdfe098e6b57a86fd36343a36cd14a1d68ddb020..3a75810712779cea730065423ea66fd298042b2d 100644 GIT binary patch delta 100 zcmYc*pCA#>$N&WQ^(M>#QcOwS?k)@rt9q4`A&}Dbba4!^=uJ*YNciEeYLg{!m*#QcOwS?k)@rt9q4r8l3=)&qkpuzpida6h{4m<&t;ucLK6V`A&@fiba4!^=uJ*YNch2j_(X$5p@~hFz+IkS{meYf%^y5? rN}Ji@6N1k!I5^$ajaTX<8zY0rv+~U{xBu$^bu)On`njxgN@xNA4FVny diff --git a/textures/186.png b/textures/186.png index eab73eb059b55d7ff35796dd2485f3568a44b6da..f249e107f47dc3fffff318d829bd88115ae9cbfb 100644 GIT binary patch delta 88 zcma#eo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4`A&^q>ba4!^=uJ*YNch2j_(X$5p@~h_g%ysA*?Of+Kn&(w a0S3N*G3SCOn(YLtXYh3Ob6Mw<&;$T_nHbCf diff --git a/textures/187.png b/textures/187.png index edd8abf84377dd539f7a823011b85af88a6c57ad..f240b778db6751dc1d4c5f8ae283d2104043627f 100644 GIT binary patch delta 100 zcmYc$N&WQ^(M>#QcOwS?k)@rt9q4mdKI;Vst07I`Gy#N3J delta 84 zcmXR`A&}DZba4!^=uJ*YNciEeYLg{!m*`kB$N&WQ^(M>#QcOwS?k)@rt9q4yx+>jO|7gQu&X%Q~loCIC6N9j^cY delta 93 zcmb;@m>?0s!N$PA@I7>`A&@fnba4!^=uJ*YNch2j_(X$5p@~hFz+IkS{meYf%^y5? vN}CH>1q~C;Pw*iFr%+@RAV#vlIvHzp?sxr<>pb-q7u6{1-oD!M#QcOwS?k)@rt9q4`A&}Dbba4!^=uJ*YNch2j_(X$5p@~h_g%ysA*&Z6cu#QcOwS?k)@rt9q4FVdQ&MBb@0I9nh)&Kwi delta 85 zcmXTUm>?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNchputIQmeb>YJb#}eZg_JRo=QYJr` ms!gt);8!*R3NB^~>|vDNv7qKkr~6N!Rt8U3KbLh*2~7agh8}DH diff --git a/textures/191.png b/textures/191.png index ff95b25cedd46574a1316d3e124b14913536353a..d9cd0d8a7ead5671990472f3a5d8509a6fda54ee 100644 GIT binary patch delta 103 zcmYeOnjn$H$N&WQ^(M>#QcOwS?k)@rt9q4%Yej21OnQ}W7!N$PA@I7>`A&^q{ba4!^=uJ*YNciEeYLj(gh2xaR|6Ljsvjp@y1DoA= frA#|ku`x0%@66g{bZGrApcV#CS3j3^P6#QcOwS?k)@rt9q4mdKI;Vst0N1A-@&Et; delta 80 zcmXR;o*?1P!N$PA@I7>`A&}DYba4!^=uJ*YNchputIQmeb>YJb$HimdKI;Vst06;h#8vp#QcOwS?k)@rt9q4`A&@fkba4!^=uJ*YNchputIQmeb>YJb$Hi=cc^B%n8XYqP qCNg@~CG<;~WL;R{$iu_)pYiC51vR_758VT5X7F_Nb6Mw<&;$SnIUg(l diff --git a/textures/194.png b/textures/194.png index 76efb9473864ee2c8c15cdccdd074405cbcade57..10d4e88819723d2349fb05307533936893ba8a0b 100644 GIT binary patch delta 97 zcmYc*nII9v$N&WQ^(M>#QcOwS?k)@rt9q4`A&}Dbba4!^=uJ*YNciEeYLj(gh2xaR|3jFXFDS&+xdpq_ jCG<;~fJAwCc>XhBQ^U7P$Pq#QcOwS?k)@rt9q4`A&}DYba4!^=uJ*YNchputIQmeb>YJb$Hi?0s$N&WQ^(M>#QcOwS?k)@rt9q4`A&^q@ba4!^=uJ*YNciEeYLj(gh2xaR|3jFXFDy7Heq%9P fuat=nFB8Mhjs9m_GXtIjbuf6k`njxgN@xNA>B1Xb diff --git a/textures/197.png b/textures/197.png index c7806ba095b5ba81409daecc26c071ffc24503ab..63e28ac329cafa82c37c746e26856614acf11186 100644 GIT binary patch delta 93 zcmYc-m>?0s$N&WQ^(M>#QcOwS?k)@rt9q4`A&}DXba4!^=uJ*YNchputIQmeb>YJb$Hi=cc^B%n8XYq( kJm7v4*zCqDHI0ptVYWDr{B!>IEkJz?p00i_>zopr0AH&ciU0rr diff --git a/textures/198.png b/textures/198.png index 40d16e75343d65153e5c6891148326a7493923ef..2ec6225bae97de80cb5030bc7eda36cffb2f0101 100644 GIT binary patch delta 95 zcmYc-njjIy$N&WQ^(M>#QcOwS?k)@rt9q4`A&}DXba4!^=uJ*YNchputIQmeb>YJb$L3QCNe2W>Gd{3> l%UHvE^njdc#ttbKhI&7BmJbu+b^!G;c)I$ztaD0e0swpJ9HIaK diff --git a/textures/199.png b/textures/199.png index de5d2572f78baa4375b2e6765fdd71ed41dd80ea..c6e2c339e062e9134843f7ea61205d7a95b156fd 100644 GIT binary patch delta 96 zcmYc;o*)s;$N&WQ^(M>#QcOwS?k)@rt9q4FVdQ&MBb@0Gl@(MgRZ+ delta 80 zcmXR;o*?1P!N$PA@I7>`A&}DYba4!^=uJ*YNch2j_(X$5p@~h_g%ysA*#a*r#3V>a jnEYU>zOvw9iU5OY#-th7FLx9JbuoCl`njxgN@xNAKJgrD diff --git a/textures/200.png b/textures/200.png index 3bd81bb7e116ab401f8620ff608206ab89d99e0d..87daf1e5d6a075c41f7043f24c6324a53913274c 100644 GIT binary patch delta 105 zcmd0(nIMtE$N&WQ^(M>#QcOwS?k)@rt9q4{Yz#(PmRmDRyE}kt89ZJ6T-G@yGywnt C{2=E5 delta 85 zcmb=)m>?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNch2j_(X$5p@~h_g$1S@;u4G41UuWi n9G5gX<_WY`Tv-s#QcOwS?k)@rt9q4zopr0MqUu ABLDyZ delta 83 zcmb`A&}Dbba4!^=uJ*YNciEeYLj)LLBfSo!ig#QcOwS?k)@rt9q4lFz yskoI~ai_k4Q93Ozq2clW|NI9GjU2>|H!_+$)Su^RWG@6%$l&Sf=d#Wzp$PzA+Z|#6 delta 97 zcmb;{nII9v!N$PA@I7>`A&|26ba4!^=uJ*YNch2j_(X$5p@~hFz+IkS{mc)HIF|o9 zm@Y8yfU+pRS#t*$gBMfv10JcGM%%0l9%dX2<FVdQ&MBb@08(5b AWB>pF diff --git a/textures/203.png b/textures/203.png index 09cfee49cbfde22e8460da33665d03dc77d54e97..61a0742ebf309c580e2f5f0a09e00f98cf22bef2 100644 GIT binary patch delta 101 zcmd0+m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4`A&@flba4!^=uJ*YNciEeYLg{!m*`kB$N&WQ^(M>#QcOwS?k)@rt9q42WM<3|f;7)#D~*$pF`A&@fgba4!^=uJ*YNch2j_(X$5p@~h_g$1S@;u4G41UuUo pIX3HW$V|HM;Iz_WHeU5+hCJ1^wX36UM+5aTc)I$ztaD0e0sz_!9lihn diff --git a/textures/205.png b/textures/205.png index 2823919c28e45a0c4ab9bbf96537265087d6c99c..6aa7de494e1b45c9682bfc033914cd8c4dfdf3cd 100644 GIT binary patch delta 93 zcmYc;m>?0s$N&WQ^(M>#QcOwS?k)@rt9q4`A&}DYba4!^=uJ*YNciEeYLg{!m*i|D*Pf4(GW*T@0SCelF{r5}E+SW*E)@ diff --git a/textures/206.png b/textures/206.png index 8cdcd81ea1d4571382bb0a8d89ba5d50e0e68ad0..17c0bc5024f3890eaf0a524b1ef5def68e0eec09 100644 GIT binary patch delta 103 zcmXS_njn$H$N&WQ^(M>#QcOwS?k)@rt9q4`A&|24ba4!^=uJ*YNch2j_(X$5p@~hFz+IkS{mc)HIF|o9 ym@Y8yfU+pRSu#QcOwS?k)@rt9q4Un!eJNW_?F?hQAxvX`A&@flba4!^=uJ*YNchputIQmeb>YJb#}eZg_EQfim~@%|Ag86Q}`t#DMnslcF{r_QqS)9Y5C0Sun5elF{r5}E+u-y&ZC diff --git a/textures/208.png b/textures/208.png index 9e0928a3798e665987b4d307a877b6bef8c0fe2d..dcdbc817bfdbb035a6a3d582737c09f352cd66b2 100644 GIT binary patch delta 95 zcmYc#QcOwS?k)@rt9q4`A&}DZba4!^=uJ*YNch2j_(X$5p@~h_g%yrdq`%DfN)V80 nQUBm^bTM16lu4FA9uLFB`5(32jtN!(buxIm`njxgN@xNAukRe# diff --git a/textures/209.png b/textures/209.png index 4b55a387e9438b7c9de901a240fcc33d5677e44d..51c534a42ea287910b788a41515a527974e86aee 100644 GIT binary patch delta 97 zcmc~SnII9v$N&WQ^(M>#QcOwS?k)@rt9q4JJ{72j!PC{xWt~$(69B;08`A&@fgba4!^=uJ*YNciEeYLg{!m*mdKI;Vst0Fgr*m;e9( diff --git a/textures/210.png b/textures/210.png index 6e26c80c47ac01df1b6227361b7875388da49d34..88fe76b230d8e036346fc73fa3f4eb74b7e0e244 100644 GIT binary patch delta 95 zcmYc*njjIy$N&WQ^(M>#QcOwS?k)@rt9q4k-ulKeBRWNwE`njxgN@xNAOx_rJ delta 83 zcmXR)n;_xG!N$PA@I7>`A&}Dbba4!^=uJ*YNciEeYLj(gh2xaR|3jFXFDRtcFY*)+ mIWBo)FW8^e-*b(Vr_2RVTn89ZJ6T-G@yGywpNz8mlW diff --git a/textures/211.png b/textures/211.png index 5770e14ad5a4d49aa2433b7521b8e7996b6c72a5..ebbf4cc749896743241382a9357e7c2d14e4b841 100644 GIT binary patch delta 98 zcmYc;ogfj*$N&WQ^(M>#QcOwS?k)@rt9q4`A&}DYba4!^=uJ*YNch2j_(X$5p@~h_g%yrlj5|UMPVg=g g=$A49@pl+9oNKz~7S6w9Hc%IXr>mdKI;Vst07H=+EC2ui diff --git a/textures/212.png b/textures/212.png index 5ea37a399ce8c15b3c568e0f09d6dcc6b674e891..b86bd67c43d407c8fc139801513636d1aa9c8196 100644 GIT binary patch delta 98 zcmc~Oogfj*$N&WQ^(M>#QcOwS?k)@rt9q4?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNchputIQmeb>YJb$L3QCNe2W>Gd{3> o%UC1$wEolKj4KNQn>`LNJQ2UmdKI;Vst04~NL=Kufz diff --git a/textures/213.png b/textures/213.png index d2cefc9e8fa0f84a7c401cc9d1cfb4abc2d26d0c..6960eb17459e6356af40e1eb3c435e2ff1f3c44f 100644 GIT binary patch delta 100 zcmYc-pCA#>$N&WQ^(M>#QcOwS?k)@rt9q4o!I)78&qol`;+040_k82|tP delta 82 zcmXR`A&}DXba4!^=uJ*YNciEeYLj)LLE1%2Vi8-QZV%gh$7cNv kE7LAK;C>U>+_u`0;S2vq?dp&9r9gcQp00i_>zopr0A7S03IG5A diff --git a/textures/214.png b/textures/214.png index fb274a8477dbd896792f3974a0f265a760560aed..49a44f08fa82bdc6e4c8c106303098bafe4cc5c2 100644 GIT binary patch delta 98 zcmYc(ogfj*$N&WQ^(M>#QcOwS?k)@rt9q4`A&}Dcba4!^=uJ*YNciEeYLj(gh2s^|4k3dU9woOqD-5Je jGp;NMY?hFa_{Yrfu!u+AZRO&4pf(0iS3j3^P6?0s$N&WQ^(M>#QcOwS?k)@rt9q4`A&}Dcba4!^=uJ*YNch2j_(X$5p@~h_g%yrdq`%DfN)V7T k`N33uWkFzb+fqjco7xjeUq5tw0BU3KboFyt=akR{0E1v2X#fBK diff --git a/textures/216.png b/textures/216.png index f907f59c14e12353a58c810b0628e4def88e8221..8c7a663601abd50158b4d204713aa9e9f8c4853f 100644 GIT binary patch delta 99 zcmc~On;;R#$N&WQ^(M>#QcOwS?k)@rt9q4gTe~DWM4f_2e8k delta 85 zcmXTRm>?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNchputIQmeb>YJb#}eZg_EQfim}Gol m{g$yt&#QcOwS?k)@rt9q4lFz qskoK=Be=eyQ6SCX(EtCFTNoMjEOz#tQF#qiz~JfX=d#Wzp$PzbeH)qp delta 82 zcmXR+ogm@M!N$PA@I7>`A&}DXba4!^=uJ*YNchputIQmeb>YJb$Hi=cc^B%H4hWcb kc(^WR>y#QcOwS?k)@rt9q4`A&^q@ba4!^=uJ*YNciEeYLj(gh2vtjiHQO$6FQ_^wi-y8 eW?WHVl3;M@$l4?(^*ITsgTd3)&t;ucLK6UoLl^h} diff --git a/textures/219.png b/textures/219.png index 71506ad4d8c0727b2fe80ab0c1909264903c7292..4b4df4f2edacc72fdd78d4e0492f5540e94be3e0 100644 GIT binary patch delta 88 zcmWH`o*)s-$N&WQ^(M>#QcOwS?k)@rt9q4GSezm}V50i_r`UHx3vIVCg!0J*Xk?EnA( delta 67 zcmc}}n;>Dw!N$PA@I7>`A&`>wba4!^=uQ6NuX?~B>%t1h#caJ&CLjiLz5s(e>(1GC T|H$?M6*73b`njxgN@xNA#iJGl diff --git a/textures/220.png b/textures/220.png index db6eb8f17a046748594d9edf8da51191213bba99..9970e9224c38028cb2eb3892d0601628c450895a 100644 GIT binary patch delta 90 zcma#aogfj)$N&WQ^(M>#QcOwS?k)@rt9q4`A&^q`ba4!^=uJ*YNciEeYLj(gh2vtjhX?#QcOwS?k)@rt9q4`A&^q^ba4!^=uQ6N&ytXkpwQ#aD`lE-WkF!G8;J3Mmx;m9 W&V;o$%~=bmn8DN4&t;ucLK6VQa1*cq diff --git a/textures/222.png b/textures/222.png index 69364bba3d6294d857e52b23aee489e1f478a333..601540cfb517709004f0b3c3022178beba4b0531 100644 GIT binary patch delta 88 zcma#fo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4`A&^q?ba4!^=uJ*YNciE;Ar W(}XqGpS1<3n!(f6&t;ucLK6VE<`W(O diff --git a/textures/223.png b/textures/223.png index e260734bd8222dff5e7d2bcc5fd9c6574c1c7d15..5367d8dd5336174b59fe3b38245682df96b129bb 100644 GIT binary patch delta 93 zcmazmm>?0s$N&WQ^(M>#QcOwS?k)@rt9q4`A&^q_ba4!^=uQ6NuX?~B>%t1h#cU5vI~p4w+E4ue;`B#QcOwS?k)@rt9q4lFz qskoIqLDS}dgVaBNf&c&ck2B~bsx`=*c*_A)z~JfX=d#Wzp$Py|)EWQ) delta 78 zcmXR+oFL)J!N$PA@I7>`A&}DWba4!^=uQ67&mtiq@nA+sVOwCc8?TgU#+3zu&24>- g88TiU6pT3-Vp1Qw=d12g2kK$)boFyt=akR{0K4`W>Hq)$ diff --git a/textures/225.png b/textures/225.png index 6ac843a07ebfc5cc1fef1feb60503a2cbd30f4db..7d8796c6dc1e364336e7a30ebe0163bc76d10664 100644 GIT binary patch delta 101 zcmd0`kB$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fiba4!^=uJ*YNciEeYBPh^iEW3;gFxdOJiq#xA6&A@ rx{zRy@nPxWz-BkzMSOB8Y>W&7hhxqqJ~R0V)Xm`O>gTe~DWM4fF~lDu diff --git a/textures/226.png b/textures/226.png index 6d1f764236d937159bef16b6676281b8f3c38570..d43744a686e60c42a8c613bc44608ec2acd47a6d 100644 GIT binary patch delta 108 zcmXS^pCFOW$N&WQ^(M>#QcOwS?k)@rt9q4#SoKQXEylz~7O0%T)78&qol`;+ E02?`A&|26ba4!^=uJ*YNciEeY7@fa#O8cu!GDnk#S{URn^DtB}(_s0*zttboFyt=akR{00s^r ASO5S3 diff --git a/textures/227.png b/textures/227.png index 13e983265d32a2d9ce69eafa8e0346d898e31b82..06cfb4abc25ae419c6275a1846d719cd7499b8a6 100644 GIT binary patch delta 98 zcmYc&ogfj*$N&WQ^(M>#QcOwS?k)@rt9q4}W7!N$PA@I7>`A&^q{ba4!^=uJ*YNciEeY7@fa#O8cu!GDp4z-BjIDbtKA f3lu}sCU-Hq@?@U3kBJTfYGLqn^>bP0l+XkKlC&5& diff --git a/textures/228.png b/textures/228.png index 7ab314375859a6a9df86fc633d00815df949d8e0..70af135032355e8805f81123b5b15364f8c45239 100644 GIT binary patch delta 100 zcmYc$N&WQ^(M>#QcOwS?k)@rt9q4R delta 84 zcmXR`A&}DZba4!^=uJ*YNciEeYLj(gh2xaR|1Ay}7;wyV){-#I n*dW=WJ*$0%qsSx2Gy#U9S0A+*s(#J{>SXYA^>bP0l+XkKxdR=i diff --git a/textures/229.png b/textures/229.png index b57792940be7c4d43944065c42e32d580c1c3fed..2330c9e175838980db65cf4bd2e4084cb4ac41fc 100644 GIT binary patch delta 104 zcmYeRo*#QcOwS?k)@rt9q4%5#qAO|!`A&}Dbba4!^=uJ*YNciEeYLj(gh2xaR|3w-Co7qk&q@3e0 m#QcOwS?k)@rt9q4bP0l+XkKG(8)4 delta 86 zcmb=*oFEa%!N$PA@I7>`A&@fgba4!^=uJ*YNciEeYIB3#QcOwS?k)@rt9q4`A&@fiba4!^=uJ*YNciEeYLj(gh2s@d4si)KHo;^KIc_&z qse#QcOwS?k)@rt9q4`A&@fkba4!^=uJ*YNciEeYIB3#QcOwS?k)@rt9q4h{{3%HX83HS(&73u%@e4G!PC{xWt~$(69Dc>9hU$A delta 90 zcmXTPogfj)!N$PA@I7>`A&@fhba4!^=uQ6N&zO*q;PBvJuD~Xim5&y)@lJ7E(xjMj t;Q{xXz-A6<7cGe=YzqrD4mO`rU^wg2vgK>}J~g0z22WQ%mvv4FO#qNsAk_c> diff --git a/textures/234.png b/textures/234.png index ca46b047d9508488e01856392eb2f619a77a985b..ca6025fbbf88eca6e7be8fa106b7805161f0b232 100644 GIT binary patch delta 91 zcma#Zn;;R!$N&WQ^(M>#QcOwS?k)@rt9q4?CD<4e*E;nb|Er-5lx6UA^>bP0l+XkKBG4KW delta 69 zcmd0cm>}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0Mfw~5dZ)H diff --git a/textures/235.png b/textures/235.png index ca46b047d9508488e01856392eb2f619a77a985b..4b4df4f2edacc72fdd78d4e0492f5540e94be3e0 100644 GIT binary patch delta 88 zcma#Zo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4GSezm}V50i_r`UHx3vIVCg!0J>rq?*IS* delta 69 zcmd0fm>}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0MYUl4gdfE diff --git a/textures/236.png b/textures/236.png index ca46b047d9508488e01856392eb2f619a77a985b..4b4df4f2edacc72fdd78d4e0492f5540e94be3e0 100644 GIT binary patch delta 88 zcma#Zo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4GSezm}V50i_r`UHx3vIVCg!0J>rq?*IS* delta 69 zcmd0fm>}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0MYUl4gdfE diff --git a/textures/237.png b/textures/237.png index ca46b047d9508488e01856392eb2f619a77a985b..4b4df4f2edacc72fdd78d4e0492f5540e94be3e0 100644 GIT binary patch delta 88 zcma#Zo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4GSezm}V50i_r`UHx3vIVCg!0J>rq?*IS* delta 69 zcmd0fm>}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0MYUl4gdfE diff --git a/textures/238.png b/textures/238.png index ca46b047d9508488e01856392eb2f619a77a985b..4b4df4f2edacc72fdd78d4e0492f5540e94be3e0 100644 GIT binary patch delta 88 zcma#Zo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4GSezm}V50i_r`UHx3vIVCg!0J>rq?*IS* delta 69 zcmd0fm>}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0MYUl4gdfE diff --git a/textures/239.png b/textures/239.png index ca46b047d9508488e01856392eb2f619a77a985b..4b4df4f2edacc72fdd78d4e0492f5540e94be3e0 100644 GIT binary patch delta 88 zcma#Zo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4GSezm}V50i_r`UHx3vIVCg!0J>rq?*IS* delta 69 zcmd0fm>}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0MYUl4gdfE diff --git a/textures/240.png b/textures/240.png index c87aef5386e80dc5d2e93007288cfa631cbb550f..32730c00818cfaf899853fd1637b03c417298b06 100644 GIT binary patch delta 95 zcmc~OnjjIy$N&WQ^(M>#QcOwS?k)@rt9q4?0r!N$PA@I7>`A&}Ddba4!^=uJ*YNciEeYIB3FVdQ&MBb@0Iy&jh5!Hn diff --git a/textures/241.png b/textures/241.png index 90f4552fbde43bc764de043319b1e52982a4e0a5..51bcea3a454b6225ae452500d62b47070b5e5f9b 100644 GIT binary patch delta 101 zcmXS@m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4C-7|#0X?_a+9#{!@_22WQ%mvv4FO#ojGABX?| delta 93 zcmb;@m>?0s!N$PA@I7>`A&@fnba4!^=uJ*YNchputIQmeb>YJb#}eZg_N)mLq)mP> wRbNr~#H+;2Z`QnG+9vk-j?MZTR;CIt%&J#s`KBtY3p9ek)78&qol`;+0JKvdIsgCw diff --git a/textures/242.png b/textures/242.png index fa251ae62e72003f290ff11ceacc991fc3f9be55..a04accd983c2af5d732c3ba1cd953f761efa7bb8 100644 GIT binary patch delta 107 zcmXS`n;?#QcOwS?k)@rt9q4`A&|25ba4!^=uQ67&um~|FrkfozT*iQE?(I)Y=Ids63j9_ zu$sBD1-|Gym?I!5)z;-`@pul0!3kcennqvUW`=c-9AEBPKU*AV5QC?ypUXO@geCw@ Cu_5&U diff --git a/textures/243.png b/textures/243.png index 886fbfadf92ba2ad69a25fdd3fa6fe3ae4c4b9c4..534e454078c130540edb0e2bc50d62692c5bbd66 100644 GIT binary patch delta 108 zcmb=dpCFOW$N&WQ^(M>#QcOwS?k)@rt9q4ejk_5kTb(p00i_>zopr E04zozkN^Mx delta 102 zcmb=goFI|N!N$PA@I7>`A&_$Lba4!^=uJ*YNchpu`#QcOwS?k)@rt9q4}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0Mr^4761SM diff --git a/textures/245.png b/textures/245.png index ca46b047d9508488e01856392eb2f619a77a985b..4b4df4f2edacc72fdd78d4e0492f5540e94be3e0 100644 GIT binary patch delta 88 zcma#Zo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4GSezm}V50i_r`UHx3vIVCg!0J>rq?*IS* delta 69 zcmd0fm>}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0MYUl4gdfE diff --git a/textures/246.png b/textures/246.png index 92a1c85cabfc66057734a9b99011798d9a9e7035..c8380f5c6814a0662aee014571c7b44f84dd60df 100644 GIT binary patch delta 101 zcmd0)m>`kB$N&WQ^(M>#QcOwS?k)@rt9q4Zt6oOmLi)CyF`;OXk;vd$@?2>>QS98&-Q delta 89 zcmb=(nII9u!N$PA@I7>`A&@fmba4!^=uJ*YNciEeY7@fa#CG`1f`j5WR385qX>iQD s@PIpKmUN4{PTMZWX8jHByiLgr|7T`x+7TXO1Juso>FVdQ&MBb@0B>I)N&o-= diff --git a/textures/247.png b/textures/247.png index 9834a29bd285394a70845aa656bd718079a99288..ab3839ed44e1905a2f8b8e9543d99c221bbf7fa5 100644 GIT binary patch delta 99 zcmXRcn;;R#$N&WQ^(M>#QcOwS?k)@rt9q4`A&|20ba4!^=uJ*YNciEeYBPiP5Np9o$tP@riy8a1*)}^i lTg&#UdAvd4OSOnHGZ^!K)OJ0`l?*h4!PC{xWt~$(697`29D4u& diff --git a/textures/248.png b/textures/248.png index 50a4f0fa159feba3acffff081c4374026407f7f3..17a3d9fb99c5d12394ba63888f9c718a0cac848e 100644 GIT binary patch delta 97 zcmc~QnII9v$N&WQ^(M>#QcOwS?k)@rt9q4`A&@fkba4!^=uJ*YNch2j_y|MF*Nln{k}f@Li+E&BJNnpo p_m#K^H)|YO%+@Pqk|mJG!|=&ookdH=ek)KjgQu&X%Q~loCIG(<8#4d^ diff --git a/textures/249.png b/textures/249.png index 47280858cc022a0db31e750344a26676ab2266e3..c4cc005301c2dd6e7512bbddbfd41f397c25d6a9 100644 GIT binary patch delta 99 zcmazmn;;R#$N&WQ^(M>#QcOwS?k)@rt9q4`A&^q_ba4!^=uJ*YNciEeYLj(gh2s`u4si+73#QcOwS?k)@rt9q4lFz qskoK=`A&^q_ba4!^=uJ*YNciEeYLj(gh2vtjiHQPw20T1cHI24e d7d#9&7@D3x(Yo}v{RU72gQu&X%Q~loCIED|85RHl diff --git a/textures/251.png b/textures/251.png index 9b68564ac5334381cd8a791b27cc85e6d1c060dd..802b5c7c0f9c99b963ddea813b9acba9b08bae9d 100644 GIT binary patch delta 103 zcmd0=njn$H$N&WQ^(M>#QcOwS?k)@rt9q4 delta 92 zcmb=*pCA#=!N$PA@I7>`A&@fjba4!^=uJ*YNciE;@t7&&RogDdiYvWRCRqYc*#swN uwXbm8VIq)xP{61|!nwWCG39|{ng9boBagg}0{ds62@IaDelF{r5}E*w`5n#x diff --git a/textures/252.png b/textures/252.png index 243005a402e54af55ceaa87e944d3813f0bb4692..1c79fada21d301daba998f18293370c699bb786e 100644 GIT binary patch delta 99 zcmYc-n;;R#$N&WQ^(M>#QcOwS?k)@rt9q4hQnuik33*5`1AjNy^{okD64+?D^CSZpehDWS3j3^P6`A&}DXba4!^=uQ6NuauCGaGOw5r>mdKI;Vst0F#;>5C8xG diff --git a/textures/253.png b/textures/253.png index d08f9cf6650039e9af519791a1d38a4c795f6ef8..5584d7f08ec5da411fea1ec8e703ddffefaf4791 100644 GIT binary patch delta 107 zcmd0+n;?#QcOwS?k)@rt9q4nT-FtC$QDA{QLjkUXh34=K~`Vo{YPDfvOoiUHx3vIVCg! E02YEEr~m)} delta 91 zcmb=+n;;R!!N$PA@I7>`A&@flba4!^=uQ6N&ytXkpwPoU-*Jb^34;;?X_u`AQZj6T uFS-_H2%Hq-v1|6=>ke#o<6X4Skzv(d9{FPorRqQf7(8A5T-G@yGywn+t{jp8 diff --git a/textures/254.png b/textures/254.png index 0d97cbf4773cdb302b6bec4b7dd5eb938e19154a..138ed230a40e07f9554f201b11e683d6fa7d48b5 100644 GIT binary patch delta 103 zcma#Anjn$H$N&WQ^(M>#QcOwS?k)@rt9q4tDnm{r-UW|g-ITR delta 76 zcmb=5pCIAR!N$PA@I7>`A&^q@ba4!^=uJ*YNciEeYLj)LLBd5#!ZhQ`g23hz#u*=; e{tIm0ki_t#GUnWM_h|w^9SokXelF{r5}E+vUmN29 diff --git a/textures/255.png b/textures/255.png index ca46b047d9508488e01856392eb2f619a77a985b..4b4df4f2edacc72fdd78d4e0492f5540e94be3e0 100644 GIT binary patch delta 88 zcma#Zo*)s-$N&WQ^(M>#QcOwS?k)@rt9q4GSezm}V50i_r`UHx3vIVCg!0J>rq?*IS* delta 69 zcmd0fm>}W6!N$PA@I7>`A&`>yba4!^=uJ*YNciEeYLj(gh2vtjUMUk0gE?1#A<$`E U0!wM46HqCGr>mdKI;Vst0MYUl4gdfE From 5250d6f2161d0948d8d56f9bf2086218aab400c2 Mon Sep 17 00:00:00 2001 From: rnd Date: Mon, 2 Jul 2018 10:54:19 +0200 Subject: [PATCH 07/39] return error fix for coroutines --- commands.lua | 6 +++--- init.lua | 4 ++-- scripts/games/sliding_puzzle_game.lua | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/commands.lua b/commands.lua index 8a4c0ab..1bea464 100644 --- a/commands.lua +++ b/commands.lua @@ -1210,10 +1210,10 @@ end local cmd_get_player = function(data,pname) -- return player for further manipulation local player = minetest.get_player_by_name(pname) - if not player then return end + if not player then error("player does not exist"); return end local spos = data.spawnpos; local ppos = player:getpos(); - if not is_same_block(ppos,spos) then return end + if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end return player end @@ -1222,7 +1222,7 @@ local cmd_get_player_inv = function(data,pname) if not player then return end local spos = data.spawnpos; local ppos = player:getpos(); - if not is_same_block(ppos,spos) then return end + if not is_same_block(ppos,spos) then error("can not get player in another protection zone") return end return player:get_inventory(); end diff --git a/init.lua b/init.lua index 39c1297..3809553 100644 --- a/init.lua +++ b/init.lua @@ -732,7 +732,7 @@ local function runSandbox( name) local cor = data.cor; if cor then -- coroutine! local err,ret - err,ret = coroutine.resume(cor) + ret,err = coroutine.resume(cor) if err then return err end return nil end @@ -1571,7 +1571,7 @@ minetest.register_on_player_receive_fields( if fields.OK and fields.code then local item = player:get_wielded_item(); --set_wielded_item(item) if string.len(fields.code) > 1000 then - minetest.chat_send_player(player,"#ROBOT: text too long") return + minetest.chat_send_player(player:get_player_name(),"#ROBOT: text too long") return end item:set_metadata(fields.code); player:set_wielded_item(item); diff --git a/scripts/games/sliding_puzzle_game.lua b/scripts/games/sliding_puzzle_game.lua index e86e800..46f37a9 100644 --- a/scripts/games/sliding_puzzle_game.lua +++ b/scripts/games/sliding_puzzle_game.lua @@ -1,10 +1,11 @@ -- sliding unscramble game by rnd, made in 20 minutes if not init then + reward = "default:gold_ingot" + size = 3; + init = true spos = self.spawnpos(); spos.y = spos.y + 1 board = {}; - size = 3; - local players = find_player(4); if not players then say("#sliding puzzle game: no players") self.remove() end name = players[1]; @@ -92,7 +93,6 @@ if event and event.y == spos.y then if score >= size*size-2 then minetest.chat_send_player(name, "CONGRATULATIONS! YOU SOLVED PUZZLE. REWARD WAS DROPPED ON TOP OF ROBOT.") pos = self.pos(); pos.y = pos.y+2; - reward = "default:gold_ingot" minetest.add_item(pos, _G.ItemStack(reward)) self.remove() end From 86c1317f06f5e99d5e8eeebc087e80599158e75e Mon Sep 17 00:00:00 2001 From: rnd Date: Fri, 6 Jul 2018 17:30:49 +0200 Subject: [PATCH 08/39] non admin players can not modify other player robot ( if auth level more than 1) --- init.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/init.lua b/init.lua index 3809553..549110c 100644 --- a/init.lua +++ b/init.lua @@ -1295,7 +1295,6 @@ local on_receive_robot_form = function(pos, formname, fields, sender) if minetest.is_protected(pos,name) then return end if fields.OK then - local meta = minetest.get_meta(pos); if fields.code then @@ -1304,7 +1303,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender) minetest.chat_send_all("#ROBOT: " .. name .. " is spamming with long text.") return end - if meta:get_int("admin") == 1 then + if meta:get_int("authlevel") > 1 and name ~= meta:get_string("owner")then local privs = minetest.get_player_privs(name); -- only admin can edit admin robot code if not privs.privs then return From f110b93dc3a69ee30c780f24ff7c1102aeb05b62 Mon Sep 17 00:00:00 2001 From: rnd Date: Thu, 19 Jul 2018 17:23:34 +0200 Subject: [PATCH 09/39] limit available operations per step: moving(8),placing(5), inserting(5) faster text render string, changed length limit --- commands.lua | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/commands.lua b/commands.lua index 1bea464..7c1bd32 100644 --- a/commands.lua +++ b/commands.lua @@ -80,6 +80,8 @@ basic_robot.commands.move = function(name,dir) local obj = basic_robot.data[name].obj; local pos = pos_in_dir(obj, dir) + check_operations(name,0.25,true) + -- can move through walkable nodes if minetest.registered_nodes[minetest.get_node(pos).name].walkable then return end -- up; no levitation! @@ -168,6 +170,8 @@ end basic_robot.commands.insert_item = function(name,item, inventory,dir) + + check_operations(name,0.4,true) local obj = basic_robot.data[name].obj; local tpos = pos_in_dir(obj, dir); -- position of target block local luaent = obj:get_luaentity(); @@ -337,6 +341,8 @@ basic_robot.commands.write_text = function(name,dir,text) end basic_robot.commands.place = function(name,nodename, param2,dir) + + check_operations(name,0.4,true) local obj = basic_robot.data[name].obj; local pos = pos_in_dir(obj, dir) local luaent = obj:get_luaentity(); @@ -493,6 +499,7 @@ local render_text = function(text,linesize) local count = math.floor(string.len(text)/linesize)+1; local width = 18; local height = 24; local tex = ""; + local ret = {}; local y = 0; local x=0; for i=1,string.len(text) do local cb = string.byte(text,i); @@ -501,26 +508,26 @@ local render_text = function(text,linesize) y=y+1; x=0 else c = string.format("%03d",cb)..".png" - tex = tex .. ":" .. (x*width) .. "," .. (y*height) .. "=" .. c; + ret[#ret+1] = ":" .. (x*width) .. "," .. (y*height) .. "=" .. c; + --tex = tex .. ":" .. (x*width) .. "," .. (y*height) .. "=" .. c; if x==linesize-1 then y=y+1 x=0 else x=x+1 end end end local background = "(black_screen.png^[resize:"..(linesize*width).. "x".. (linesize*height) ..")"; - tex = "([combine:"..(linesize*width).."x"..(linesize*height)..tex..")"; - tex = background .. "^" .. tex; - return tex; + --tex = "([combine:"..(linesize*width).."x"..(linesize*height)..tex..")"; + return background .. "^" .."([combine:"..(linesize*width).."x"..(linesize*height)..table.concat(ret,"")..")"; end basic_robot.commands.display_text = function(obj,text,linesize,size) - if not linesize or linesize<1 then linesize = 20 end + if not linesize or linesize<1 then linesize = 20 elseif linesize>40 then linesize = 40 end if size and size<=0 then size = 1 end if string.len(text)>linesize*linesize then text = string.sub(text,1,linesize*linesize) end local tex = render_text(text,linesize); if not size then return tex end - if string.len(tex)<60000 then + if string.len(tex)<=1600 then obj:set_properties({textures={"arrow.png","basic_machine_side.png",tex,"basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"},visual_size = {x=size,y=size}}) else self.label("error: string too long") From c3599643a98946b41e8686c3c95e71382a2d7748 Mon Sep 17 00:00:00 2001 From: rnd Date: Fri, 20 Jul 2018 13:06:06 +0200 Subject: [PATCH 10/39] automatically start one robot at server start: setting 'local admin_bot_pos' defines its position self.operations() displays remaining available operations for robot tweaked operation costs for dig/move/..., total available operations per step is now 10 --- commands.lua | 21 ++++++++++++--------- init.lua | 29 +++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/commands.lua b/commands.lua index 7c1bd32..68b9c84 100644 --- a/commands.lua +++ b/commands.lua @@ -68,7 +68,7 @@ local check_operations = function(name, amount, quit) data.operations = operations else if quit then - error("robot out of available operations in one step."); return false + error("Robot out of available operations in one step (1s). View available operations with self.operations() and check help to see how much operations each action requires."); return false end return false end @@ -77,11 +77,11 @@ end basic_robot.commands.move = function(name,dir) + + check_operations(name,2,true) local obj = basic_robot.data[name].obj; local pos = pos_in_dir(obj, dir) - - check_operations(name,0.25,true) - + -- can move through walkable nodes if minetest.registered_nodes[minetest.get_node(pos).name].walkable then return end -- up; no levitation! @@ -122,7 +122,7 @@ basic_robot.digcosts = { -- 1 energy = 1 coal basic_robot.commands.dig = function(name,dir) local energy = 0; - check_operations(name,2,true) + check_operations(name,6,true) local obj = basic_robot.data[name].obj; local pos = pos_in_dir(obj, dir) @@ -171,7 +171,7 @@ end basic_robot.commands.insert_item = function(name,item, inventory,dir) - check_operations(name,0.4,true) + check_operations(name,2,true) local obj = basic_robot.data[name].obj; local tpos = pos_in_dir(obj, dir); -- position of target block local luaent = obj:get_luaentity(); @@ -214,6 +214,8 @@ basic_robot.commands.insert_item = function(name,item, inventory,dir) end basic_robot.commands.take_item = function(name,item, inventory,dir) + + check_operations(name,2,true) local obj = basic_robot.data[name].obj; local tpos = pos_in_dir(obj, dir); -- position of target block local luaent = obj:get_luaentity(); @@ -284,7 +286,8 @@ basic_robot.no_teleport_table = { basic_robot.commands.pickup = function(r,name) if r>8 then return false end - + + check_operations(name,4,true) local pos = basic_robot.data[name].obj:getpos(); local spos = basic_robot.data[name].spawnpos; -- position of spawner block local meta = minetest.get_meta(spos); @@ -342,7 +345,7 @@ end basic_robot.commands.place = function(name,nodename, param2,dir) - check_operations(name,0.4,true) + check_operations(name,2,true) local obj = basic_robot.data[name].obj; local pos = pos_in_dir(obj, dir) local luaent = obj:get_luaentity(); @@ -386,7 +389,7 @@ end basic_robot.commands.attack = function(name, target) -- attack range 4, damage 5 local energy = 0; - check_operations(name,2,true); + check_operations(name,4,true); local reach = 4; local damage = 5; diff --git a/init.lua b/init.lua index 549110c..9ad58fd 100644 --- a/init.lua +++ b/init.lua @@ -9,11 +9,14 @@ basic_robot.advanced_count = 16 -- how many robots player with robot privs can h basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications +local admin_bot_pos = {x=0,y=0,z=0} -- position of admin robot spawner that will be run automatically on server start + +basic_robot.maxoperations = 10; -- how many operations (dig, generate energy,..) available per run, 0 = unlimited +basic_robot.dig_require_energy = true; -- does robot require energy to dig? + basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses ["craft_guide:sign_wall"] = true, } -basic_robot.maxoperations = 2; -- how many operations (dig, generate energy,..) available per run, 0 = unlimited -basic_robot.dig_require_energy = true; -- does robot require energy to dig? ---------------------- basic_robot.http_api = minetest.request_http_api(); @@ -88,6 +91,7 @@ function getSandboxEnv (name) pos = function() return basic_robot.data[name].obj:getpos() end, spawnpos = function() local pos = basic_robot.data[name].spawnpos; return {x=pos.x,y=pos.y,z=pos.z} end, name = function() return name end, + operations = function() return basic_robot.data[name].operations end, viewdir = function() local yaw = basic_robot.data[name].obj:getyaw(); return {x=math.cos(yaw), y = 0, z=math.sin(yaw)} end, set_properties = function(properties) @@ -1135,6 +1139,15 @@ local spawn_robot = function(pos,node,ttl) self.running = 1 end + +--admin robot that starts automatically after server start +minetest.after(10, function() + minetest.forceload_block(admin_bot_pos,true) -- load map position + spawn_robot(admin_bot_pos,node,1) + print("[BASIC_ROBOT] admin bot started.") +end) + + local despawn_robot = function(pos) local meta = minetest.get_meta(pos); @@ -1388,6 +1401,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender) " sender,mail = self.read_mail() reads mail, if any\n" .. " self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}\n".. " self.name() returns robot name\n".. + " self.operations() returns remaining robot operations\n".. " self.set_properties({textures=.., visual=..,visual_size=.., , ) sets visual appearance\n".. " set_animation(anim_start,anim_end,anim_speed,anim_stand_start) set mesh animation \n".. " self.spam(0/1) (dis)enable message repeat to all\n".. @@ -1539,6 +1553,17 @@ local on_receive_robot_form = function(pos, formname, fields, sender) end -- handle form: when rightclicking robot entity, remote controller + + +-- minetest.register_on_player_receive_fields( + -- function(player, formname, fields) + -- local gui = robogui[formname]; + -- if gui then gui.response(player,formname,fields) end + -- end +-- ) + + + minetest.register_on_player_receive_fields( function(player, formname, fields) From 1169f5877f2f0baf2c64eb4dadf1ba4643f6607c Mon Sep 17 00:00:00 2001 From: rnd Date: Sun, 22 Jul 2018 12:23:21 +0200 Subject: [PATCH 11/39] improved code preprocessor --- init.lua | 150 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 43 deletions(-) diff --git a/init.lua b/init.lua index 9ad58fd..5ff999e 100644 --- a/init.lua +++ b/init.lua @@ -11,7 +11,7 @@ basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running local admin_bot_pos = {x=0,y=0,z=0} -- position of admin robot spawner that will be run automatically on server start -basic_robot.maxoperations = 10; -- how many operations (dig, generate energy,..) available per run, 0 = unlimited +basic_robot.maxoperations = 10; -- how many operations (dig, place,move,...,generate energy,..) available per run, 0 = unlimited basic_robot.dig_require_energy = true; -- does robot require energy to dig? basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses @@ -21,7 +21,7 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/06/30a"; +basic_robot.version = "2018/07/22a"; basic_robot.data = {}; -- stores all robot related data --[[ @@ -348,34 +348,8 @@ function getSandboxEnv (name) return end end, - - run = function(script) - if basic_robot.data[name].authlevel < 3 then - local err = check_code(script); - script = preprocess_code(script); - if err then - minetest.chat_send_player(name,"#ROBOT CODE CHECK ERROR : " .. err) - return - end - end - - local ScriptFunc, CompileError = loadstring( script ) - if CompileError then - minetest.chat_send_player(name, "#code.run: compile error " .. CompileError ) - return false - end - - setfenv( ScriptFunc, basic_robot.data[name].sandbox ) - - local Result, RuntimeError = pcall( ScriptFunc ); - if RuntimeError then - minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError ) - return false - end - return true - end }, - + rom = basic_robot.data[name].rom, string = { @@ -490,6 +464,34 @@ function getSandboxEnv (name) end if authlevel>=1 then -- robot privs + + env.code.run = function(script) + if basic_robot.data[name].authlevel < 3 then + local err = check_code(script); + script = preprocess_code(script); + if err then + minetest.chat_send_player(name,"#ROBOT CODE CHECK ERROR : " .. err) + return + end + end + + local ScriptFunc, CompileError = loadstring( script ) + if CompileError then + minetest.chat_send_player(name, "#code.run: compile error " .. CompileError ) + return false + end + + setfenv( ScriptFunc, basic_robot.data[name].sandbox ) + + local Result, RuntimeError = pcall( ScriptFunc ); + if RuntimeError then + minetest.chat_send_player(name, "#code.run: run error " .. RuntimeError ) + return false + end + return true + end + + env.self.read_form = function() local fields = basic_robot.data[name].read_form; local sender = basic_robot.data[name].form_sender; @@ -551,8 +553,8 @@ end -- code checker check_code = function(code) - --"while ", "for ", "do ","goto ", - local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall","\\\""} + --"while ", "for ", "do ","goto ", + local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall"} --,"\\\"", "%[=*%[","--[["} for _, v in pairs(bad_code) do if string.find(code, v) then return v .. " is not allowed!"; @@ -561,6 +563,59 @@ check_code = function(code) end +local identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code + + local i = 0; local j; local length = string.len(code); + local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string + local modes = { + {"'","'"}, + {"\"","\""}, + {"%[=*%[","%]=*%]"} + } + local ret = {} + while i < length do + i=i+1 + + local jmin = length+1; + if mode == 0 then -- not yet inside string + for k=1,#modes do + j = string.find(code,modes[k][1],i); + if j and jlow+1 do + mid = math.floor((low+high)/2) + if posstrings[low][2] then mid = high else mid = low end + return strings[mid][1]<=pos and pos<=strings[mid][2] +end + +--[[ is_inside_string = function(pos,script) local i1=string.find (script, "\"", 1); if not i1 then @@ -588,15 +643,19 @@ is_inside_string = function(pos,script) end return false; end +--]] -- COMPILATION -preprocess_code = function(script) +preprocess_code = function(script) -- version 07/22/2018 + --[[ idea: in each local a = function (args) ... end insert counter like: local a = function (args) counter() ... end when counter exceeds limit exit with error --]] + script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-.*\n","\n") -- strip comments in fancy way + script="_ccounter = 0; " .. script; local i1 -- process script to insert call counter in every function @@ -607,6 +666,9 @@ preprocess_code = function(script) local i1=0; local i2 = 0; local found = true; + + local strings = identify_strings(script); + while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS found = false; @@ -614,12 +676,13 @@ preprocess_code = function(script) -- i1 = where its looking - i2=string.find (script, "while ", i1) -- fix while OK + i2=string.find (script, "while%s", i1) -- fix while OK if i2 then - if not is_inside_string(i2,script) then + if not is_inside_string(strings,i2) then local i21 = i2; - i2=string.find(script, "do", i2); + i2=string.find(script, "%sdo%s", i2); if i2 then + i2 = i2 + 1 script = script.sub(script,1, i2+1) .. _increase_ccounter .. script.sub(script, i2+2); i1=i21+6; -- after while found = true; @@ -630,7 +693,7 @@ preprocess_code = function(script) i2=string.find (script, "function", i1) -- fix functions if i2 then --minetest.chat_send_all("func0") - if not is_inside_string(i2,script) then + if not is_inside_string(strings,i2) then i2=string.find(script, ")", i2); if i2 then script = script.sub(script,1, i2) .. _increase_ccounter .. script.sub(script, i2+1); @@ -641,11 +704,12 @@ preprocess_code = function(script) end - i2=string.find (script, "for ", i1) -- fix for OK + i2=string.find (script, "for%s", i1) -- fix for OK if i2 then - if not is_inside_string(i2,script) then - i2=string.find(script, "do", i2); + if not is_inside_string(strings,i2) then + i2=string.find(script, "%sdo%s", i2); if i2 then + i2 = i2 + 1 script = script.sub(script,1, i2+1) .. _increase_ccounter .. script.sub(script, i2+2); i1=i2+string.len(_increase_ccounter); found = true; @@ -653,9 +717,9 @@ preprocess_code = function(script) end end - i2=string.find (script, "goto ", i1) -- fix goto OK + i2=string.find (script, "goto%s", i1) -- fix goto OK if i2 then - if not is_inside_string(i2,script) then + if not is_inside_string(strings,i2) then script = script.sub(script,1, i2-1) .. _increase_ccounter .. script.sub(script, i2); i1=i2+string.len(_increase_ccounter)+5; -- insert + skip goto found = true; @@ -664,7 +728,7 @@ preprocess_code = function(script) i2=string.find (script, "pause()", i1) -- fix pause ? if i2 then - if not is_inside_string(i2,script) then + if not is_inside_string(strings,i2) then script = script.sub(script,1, i2-1) .. "_ccounter = 0;" .. script.sub(script,i2) i1=i2+14+7; -- insert + skip _ccounter & pause found = true; @@ -1385,7 +1449,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender) " read_text.direction(stringname,mode) reads text of signs, chests and other blocks, optional stringname for other meta,\n mode 1 read number\n".. " write_text.direction(text,mode) writes text to target block as infotext\n".. "**BOOKS/CODE\n title,text=book.read(i) returns title,contents of book at i-th position in library \n book.write(i,title,text) writes book at i-th position at spawner library\n".. - " code.run(text) compiles and runs the code in sandbox\n".. + " code.run(text) compiles and runs the code in sandbox (privs only)\n".. " code.set(text) replaces current bytecode of robot\n".. " find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot, or false if none\n".. "**PLAYERS\n".. From e5382fd12e42888722ebe686501df98d0bf6c492 Mon Sep 17 00:00:00 2001 From: rnd Date: Sun, 22 Jul 2018 12:41:36 +0200 Subject: [PATCH 12/39] ... --- init.lua | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/init.lua b/init.lua index 5ff999e..872ec04 100644 --- a/init.lua +++ b/init.lua @@ -2,22 +2,22 @@ basic_robot = {}; ----- SETTINGS ------ +------ SETTINGS -------- basic_robot.call_limit = 48; -- how many execution calls per script run allowed basic_robot.entry_count = 2 -- how many robots ordinary player can have basic_robot.advanced_count = 16 -- how many robots player with robot privs can have basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches basic_robot.password = "raN___dOM_ p4S"; -- IMPORTANT: change it before running mod, password used for authentifications -local admin_bot_pos = {x=0,y=0,z=0} -- position of admin robot spawner that will be run automatically on server start +basic_robot.admin_bot_pos = {x=0,y=1,z=0} -- position of admin robot spawner that will be run automatically on server start basic_robot.maxoperations = 10; -- how many operations (dig, place,move,...,generate energy,..) available per run, 0 = unlimited -basic_robot.dig_require_energy = true; -- does robot require energy to dig? +basic_robot.dig_require_energy = true; -- does robot require energy to dig stone? basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses ["craft_guide:sign_wall"] = true, } ----------------------- +----- END OF SETTINGS ------ basic_robot.http_api = minetest.request_http_api(); @@ -400,14 +400,14 @@ function getSandboxEnv (name) --_ccounter = basic_robot.data[name].ccounter, -- counts how many executions of critical spots in script - increase_ccounter = - function() - local _ccounter = basic_robot.data[name].ccounter; - if _ccounter > basic_robot.call_limit then - error("Execution limit " .. basic_robot.call_limit .. " exceeded"); - end - basic_robot.data[name].ccounter = _ccounter + 1; - end, + -- increase_ccounter = + -- function() + -- local _ccounter = basic_robot.data[name].ccounter; + -- if _ccounter > basic_robot.call_limit then + -- error("Execution limit " .. basic_robot.call_limit .. " exceeded"); + -- end + -- basic_robot.data[name].ccounter = _ccounter + 1; + -- end, }; -- ROBOT FUNCTIONS: move,dig, place,insert,take,check_inventory,activate,read_node,read_text,write_text @@ -606,6 +606,7 @@ end is_inside_string = function(strings,pos) -- is position inside one of the strings? local low = 1; local high = #strings; + if high == 0 then return false end local mid = high; while high>low+1 do mid = math.floor((low+high)/2) @@ -1206,9 +1207,10 @@ end --admin robot that starts automatically after server start minetest.after(10, function() + local admin_bot_pos = basic_robot.admin_bot_pos; minetest.forceload_block(admin_bot_pos,true) -- load map position - spawn_robot(admin_bot_pos,node,1) - print("[BASIC_ROBOT] admin bot started.") + spawn_robot(admin_bot_pos,nil,1) + print("[BASIC_ROBOT] admin robot at " .. admin_bot_pos.x .. " " .. admin_bot_pos.y .. " " .. admin_bot_pos.z .. " started.") end) From db15170554ad9973b3226446e03109d55925d0fa Mon Sep 17 00:00:00 2001 From: rnd Date: Sun, 22 Jul 2018 18:29:30 +0200 Subject: [PATCH 13/39] bugfix: code comment removal --- init.lua | 11 ++- scripts/farm_walker.lua | 15 +++ scripts/games/maze_generator.lua | 138 +++++++++++++++++++++++++++ scripts/server mods/colored chat.lua | 42 ++++++++ 4 files changed, 201 insertions(+), 5 deletions(-) create mode 100644 scripts/farm_walker.lua create mode 100644 scripts/games/maze_generator.lua create mode 100644 scripts/server mods/colored chat.lua diff --git a/init.lua b/init.lua index 872ec04..695257e 100644 --- a/init.lua +++ b/init.lua @@ -651,20 +651,21 @@ end preprocess_code = function(script) -- version 07/22/2018 --[[ idea: in each local a = function (args) ... end insert counter like: - local a = function (args) counter() ... end + local a = function (args) counter_check_code ... end when counter exceeds limit exit with error --]] - script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-.*\n","\n") -- strip comments in fancy way + script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments in fancy way script="_ccounter = 0; " .. script; - local i1 -- process script to insert call counter in every function + local i1 + -- process script to insert call counter in every function local _increase_ccounter = " if _ccounter > " .. basic_robot.call_limit .. " then error(\"Execution count \".. _ccounter .. \" exceeded ".. basic_robot.call_limit .. "\") end _ccounter = _ccounter + 1; " - local i1=0; local i2 = 0; + local i1=0; local i2 = 0; local found = true; @@ -675,7 +676,7 @@ preprocess_code = function(script) -- version 07/22/2018 found = false; i2 = nil; - -- i1 = where its looking + -- i1 = where its looking in current pass, i2 = hit position i2=string.find (script, "while%s", i1) -- fix while OK if i2 then diff --git a/scripts/farm_walker.lua b/scripts/farm_walker.lua new file mode 100644 index 0000000..b0c8b00 --- /dev/null +++ b/scripts/farm_walker.lua @@ -0,0 +1,15 @@ +if not init then + init = true + angle = 90 + walk = {["default:dirt"] = 1} + stop = {["wool:white"] = 1} +end + +node = read_node.forward_down() +if walk[node] then + move.forward() +elseif stop[node] then + self.reset(); angle = 90 +else + turn.angle(angle);move.forward(); turn.angle(angle); angle = - angle +end \ No newline at end of file diff --git a/scripts/games/maze_generator.lua b/scripts/games/maze_generator.lua new file mode 100644 index 0000000..3b377c1 --- /dev/null +++ b/scripts/games/maze_generator.lua @@ -0,0 +1,138 @@ +-- maze generation by rnd + +-- http://en.wikipedia.org/wiki/Maze_generation_algorithm#Depth-first_search, recursive backtracker +-- representation of node coordinate (row,coloumn)=(i,j) -> (i-1)*n+j, i=1..n, j=1...m +-- representation of walls: below node k --> k, left of node k --> k+m.n + +-- good overview of maze generation algorithms using javascript/html5 +-- http://www.jamisbuck.org/presentations/rubyconf2011/index.html#recursive-backtracker + +-- helper functions +--stack in lua +local stack={}; +function stack.push(s,e) s[#s+1]=e end +function stack.pop(s) local r = s[#s];s[#s]=nil;return r end +--function table2string(s) local r = ""; for i,v in pairs(s) do r = r.. " ["..i.."]=".. v ; end return r end + +function maze_deep_first_search(m,n,start,seed) -- returns a table of strings representing line renders + + local steps,maxsteps; steps= 0; maxsteps = 999999; + local maze = {} + maze.m = m; maze.n = n; + maze.unvisited = {};maze.stack = {}; maze.walls = {}; + maze.free = maze.m*maze.n; + local i,j,k + local nb,wall -- unvisited neighbbors, walls + + --init structures + for i=1,maze.m do + for j =1,maze.n do + k=(i-1)*maze.n+j;maze.unvisited[k]=true -- initially all cells unvisited + maze.walls[k]=true;maze.walls[k+maze.n*maze.m]=true; -- walls are there + end + end + + _G.math.randomseed(seed) + maze.current = start + maze.unvisited [ maze.current ] = false; + maze.free = maze.free-1; maze.stack[1+#maze.stack] = maze.current + + while maze.free>0 and steps1 then -- down + k=(i-2)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k+maze.n;nb[#nb+1]=k end + end + if i1 then --left + k=(i-1)*maze.n+j-1; if maze.unvisited[k] then wall[#wall+1]=k+1+maze.n*maze.m;nb[#nb+1]=k end + end + + --print(" unvisited neighbors " .. table2string(nb)) + if (#nb)>0 then -- if unvisited neighbors, choose random one as next current node + stack.push(maze.stack,maze.current) -- remember previous current node + k=math.random(#nb); -- pick random unvisited neighbor + maze.walls[wall[k]]=false; -- remove wall + --print(" removed wall ".. wall[k]) + k=nb[k]; + maze.current = k; -- new current cell + maze.unvisited[k]=false; maze.free = maze.free-1 -- one less unvisited + --print("new explore " .. k); + + elseif (#maze.stack)~=0 then -- no unvisited neighbors, backtrack using stack + + maze.current = stack.pop(maze.stack) + --print("backtrack to "..maze.current) + + else -- even stack is empty, just pick random unvisited cell + k = math.random(maze.free); j=1; + for i =1,maze.m*maze.n do + if maze.unvisited[i] then + if j==k then k=i; break end -- pick node + j=j+1 + end + end + --print(" stack empty, random pick " ..k) + maze.current=k;maze.unvisited[k]=false; maze.free = maze.free -1; + end + end -- of do + + -- render maze with chars, row by row + maze.ret = {}; + local hor;local vert; + local wall = "1" + + for i=1,maze.m do + hor="";vert=""; + k= (i-1)*maze.n; + -- horizontal + for j = 1, maze.n do + k=k+1; + -- if maze.walls[k+maze.n*maze.m] then vert=vert.."X." else vert=vert.. "0." end + -- if maze.walls[k] then hor=hor.."XX" else hor=hor.."X0" end + if maze.walls[k+maze.n*maze.m] then vert=vert..wall.."0" else vert=vert.. "00" end + if maze.walls[k] then hor=hor..wall..wall else hor=hor..wall.."0" end + end + maze.ret[1+#maze.ret]=hor..wall;maze.ret[1+#maze.ret]=vert..wall; + end + maze.ret[1+#maze.ret] = string.rep(wall,2*maze.n+1) + return maze.ret + end + +-- RUN PROGRAM + local maze=maze_deep_first_search(10,30,1,2015) + --for _,v in pairs(maze) do print(v) end + + + +make_maze = function(m,n,start,seed) + local pos = self.spawnpos();pos.y=pos.y+1 + local p + local maze=maze_deep_first_search(m,n,start,seed) -- m,n,start,seed + local i,j,k;local p = {x=pos.x,y=pos.y,z=pos.z}; + for i,v in pairs(maze) do + p.x = pos.x+i + for k = 1,string.len(v) do + p.z=pos.z+k + if string.sub(v,k,k)=="1" then + minetest.set_node(p,{name="default:brick"}) + else minetest.set_node(p,{name="air"}) + end + end + end +end + +make_maze(10,10,1,1) +self.remove() \ No newline at end of file diff --git a/scripts/server mods/colored chat.lua b/scripts/server mods/colored chat.lua new file mode 100644 index 0000000..d6ebb1c --- /dev/null +++ b/scripts/server mods/colored chat.lua @@ -0,0 +1,42 @@ +-- with current mods there are 4 registered chat responses so we add 5th +-- CHANGE COLOR OF CHAT FOR CERTAIN PLAYERS + +if not rom.color_chat_messages then rom.color_chat_messages = 1+#minetest.registered_on_chat_messages end + +colors = {"cyan", "LawnGreen"} +chatgroup = {}; -- players in here will see chat without colors +--say("chat " .. rom.chat_messages) + +minetest.registered_on_chat_messages[rom.color_chat_messages] = +function(name,message) + if message == "nocolor" then + chatgroup[name] = not chatgroup[name] + minetest.chat_send_all("colored chat display " .. (chatgroup[name] and "DISABLED" or "ENABLED") .. " for " .. name) + return false + else + --message = os.date("%X") .. " " .. name .." <> " .. message; + local newmessage = "["..name .."] " .. message; + local player = minetest.get_player_by_name(name); + local pos1 = player:get_pos(); + + for _,player in pairs(minetest.get_connected_players()) do + local name = player:get_player_name() + local pos2 = player:get_pos(); + local dist = math.sqrt((pos2.x-pos1.x)^2+(pos2.y-pos1.y)^2+ (pos2.z-pos1.z)^2) + local length = string.len(name); + local color = 1; -- default + if (chatgroup[name] or dist>32 or dist == 0) then color = 0 end + if string.find(message,string.lower(name)) then color = 2 end + + if color == 0 then + minetest.chat_send_player(name, newmessage) + else + minetest.chat_send_player(name, minetest.colorize(colors[color], newmessage)) + end + + end + end + return true +end + +self.remove() \ No newline at end of file From 21158c67eb8758f116c659ddb533da5cfe221832 Mon Sep 17 00:00:00 2001 From: rnd Date: Mon, 23 Jul 2018 15:32:46 +0200 Subject: [PATCH 14/39] tweaked operation costs redo of code preprocessing --- README.txt | 10 +++- commands.lua | 27 +++++++--- init.lua | 143 ++++++++++++++++++++------------------------------- 3 files changed, 87 insertions(+), 93 deletions(-) diff --git a/README.txt b/README.txt index 5950b38..a247eec 100644 --- a/README.txt +++ b/README.txt @@ -18,4 +18,12 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ----------------------------------------------------------------------- \ No newline at end of file +---------------------------------------------------------------------- + + +GAMEPLAY: + +- robot has limited operations available every run ( 1 run per 1 second). +- while using for loops, while loops or function calls it is limited to default 48 such code executions per run +- while using 'physical' operations like move/dig robot has (default) 10 operations available per run. Default costs are + move=2, dig = 6, insert = 2, place = 2, machine.generate = 6, machine.smelt = 6, machine.grind = 6, \ No newline at end of file diff --git a/commands.lua b/commands.lua index 68b9c84..6956116 100644 --- a/commands.lua +++ b/commands.lua @@ -115,7 +115,7 @@ end basic_robot.digcosts = { -- 1 energy = 1 coal ["default:stone"] = 1/25, - + ["default:cloud"] = 10^8, } @@ -433,9 +433,18 @@ basic_robot.commands.grab = function(name,target) end +--local minetest_version = minetest.get_version().string; basic_robot.commands.read_book = function (itemstack) -- itemstack should contain book - local data = itemstack:get_meta():to_table().fields -- 0.4.16 - --local data = minetest.deserialize(itemstack:get_metadata()) -- pre 0.4.16 + local data; + --if minetest_version == "0.4.16" then + data = itemstack:get_meta():to_table().fields -- 0.4.16 + if data and data.text then + data.text = data.text:gsub(string.char(13),string.char(10)) --for unknown reason books sometime? convert \n (10) to CR (13) + end + -- else + -- local data = minetest.deserialize(itemstack:get_metadata()) -- pre 0.4.16 + -- end + if data then return data.title,data.text; else @@ -539,6 +548,8 @@ end local robot_activate_furnace = minetest.registered_nodes["default:furnace"].on_metadata_inventory_put; -- this function will activate furnace basic_robot.commands.activate = function(name,mode, dir) + + check_operations(name,2,true); local obj = basic_robot.data[name].obj; local tpos = pos_in_dir(obj, dir); -- position of target block in front @@ -896,7 +907,7 @@ basic_robot.commands.machine = { -- convert fuel into energy generate_power = function(name,input, amount) -- fuel used, if no fuel then amount specifies how much energy builtin generator should produce - check_operations(name,1.5, true) + check_operations(name,6, true) if amount and amount>0 then -- attempt to generate power from builtin generator local pos = basic_robot.data[name].spawnpos; -- position of spawner block @@ -943,7 +954,7 @@ basic_robot.commands.machine = { smelt = function(name,input,amount) -- input material, amount of energy used for smelt local energy = 0; -- can only do one step at a run time - check_operations(name,2,true) + check_operations(name,6,true) if string.find(input," ") then return nil, "0: only one item per smelt" end @@ -1005,6 +1016,7 @@ basic_robot.commands.machine = { -- grind grind = function(name,input) --[in] ={fuel cost, out, quantity of material required for processing} + check_operations(name,6,true) local recipe = basic_robot.technic.grinder_recipes[input]; if not recipe then return nil, "unknown recipe" end local cost = recipe[1]; local output = recipe[2]; @@ -1035,6 +1047,7 @@ basic_robot.commands.machine = { -- compress compress = function(name,input) --[in] ={fuel cost, out, quantity of material required for processing} + check_operations(name,6,true) local recipe = basic_robot.technic.compressor_recipes[input]; if not recipe then return nil, "unknown recipe" end local cost = recipe[1]; local output = recipe[2]; @@ -1061,13 +1074,15 @@ basic_robot.commands.machine = { end, transfer_power = function(name,amount,target) + + check_operations(name,2, true); local pos = basic_robot.data[name].spawnpos; local data = basic_robot.data[name]; local tdata = basic_robot.data[target]; if not tdata then return nil, "target inactive" end local energy = 0; -- can only do one step at a run time - check_operations(name,0.5, true); + energy = data.menergy or 0; if amount>energy then return nil,"energy too low" end diff --git a/init.lua b/init.lua index 695257e..4351cbd 100644 --- a/init.lua +++ b/init.lua @@ -21,11 +21,11 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/07/22a"; +basic_robot.version = "2018/07/23a"; basic_robot.data = {}; -- stores all robot related data --[[ -[name] = { sandbox= .., bytecode = ..., ram = ..., obj = robot object, spawnpos= ..., authlevel = ...} +[name] = { sandbox= .., bytecode = ..., ram = ..., obj = robot object, spawnpos= ..., authlevel = ... , t = code execution time} robot object = object of entity, used to manipulate movements and more --]] basic_robot.ids = {}; -- stores maxid for each player @@ -52,6 +52,7 @@ function getSandboxEnv (name) left_up = 11, right_up = 12, forward_up = 13, backward_up = 14 } + if not basic_robot.data[name].rom then basic_robot.data[name].rom = {} end -- create rom if not yet existing local env = { pcall=pcall, @@ -398,16 +399,6 @@ function getSandboxEnv (name) tonumber = tonumber, pairs = pairs, ipairs = ipairs, error = error, type=type, - --_ccounter = basic_robot.data[name].ccounter, -- counts how many executions of critical spots in script - - -- increase_ccounter = - -- function() - -- local _ccounter = basic_robot.data[name].ccounter; - -- if _ccounter > basic_robot.call_limit then - -- error("Execution limit " .. basic_robot.call_limit .. " exceeded"); - -- end - -- basic_robot.data[name].ccounter = _ccounter + 1; - -- end, }; -- ROBOT FUNCTIONS: move,dig, place,insert,take,check_inventory,activate,read_node,read_text,write_text @@ -565,7 +556,7 @@ end local identify_strings = function(code) -- returns list of positions {start,end} of literal strings in lua code - local i = 0; local j; local length = string.len(code); + local i = 0; local j; local _; local length = string.len(code); local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string local modes = { {"'","'"}, @@ -616,38 +607,24 @@ is_inside_string = function(strings,pos) -- is position inside one of the string return strings[mid][1]<=pos and pos<=strings[mid][2] end ---[[ -is_inside_string = function(pos,script) - local i1=string.find (script, "\"", 1); - if not i1 then - return false - end - local i2=0; - local par = 1; - - if pos Date: Mon, 23 Jul 2018 17:35:03 +0200 Subject: [PATCH 15/39] drops abuse fixed .. replaced with safe string.concat(table of strings, separator) --- commands.lua | 4 ++-- init.lua | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/commands.lua b/commands.lua index 6956116..2aff995 100644 --- a/commands.lua +++ b/commands.lua @@ -490,8 +490,8 @@ basic_robot.give_drops = function(nodename, inv) -- gives apropriate drops when local i = 0; for k,v in pairs(drop.items) do if i > max_items then break end; i=i+1; - local rare = v.rarity or 1; - if math.random(1, rare)==1 then + local rare = v.rarity or 0; + if rare>0 and math.random(1, rare)==1 then dropname = v.items[math.random(1,#v.items)]; -- pick item randomly from list inv:add_item("main",dropname); diff --git a/init.lua b/init.lua index 4351cbd..72cc4a8 100644 --- a/init.lua +++ b/init.lua @@ -16,6 +16,10 @@ basic_robot.dig_require_energy = true; -- does robot require energy to dig stone basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes inventories to prevent player abuses ["craft_guide:sign_wall"] = true, + ["basic_machines:battery_0"] = true, + ["basic_machines:battery_1"] = true, + ["basic_machines:battery_2"] = true, + ["basic_machines:generator"] = true, } ----- END OF SETTINGS ------ @@ -361,6 +365,18 @@ function getSandboxEnv (name) len = string.len, lower = string.lower, upper = string.upper, rep = string.rep, reverse = string.reverse, sub = string.sub, + + concat = function(strings, sep) + local length = 0; + for i = 1,#strings do + length = length + string.len(strings[i]) + if length > 1024 then + error("result string longer than 1024") + return + end + end + return table.concat(strings,sep or "") + end, }, math = { abs = math.abs, acos = math.acos, @@ -545,7 +561,7 @@ end check_code = function(code) --"while ", "for ", "do ","goto ", - local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall"} --,"\\\"", "%[=*%[","--[["} + local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall",".."} --,"\\\"", "%[=*%[","--[["} for _, v in pairs(bad_code) do if string.find(code, v) then return v .. " is not allowed!"; @@ -1426,6 +1442,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender) " code.run(text) compiles and runs the code in sandbox (privs only)\n".. " code.set(text) replaces current bytecode of robot\n".. " find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot, or false if none\n".. + " string.concat({string1,string2,...}, separator) returns concatenated string. maxlength 1024\n".. "**PLAYERS\n".. " find_player(3,pos) finds players in radius 3 around robot(position) and returns list, if none returns nil\n".. " attack(target) attempts to attack target player if nearby \n".. From 135fecfe20b69fc94d51cc4859c33d580661cc6a Mon Sep 17 00:00:00 2001 From: rnd Date: Tue, 24 Jul 2018 11:50:23 +0200 Subject: [PATCH 16/39] preprocess update --- init.lua | 82 +++++++++++++++++++++----------------------------------- 1 file changed, 31 insertions(+), 51 deletions(-) diff --git a/init.lua b/init.lua index 72cc4a8..b32146e 100644 --- a/init.lua +++ b/init.lua @@ -561,7 +561,7 @@ end check_code = function(code) --"while ", "for ", "do ","goto ", - local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall",".."} --,"\\\"", "%[=*%[","--[["} + local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall","%.%."} --,"\\\"", "%[=*%[","--[["} for _, v in pairs(bad_code) do if string.find(code, v) then return v .. " is not allowed!"; @@ -641,7 +641,7 @@ local find_outside_string = function(script, pattern, pos, strings) return nil end -preprocess_code = function(script) -- version 07/22/2018 +preprocess_code = function(script) -- version 07/24/2018 --[[ idea: in each local a = function (args) ... end insert counter like: local a = function (args) counter_check_code ... end @@ -661,60 +661,40 @@ preprocess_code = function(script) -- version 07/22/2018 local strings = identify_strings(script); local inserts = {}; - while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS + local constructs = { + {"while%s", "%sdo%s", 2, 6}, -- numbers: insertion pos = i2+2, after skip to i1 = i12+6 + {"function", ")", 0, 0}, + {"for%s", "%sdo%s", 2, 0}, + {"goto%s", nil , -1, 5}, + } + + for i = 1,#constructs do + i1 = 0 + while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS - found = false; - i2 = nil; - -- i1 = where its looking in current pass, i2 = hit position - - i2=string.find (script, "while%s", i1) -- fix while OK - if i2 then - if not is_inside_string(strings,i2) then + found = false; + + i2=find_outside_string(script, constructs[i][1], i1, strings) -- first part of construct + if i2 then local i21 = i2; - i2 = find_outside_string(script, "%sdo%s", i2, strings); -- find first do not inside string - if i2 then - i2 = i2 + 2 -- skip space and position at 'o' in ' do' - inserts[#inserts+1]= i2; - i1=i21+6; -- after while - found = true; + if constructs[i][2] then + i2 = find_outside_string(script, constructs[i][2], i2, strings); -- second part of construct ( if any ) + if i2 then + inserts[#inserts+1]= i2+constructs[i][3]; -- move to last position of construct[i][2] + found = true; + end + else + inserts[#inserts+1]= i2+constructs[i][3] + found = true -- 1 part construct + end + + if found then + i1=i21+constructs[i][4]; -- skip to after constructs[i][1] end end + end - - i2=string.find (script, "function", i1) -- fix functions - if i2 then - if not is_inside_string(strings,i2) then - i2 = find_outside_string(script, ")", i2, strings); - if i2 then - inserts[#inserts+1]= i2; - i1=i2; - found = true; - end - end - - end - - i2=string.find (script, "for%s", i1) -- fix for OK - if i2 then - if not is_inside_string(strings,i2) then - i2 = find_outside_string(script, "%sdo%s", i2, strings); - if i2 then - i2 = i2 + 2 -- position at 'o' in ' do' - inserts[#inserts+1]= i2; - i1=i2; - found = true; - end - end - end - - i2=string.find(script, "goto%s", i1) -- fix goto OK - if i2 then - if not is_inside_string(strings,i2) then - inserts[#inserts+1]= i2-1; -- just before goto - i1=i2+5; -- insert + skip goto - found = true; - end - end + end -- add inserts From 0a4ae08a9d4c622c3c5544487e611b0c5beee625 Mon Sep 17 00:00:00 2001 From: rnd Date: Tue, 24 Jul 2018 12:20:42 +0200 Subject: [PATCH 17/39] .. --- init.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/init.lua b/init.lua index b32146e..4449c40 100644 --- a/init.lua +++ b/init.lua @@ -395,13 +395,6 @@ function getSandboxEnv (name) sqrt = math.sqrt, tan = math.tan, tanh = math.tanh, }, - table = { - concat = table.concat, - insert = table.insert, - maxn = table.maxn, - remove = table.remove, - sort = table.sort, - }, os = { clock = os.clock, difftime = os.difftime, @@ -471,6 +464,15 @@ function getSandboxEnv (name) end if authlevel>=1 then -- robot privs + + + env.table = { + concat = table.concat, + insert = table.insert, + maxn = table.maxn, + remove = table.remove, + sort = table.sort, + }, env.code.run = function(script) if basic_robot.data[name].authlevel < 3 then From 4e6e431efdd46d8157ddeae809bf375d39823293 Mon Sep 17 00:00:00 2001 From: rnd Date: Tue, 24 Jul 2018 16:04:22 +0200 Subject: [PATCH 18/39] new demo scripts --- init.lua | 4 +- scripts/gui/craft_guide.lua | 108 ++++++++++++++++++++++ scripts/gui/gui_deposit_withdraw_demo.lua | 82 ++++++++++++++++ scripts/server mods/chatlog.lua | 50 ++++++++++ 4 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 scripts/gui/craft_guide.lua create mode 100644 scripts/gui/gui_deposit_withdraw_demo.lua create mode 100644 scripts/server mods/chatlog.lua diff --git a/init.lua b/init.lua index 4449c40..2e69cef 100644 --- a/init.lua +++ b/init.lua @@ -472,7 +472,7 @@ function getSandboxEnv (name) maxn = table.maxn, remove = table.remove, sort = table.sort, - }, + } env.code.run = function(script) if basic_robot.data[name].authlevel < 3 then @@ -1936,7 +1936,7 @@ end minetest.register_craftitem("basic_robot:control", { description = "Robot remote control", inventory_image = "control.png", - groups = {book = 1, not_in_creative_inventory = 1}, + groups = {book = 1}, --not_in_creative_inventory = 1 stack_max = 1, on_secondary_use = function(itemstack, user, pointed_thing) diff --git a/scripts/gui/craft_guide.lua b/scripts/gui/craft_guide.lua new file mode 100644 index 0000000..fc088e1 --- /dev/null +++ b/scripts/gui/craft_guide.lua @@ -0,0 +1,108 @@ +-- ROBOT craft guide by rnd, 2017 +if not list then + + tname = "rnd"; + list = {}; + tmplist = _G.minetest.registered_items; + for k,v in pairs(tmplist) do + local texture = v.inventory_image or ""; + if texture=="" and v.tiles then texture = v.tiles[1] or "" end + if (not v.groups.not_in_craft_guide or v.groups.not_in_craft_guide == 0) and type(texture)=="string" and texture~="" then + list[#list+1] = {_G.minetest.formspec_escape(k),_G.minetest.formspec_escape(v.description),_G.minetest.formspec_escape(texture)}; -- v.inventory_image, k, v.description + end + end + + + idx = 1; n = 35; row = 6; size = 1.25; + filter = "" item = "" recipeid = 1 + filterlist = {}; for i = 1,#list do filterlist[i] = i end + + get_texture = function(ritem) + local v = _G.minetest.registered_items[ritem]; if not v then return "" end + local texture = v.inventory_image or ""; + if texture=="" and v.tiles then texture = v.tiles[1] or "" end + if type(texture)~="string" then return "" end + return texture + end + + get_form = function() + local form = "size[7.5,8.5]"; + local x,y,i; local idxt = idx+n; if idxt > #filterlist then idxt = #filterlist end + for i = idx, idxt do + local id = filterlist[i]; + if list[id] and list[id][3] then + x = ((i-idx) % row) + y = (i-idx-x)/row; + form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. list[id][3] ..";".."item"..";".. list[id][1] .."]" + end + end + form = form .. "textarea[0.25,0;2,0.75;filter;filter;"..filter .. "]" .. "button[2.,0;1,0.5;search;search]".. + "button[5.5,0;1,0.5;prev;PREV]" .. "button[6.5,0;1,0.5;next;NEXT]" .. "label[4,0;".. idx .. "-"..idxt .. "/" .. #filterlist.."]"; + return form + end + + get_recipe = function() + local form = "size[7.5,8.5]"; + local recipes = _G.minetest.get_all_craft_recipes(item); if not recipes then return end; + local recipe = recipes[recipeid]; if not recipe then return end + local items = recipe.items + local x,y,i; + for i = 0, 8 do + local ritem = items[i+1] or ""; local sritem = ""; + local j = string.find(ritem,":"); if j then sritem = string.sub(ritem,j+1) end; --ritem = _G.minetest.formspec_escape(ritem); + x = (i % 3) + y = (i-x)/3; + form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. get_texture(ritem) ..";".."item"..";".. sritem .."]" + end + form = form .. "textarea[0.25,0;2,0.75;recipeid;recipeid ".. #recipes .. ";"..recipeid .. "]" .. "button[2.,0;1,0.5;go;go]".. + "label[3,0;" .. item .. "]" .. "button[6.5,0;1,0.5;back;BACK]" ; + return form + end + + s=0 +end + +if s==0 then + local p = find_player(4); s = 1 + if p then + self.show_form(p[1],get_form()) + else + self.remove() + end +end + + +sender,fields = self.read_form() +if sender then + + if fields.search then + filter = fields.filter or "" + filterlist = {}; + for i = 1,#list do + if string.find(list[i][1],filter) then filterlist[#filterlist+1] = i end + end + idx=1;self.show_form(sender,get_form()) + + elseif fields.prev then + idx = idx - n; if idx<1 then idx =#filterlist-n end + self.show_form(sender,get_form()) + elseif fields.next then + idx = idx+n; if idx > #filterlist then idx = 1 end + self.show_form(sender,get_form()) + elseif fields.back then + self.show_form(sender,get_form()) + elseif fields.recipeid then + recipeid = tonumber(fields.recipeid) or 1; + self.show_form(sender,get_recipe()) + elseif fields.item then + item = fields.item; + local recipes = _G.minetest.get_all_craft_recipes(item); + local count = 0; if recipes then count = #recipes end + if count>0 then + recipeid = 1 + self.show_form(sender,get_recipe() or "") + end + elseif fields.quit then + self.remove() + end +end \ No newline at end of file diff --git a/scripts/gui/gui_deposit_withdraw_demo.lua b/scripts/gui/gui_deposit_withdraw_demo.lua new file mode 100644 index 0000000..3c87489 --- /dev/null +++ b/scripts/gui/gui_deposit_withdraw_demo.lua @@ -0,0 +1,82 @@ + +if not init then init = true + + deposit = function(pname) + _, text = book.read(1); + local data = deserialize(text) or {}; + + local player = _G.minetest.get_player_by_name(pname) + local inv = player:get_inventory(); + local pstack = inv:get_stack("main", 1); + local iname = pstack:to_string() + + + item, count = _G.string.match(iname,"(%S+)%s?(%d*)"); + item = item or "";if item == "" then return end + + count = tonumber(count) or 1; + + --say("item " .. item .. ", count " .. count) + + data[pname] = data[pname] or {}; + local pdata = data[pname]; + pdata[item] = (pdata[item] or 0) + count + + inv:set_stack("main", 1, _G.ItemStack("")) + + book.write(1,"",serialize(data)) + local form = "size [5,5] label[0,0; You deposited " .. item .. "( " .. count .. " pieces)]" + self.show_form(pname, form) + --say(pname .. " deposited " .. item .. "( " .. count .. " pieces) ") + end + + check = function(pname) + _, text = book.read(1); + local data = deserialize(text) or {}; + data[pname] = data[pname] or {}; + + --say(serialize(data[pname])) + local text = serialize(data[pname]) + local form = "size[5,5] textarea[0,0;6,6;STORAGE;STORAGE;".. + "YOU HAVE STORED FOLLOWING ITEMS:\n\n".. minetest.formspec_escape(text) .. "\n\nUse WITHDRAW to get items back]" + self.show_form(pname, form) + --say(pname .. " deposited " .. item .. "( " .. count .. " pieces) ") + end + + withdraw = function(pname) + _, text = book.read(1); + local data = deserialize(text) or {}; + data[pname] = data[pname] or {}; + + local player = _G.minetest.get_player_by_name(pname) + local inv = player:get_inventory(); + local pdata = data[pname] + for k,v in pairs(pdata) do + inv:add_item("main", _G.ItemStack(k .. " " .. v)) + end + data[pname] = nil; + book.write(1,"",serialize(data)) + end + + + local players = find_player(4) + if not players then self.remove() end + + pname = players[1] + + local form = "size [5,5] button[0,0;2,1;DEPOSIT;DEPOSIT] button[0,1;2,1;CHECK;CHECK] button[0,2;2,1;WITHDRAW;WITHDRAW]" + self.show_form(pname, form) +end + +sender,fields = self.read_form() +if sender then + if fields.DEPOSIT then + deposit(sender) + elseif fields.CHECK then + check(sender) + elseif fields.WITHDRAW then + withdraw(sender) + end + + --say(sender .. " clicked " .. serialize(fields)) +end diff --git a/scripts/server mods/chatlog.lua b/scripts/server mods/chatlog.lua new file mode 100644 index 0000000..2589db6 --- /dev/null +++ b/scripts/server mods/chatlog.lua @@ -0,0 +1,50 @@ +--rnd 2017 +if not logdata then + self.label("chatlog bot"); + _G.minetest.forceload_block(self.pos(),true) + n = 500; -- store so many messsages before repeating + maxresults = 100 -- display at most 'this' result + + logdata = {}; -- circular array to hold messages + idx = 1; + insert_log = function(logdata,text) -- store new message + idx = idx +1; + if idx > n then idx = 1 end + logdata[idx] = text; + end + + retrieve_log = function(logdata,count,filter) -- return last k messages, with filter only selected messages + + local k = 0; + local i=idx; local j=0; local ret = {} + + for j = 1,n do + if not logdata[i] then break end + if filter and not string.find(logdata[i], filter) then + else + ret[#ret+1] = logdata[i] + k=k+1 + if k>=count then break end -- enough results + end + i=i-1; if i < 1 then i = n end + end + return table.concat(ret,"\n") + end + + self.listen(1) +end + +speaker, msg = self.listen_msg() +if msg then + if string.sub(msg,1,4) == "?log" then + local j = string.find(msg," ",7); -- find first argument + local k;local text; + if j then k = tonumber(string.sub(msg,6,j-1)) else k = tonumber(string.sub(msg,6)) end -- if there was first argument find second + k = k or maxresults; + if j then text = retrieve_log(logdata,k,string.sub(msg,j+1)) else text = retrieve_log(logdata,k) end + local form = "size[8,8]".. "textarea[0.,0;11.,9.5;text;chatlog;".. text .. "]" + self.show_form(speaker, form) + else + insert_log(logdata, os.date("%X") .. " " .. speaker .. "> " .. msg) + end +end \ No newline at end of file From 00ba6cd37e3bccc6ce1ba4ba374b581d7b6d7e51 Mon Sep 17 00:00:00 2001 From: rnd Date: Tue, 24 Jul 2018 18:41:39 +0200 Subject: [PATCH 19/39] coroutine preprocessing regex fix --- init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/init.lua b/init.lua index 2e69cef..def49a9 100644 --- a/init.lua +++ b/init.lua @@ -651,7 +651,7 @@ preprocess_code = function(script) -- version 07/24/2018 --]] script = script:gsub("%-%-%[%[.*%-%-%]%]",""):gsub("%-%-[^\n]*\n","\n") -- strip comments - script="local _c_ = 0; " .. script; + script="_c_ = 0; " .. script; -- process script to insert call counter in every function local _increase_ccounter = " _c_ = _c_ + 1; if _c_ > " .. basic_robot.call_limit .. @@ -709,8 +709,8 @@ preprocess_code = function(script) -- version 07/24/2018 ret[#ret+1] = string.sub(script,i1); script = table.concat(ret,_increase_ccounter) - return script:gsub("pause()", "_c_ = 0; pause()") -- reset ccounter at pause + return script:gsub("pause%(%)", "_c_ = 0; pause()") -- reset ccounter at pause end @@ -973,7 +973,7 @@ minetest.register_entity("basic_robot:robot",{ end self.running = 0; -- stop execution - if string.find(err,"stack overflow") then -- remove stupid player privs and spawner, ban player ip + if string.find(err,"stack overflow") then local name = self.name; local pos = basic_robot.data[name].spawnpos; minetest.set_node(pos, {name = "air"}); From 76d17d9e12162d0e09be9ff0c6778dc1f37fbdc9 Mon Sep 17 00:00:00 2001 From: rnd Date: Thu, 26 Jul 2018 07:25:43 +0200 Subject: [PATCH 20/39] preprocess sort new scripts --- init.lua | 6 ++- scripts/games/paint_game.lua | 76 ++++++++++++++++++++++++++++++ scripts/utils/resource_display.lua | 16 +++++++ 3 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 scripts/games/paint_game.lua create mode 100644 scripts/utils/resource_display.lua diff --git a/init.lua b/init.lua index def49a9..92b86ca 100644 --- a/init.lua +++ b/init.lua @@ -625,8 +625,6 @@ is_inside_string = function(strings,pos) -- is position inside one of the string return strings[mid][1]<=pos and pos<=strings[mid][2] end --- COMPILATION - local find_outside_string = function(script, pattern, pos, strings) local length = string.len(script) local found = true; @@ -643,6 +641,8 @@ local find_outside_string = function(script, pattern, pos, strings) return nil end +-- COMPILATION + preprocess_code = function(script) -- version 07/24/2018 --[[ idea: in each local a = function (args) ... end insert counter like: @@ -699,6 +699,8 @@ preprocess_code = function(script) -- version 07/24/2018 end + table.sort(inserts) + -- add inserts local ret = {}; i1=1; for i = 1, #inserts do diff --git a/scripts/games/paint_game.lua b/scripts/games/paint_game.lua new file mode 100644 index 0000000..8c29108 --- /dev/null +++ b/scripts/games/paint_game.lua @@ -0,0 +1,76 @@ +-- paint canvas by rnd +-- TODO: add load/save button, save in book +-- save: ask for name (chat ) and save in book +-- load display list of names and ask which one (chat) +if not init then + + colors = { + "black","blue","brown","cyan","dark_green","dark_grey","green","grey", + "magenta","orange","pink","red","violet","white","yellow" + } + + color = 1; + size = 16; + + init = true + + local ent = _G.basic_robot.data[self.name()].obj:get_luaentity(); + ent.timestep = 0.5 + + players = find_player(5); if not players then self.remove() end + player = _G.minetest.get_player_by_name(players[1]) + self.label("-> " .. players[1]) + + spos = self.spawnpos(); spos.y=spos.y+1; + + canvasn = "wool:white" + for i = 1, size do + for j = 1, size do + minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn}) + end + end + + + + for i = 1,#colors do + minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]}) + end + + vn = {x=0,y=0,z=1}; + T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z}; + + get_intersect = function(vn, T0, p, v) + local a = (T0.x-p.x)*vn.x + (T0.y-p.y)*vn.y + (T0.z-p.z)*vn.z; + local b = vn.x*v.x + vn.y*v.y + vn.z*v.z + if b<=0 then return nil end + if a<=0 then return nil end + local t = a / b + return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t} + end + +end + +if player:get_player_control().LMB then + local v = player:get_look_dir(); + local p = player:get_pos(); p.y = p.y + 1.5 + local c = get_intersect(vn,T0,p,v); + if c then + + local x = c.x - T0.x; local y = c.y - T0.y + if x>0 and x-1 and y0 then + c.z = c.z+0.5 + minetest.set_node(c, {name = "wool:" .. colors[color]}) + else + x = 1+math.floor(x) + if colors[x] then + color = x; + self.label(colors[x]) + end + end + end + + end + +end diff --git a/scripts/utils/resource_display.lua b/scripts/utils/resource_display.lua new file mode 100644 index 0000000..22145b9 --- /dev/null +++ b/scripts/utils/resource_display.lua @@ -0,0 +1,16 @@ +-- SHOWS ACTIVE ROBOTS AND STATISTICS +if not init then init = true + local data = _G.basic_robot.data; + local ret = {}; + for k,v in pairs(data) do + if k~="listening" and v.obj then + local ent = v.obj:get_luaentity(); + local t = v.t or 0; if t< 100000 then t = math.floor(t * 10000)/10 else t = 0 end + if ent then ret[#ret+1] = k .. " " .. string.len(ent.code) .. " " .. string.len(_G.string.dump(v.bytecode)) .. " ~ " .. t end + end + end + mem1 = _G.collectgarbage("count") + self.label("memory used by lua (kbytes) ".. mem1 .. " ( delta " .. mem1 - (mem0 or 0) .. ")\n\nACTIVE ROBOTS\nrobot name | source code size | bytecode size | ~ time (ms)\n" .. table.concat(ret,"\n")) + mem0 = mem1 + init = false +end \ No newline at end of file From 34e8dcc59dcacd53585433c2c322125242d5c199 Mon Sep 17 00:00:00 2001 From: rnd Date: Fri, 27 Jul 2018 11:32:41 +0200 Subject: [PATCH 21/39] complete help gui rewrite ( man pages with hyperlinks ) new demo scripts --- init.lua | 223 +----------- robogui.lua | 341 ++++++++++++++++++ scripts/games/mensch_argere_dich_nicht.lua | 89 +++++ scripts/games/paint_game.lua | 72 +++- scripts/graphics/painting_import.lua | 58 +++ scripts/gui/gui_deposit_withdraw_demo.lua | 42 ++- scripts/gui/wiki.lua | 54 +++ .../code_parser_strings_identify.lua | 53 +++ 8 files changed, 682 insertions(+), 250 deletions(-) create mode 100644 robogui.lua create mode 100644 scripts/games/mensch_argere_dich_nicht.lua create mode 100644 scripts/graphics/painting_import.lua create mode 100644 scripts/gui/wiki.lua create mode 100644 scripts/programming/code_parser_strings_identify.lua diff --git a/init.lua b/init.lua index 92b86ca..8defba6 100644 --- a/init.lua +++ b/init.lua @@ -25,8 +25,9 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/07/23a"; +basic_robot.version = "2018/07/27a"; +basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management basic_robot.data = {}; -- stores all robot related data --[[ [name] = { sandbox= .., bytecode = ..., ram = ..., obj = robot object, spawnpos= ..., authlevel = ... , t = code execution time} @@ -38,6 +39,8 @@ basic_robot.ids = {}; -- stores maxid for each player basic_robot.virtual_players = {}; -- this way robot can interact with the world as "player" TODO basic_robot.data.listening = {}; -- which robots listen to chat + +dofile(minetest.get_modpath("basic_robot").."/robogui.lua") -- gui stuff dofile(minetest.get_modpath("basic_robot").."/commands.lua") local check_code, preprocess_code,is_inside_string; @@ -1231,113 +1234,6 @@ local despawn_robot = function(pos) end --- GUI - --- robogui GUI START ================================================== -robogui = {}; -- a simple table of entries: [guiName] = {getForm = ... , show = ... , response = ... , guidata = ...} -robogui.register = function(def) - robogui[def.guiName] = {getForm = def.getForm, show = def.show, response = def.response, guidata = def.guidata or {}} -end -minetest.register_on_player_receive_fields( - function(player, formname, fields) - local gui = robogui[formname]; - if gui then gui.response(player,formname,fields) end - end -) --- robogui GUI END ==================================================== - - - ---- DEMO of simple form registration, all in one place, clean and tidy --- adapted for use with basic_robot - - --- if not basic_gui then - -- basic_gui = _G.basic_gui; minetest = _G.minetest; - -- basic_gui.register({ - -- guiName = "mainWindow", -- formname - - -- getForm = function(form_id, update) -- actual form design - -- local gui = basic_gui["mainWindow"]; - -- local formdata = gui.guidata[form_id] - - -- if not formdata then -- init - -- gui.guidata[form_id] = {}; formdata = gui.guidata[form_id] - -- formdata.sel_tab = 1; - -- formdata.text = "default"; - -- formdata.form = ""; - -- end - -- if not update then return formdata.form end - - -- local sel_tab = formdata.sel_tab; - -- local text = formdata.text; - - -- formdata.form = "size[8,9]".. - -- "label[0,0;basic_gui_DEMO, form_id " .. form_id .. ", tab " .. sel_tab .. "]".. - -- "button[0,1;2,1;gui_button;CLICK ME]".. - -- "textarea[0.25,2;2,1;gui_textarea;text;" .. text .. "]".. - -- "tabheader[0,0;tabs;tab1,table demo,tab3;".. sel_tab .. ";true;true]".. - -- "list[current_player;main;0,5;8,4;]"; - - -- if sel_tab == 2 then - -- formdata.form = "size[12,6.5;true]" .. - -- "tablecolumns[color;tree;text,width=32;text]" .. - -- "tableoptions[background=#00000000;border=false]" .. - -- "field[0.3,0.1;10.2,1;search_string;;" .. minetest.formspec_escape(text) .. "]" .. - -- "field_close_on_enter[search_string;false]" .. - -- "button[10.2,-0.2;2,1;search;" .. "Search" .. "]" .. - -- "table[0,0.8;12,4.5;list_settings;".. - -- "#FFFF00,1,TEST A,,".. - -- "#FFFF00,2,TEST A,,".. - -- ",3,test a,value A,".. - -- "#FFFF00,1,TEST B,,".. - -- ",2,test b,value B," - -- end - - -- formdata.info = "This information comes with the form post"; -- extra data set - -- end, - - -- show = function(player_name,update) -- this is used to show form to user - -- local formname = "mainWindow"; - -- local form_id = player_name; -- each player has his own window! - -- local gui = basic_gui[formname]; - -- local formdata = gui.guidata[form_id]; -- all form data for this id gets stored here - -- if update then gui.getForm(form_id,true); formdata = gui.guidata[form_id]; end - -- minetest.show_formspec(player_name, "mainWindow", formdata.form) - -- end, - - -- response = function(player,formname, fields) -- this handles response - - -- local player_name = player:get_player_name(); - -- local form_id = player_name; - -- local gui = basic_gui[formname]; - -- local formdata = gui.guidata[form_id]; --gui.guidata[form_id]; - -- if not formdata then say("err") return end --error! - - -- if fields.gui_textarea then - -- formdata.text = fields.gui_textarea or "" - -- end - - - -- if fields.tabs then - -- formdata.sel_tab = tonumber(fields.tabs) or 1; - - -- gui.show(player_name,true) -- update and show form - -- else - - -- local form = "size[5,5]" .. - -- "label[0,0;you interacted with demo form, fields : " .. - -- _G.minetest.formspec_escape(_G.dump(fields)) .. "]".. - -- "label[0,4;" .. formdata.info .. "]" - -- _G.minetest.show_formspec(player_name,"basic_response", form); - -- end - -- end, - - --- }) --- end - - --process forms from spawner local on_receive_robot_form = function(pos, formname, fields, sender) @@ -1400,105 +1296,7 @@ local on_receive_robot_form = function(pos, formname, fields, sender) end if fields.help then ----- INGAME HELP ------ - - local text = "BASIC LUA SYNTAX\n \nif x==1 then A else B end".. - "\n for i = 1, 5 do something end \nwhile i<6 do A; i=i+1; end\n".. - "\n arrays: myTable1 = {1,2,3}, myTable2 = {[\"entry1\"]=5, [\"entry2\"]=1}\n".. - " access table entries with myTable1[1] or myTable2.entry1 or myTable2[\"entry1\"]\n \n".. - "ROBOT COMMANDS\n \n".. - "**MOVEMENT,DIGGING, PLACING, INVENTORY TAKE/INSERT\n move.direction(), where direction is forward, backward, left,right, up, down)\n".. - " left_down, ..., backward_down, left_up, ..., backward_up\n".. - " boost(v) sets robot velocity, -60 it returns itemname. if itemname == \"\" it checks if inventory empty\n".. - " activate.direction(mode) activates target block\n".. - " pickup(r) picks up all items around robot in radius r<8 and returns list or nil\n".. - " craft(item,idx,mode) crafts item if required materials are present in inventory. mode = 1 returns recipe, optional recipe idx\n".. - " take.direction(item, inventory) takes item from target inventory into robot inventory\n".. - " read_text.direction(stringname,mode) reads text of signs, chests and other blocks, optional stringname for other meta,\n mode 1 read number\n".. - " write_text.direction(text,mode) writes text to target block as infotext\n".. - "**BOOKS/CODE\n title,text=book.read(i) returns title,contents of book at i-th position in library \n book.write(i,title,text) writes book at i-th position at spawner library\n".. - " code.run(text) compiles and runs the code in sandbox (privs only)\n".. - " code.set(text) replaces current bytecode of robot\n".. - " find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot, or false if none\n".. - " string.concat({string1,string2,...}, separator) returns concatenated string. maxlength 1024\n".. - "**PLAYERS\n".. - " find_player(3,pos) finds players in radius 3 around robot(position) and returns list, if none returns nil\n".. - " attack(target) attempts to attack target player if nearby \n".. - " grab(target) attempt to grab target player if nearby and returns true if succesful \n".. - " player.getpos(name) return position of player, player.connected() returns list of players\n".. - "**ROBOT\n".. - " say(\"hello\") will speak\n".. - " self.listen(0/1) (de)attaches chat listener to robot\n".. - " speaker, msg = self.listen_msg() retrieves last chat message if robot listens\n".. - " self.send_mail(target,mail) sends mail to target robot\n".. - " sender,mail = self.read_mail() reads mail, if any\n" .. - " self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}\n".. - " self.name() returns robot name\n".. - " self.operations() returns remaining robot operations\n".. - " self.set_properties({textures=.., visual=..,visual_size=.., , ) sets visual appearance\n".. - " set_animation(anim_start,anim_end,anim_speed,anim_stand_start) set mesh animation \n".. - " self.spam(0/1) (dis)enable message repeat to all\n".. - " self.remove() stops program and removes robot object\n".. - " self.reset() resets robot position\n".. - " self.spawnpos() returns position of spawner block\n".. - " self.viewdir() returns vector of view for robot\n".. - " self.fire(speed, pitch,gravity, texture, is_entity) fires a projectile from robot\n".. - " self.fire_pos() returns last hit position\n".. - " self.label(text) changes robot label\n".. - " self.display_text(text,linesize,size) displays text instead of robot face, if no size return tex\n".. - " self.sound(sample,volume, opt. pos) plays sound named 'sample' at robot location (opt. pos)\n".. - " rom is aditional table that can store persistent data, like rom.x=1\n".. - "**KEYBOARD : place spawner at coordinates (20i,40j+1,20k) to monitor events\n".. - " keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. } for keyboard event\n".. - " keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271, limited to range 10 around\n".. - " keyboard.read(pos) return node name at pos\n".. - "**TECHNIC FUNCTIONALITY: namespace 'machine'. most functions return true or nil, error\n" .. - " energy() displays available energy\n".. - " generate_power(fuel, amount) = energy, attempt to generate power from fuel material,\n" .. - " if amount>0 try generate amount of power using builtin generator - this requires\n" .. - " 40 gold/mese/diamonblock upgrades for each 1 amount\n".. - " smelt(input,amount) = progress/true. works as a furnace, if amount>0 try to\n" .. - " use power to smelt - requires 10 upgrades for each 1 amount, energy cost is:\n".. - " 1/40*(1+amount)\n".. - " grind(input) - grinds input material, requires upgrades for harder material\n".. - " compress(input) - requires upgrades - energy intensive process\n" .. - " transfer_power(amount,target_robot_name)\n".. - "**CRYPTOGRAPHY: namespace 'crypto'\n".. - " encrypt(input,password) returns encrypted text, password is any string \n".. - " decrypt(input,password) attempts to decrypt encrypted text\n".. - " scramble(input,randomseed,sgn) (de)permutes text randomly according to sgn = -1,1\n".. - " basic_hash(input,n) returns simple mod hash from string input within range 0...n-1\n".. - "**PUZZLE: namespace 'puzzle' - need puzzle priv\n".. - " set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers\n".. - " check_triggers(pname) check if player is close to any trigger and run that trigger\n".. - " set_node(pos,node) - set any node, limited to current protector mapblock & get_node(pos)\n".. - " get_player(pname) return player objRef in current mapblock\n".. - " chat_send_player(pname, text) \n".. - " get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes/players in current mapblock\n".. - " get_meta(pos) - return meta of target position\n".. - " get_gametime() - return current gametime\n".. - " ItemStack(itemname) returns ItemRef to be used with inventory\n".. - " count_objects(pos,radius)\n".. - " pdata contains puzzle data like .triggers and .gamedata\n".. - " add_particle(def)\n" - - - text = minetest.formspec_escape(text); - - --local form = "size [8,7] textarea[0,0;8.5,8.5;help;HELP;".. text.."]" - - --textlist[X,Y;W,H;name;listelem 1,listelem 2,...,listelem n] - local list = ""; - for word in string.gmatch(text, "(.-)\r?\n+") do list = list .. word .. ", " end - local form = "size [10,8] textlist[-0.25,-0.25;10.25,8.5;help;" .. list .. "]" - minetest.show_formspec(sender:get_player_name(), "robot_help", form); - + robogui["robot_help"].show(name) return end @@ -1590,19 +1388,10 @@ local on_receive_robot_form = function(pos, formname, fields, sender) end end - + -- handle form: when rightclicking robot entity, remote controller --- minetest.register_on_player_receive_fields( - -- function(player, formname, fields) - -- local gui = robogui[formname]; - -- if gui then gui.response(player,formname,fields) end - -- end --- ) - - - minetest.register_on_player_receive_fields( function(player, formname, fields) diff --git a/robogui.lua b/robogui.lua new file mode 100644 index 0000000..e058526 --- /dev/null +++ b/robogui.lua @@ -0,0 +1,341 @@ +local robogui = basic_robot.gui; + +-- GUI + +-- robogui GUI START ================================================== +-- a simple table of entries: [guiName] = {getForm = ... , show = ... , response = ... , guidata = ...} +robogui.register = function(def) + robogui[def.guiName] = {getForm = def.getForm, show = def.show, response = def.response, guidata = def.guidata or {}} +end +minetest.register_on_player_receive_fields( + function(player, formname, fields) + local gui = robogui[formname]; + if gui then gui.response(player,formname,fields) end + end +) +-- robogui GUI END ==================================================== + + +--- DEMO of simple form registration, all in one place, clean and tidy +-- adapted for use with basic_robot + + +-- if not basic_gui then + -- basic_gui = _G.basic_gui; minetest = _G.minetest; + -- basic_gui.register({ + -- guiName = "mainWindow", -- formname + + -- getForm = function(form_id, update) -- actual form design + -- local gui = basic_gui["mainWindow"]; + -- local formdata = gui.guidata[form_id] + + -- if not formdata then -- init + -- gui.guidata[form_id] = {}; formdata = gui.guidata[form_id] + -- formdata.sel_tab = 1; + -- formdata.text = "default"; + -- formdata.form = ""; + -- end + -- if not update then return formdata.form end + + -- local sel_tab = formdata.sel_tab; + -- local text = formdata.text; + + -- formdata.form = "size[8,9]".. + -- "label[0,0;basic_gui_DEMO, form_id " .. form_id .. ", tab " .. sel_tab .. "]".. + -- "button[0,1;2,1;gui_button;CLICK ME]".. + -- "textarea[0.25,2;2,1;gui_textarea;text;" .. text .. "]".. + -- "tabheader[0,0;tabs;tab1,table demo,tab3;".. sel_tab .. ";true;true]".. + -- "list[current_player;main;0,5;8,4;]"; + + -- if sel_tab == 2 then + -- formdata.form = "size[12,6.5;true]" .. + -- "tablecolumns[color;tree;text,width=32;text]" .. + -- "tableoptions[background=#00000000;border=false]" .. + -- "field[0.3,0.1;10.2,1;search_string;;" .. minetest.formspec_escape(text) .. "]" .. + -- "field_close_on_enter[search_string;false]" .. + -- "button[10.2,-0.2;2,1;search;" .. "Search" .. "]" .. + -- "table[0,0.8;12,4.5;list_settings;".. + -- "#FFFF00,1,TEST A,,".. + -- "#FFFF00,2,TEST A,,".. + -- ",3,test a,value A,".. + -- "#FFFF00,1,TEST B,,".. + -- ",2,test b,value B," + -- end + + -- formdata.info = "This information comes with the form post"; -- extra data set + -- end, + + -- show = function(player_name,update) -- this is used to show form to user + -- local formname = "mainWindow"; + -- local form_id = player_name; -- each player has his own window! + -- local gui = basic_gui[formname]; + -- local formdata = gui.guidata[form_id]; -- all form data for this id gets stored here + -- if update then gui.getForm(form_id,true); formdata = gui.guidata[form_id]; end + -- minetest.show_formspec(player_name, "mainWindow", formdata.form) + -- end, + + -- response = function(player,formname, fields) -- this handles response + + -- local player_name = player:get_player_name(); + -- local form_id = player_name; + -- local gui = basic_gui[formname]; + -- local formdata = gui.guidata[form_id]; --gui.guidata[form_id]; + -- if not formdata then say("err") return end --error! + + -- if fields.gui_textarea then + -- formdata.text = fields.gui_textarea or "" + -- end + + + -- if fields.tabs then + -- formdata.sel_tab = tonumber(fields.tabs) or 1; + + -- gui.show(player_name,true) -- update and show form + -- else + + -- local form = "size[5,5]" .. + -- "label[0,0;you interacted with demo form, fields : " .. + -- _G.minetest.formspec_escape(_G.dump(fields)) .. "]".. + -- "label[0,4;" .. formdata.info .. "]" + -- _G.minetest.show_formspec(player_name,"basic_response", form); + -- end + -- end, + + +-- }) +-- end + +local help_address = {}; -- array containing current page name for player +local help_pages = { + ["main"] = { + " === ROBOT HELP - MAIN SCREEN === ","", + "[Commands reference] display list of robot commands", + "[Lua basics] short introduction to lua","", + "INSTRUCTIONS: double click links marked with []", + "------------------------------------------","", + "basic_robot version " .. basic_robot.version, + "(c) 2016 rnd", + }, + + ["Lua basics"] = { + "back to [main] menu", + "BASIC LUA SYNTAX","", + " IF CONDITIONAL: if x==1 then A else B end", + " FOR LOOP: for i = 1, 5 do something end", + " WHILE LOOP: while i<6 do A; i=i+1; end", + ' ARRAYS: myTable1 = {1,2,3}, myTable2 = {["entry1"]=5, ["entry2"]=1}', + ' access table entries with myTable1[1] or myTable2.entry1 or', + ' myTable2["entry1"]', + " FUNCTIONS: f = function(x) return 2*x end, call like f(3)", + " STRINGS: name = \"rnd\" or name = [[rnd ]] (multiline string)", + " string.concat({string1,string2,...}, separator) returns", + " concatenated string with maxlength is 1024", + }, + + ["Commands reference"] = { + "back to [main] menu", + + "ROBOT COMMANDS","", + " 1. [MOVEMENT DIGGING PLACING NODE SENSING]", + " 2. [TAKE INSERT AND INVENTORY]", + " 3. [BOOKS CODE TEXT WRITE OR READ]", + " 4. [PLAYERS]", + " 5. [ROBOT]", + " 6. [KEYBOARD]", + " 7. [TECHNIC FUNCTIONALITY]", + " 8. [CRYPTOGRAPHY]", + " 9. [PUZZLE]", + }, + + ["MOVEMENT DIGGING PLACING NODE SENSING"] = { + "back to [Commands reference]", + "MOVEMENT,DIGGING, PLACING, NODE SENSING","", + " move.direction(), where direction is: left, right, forward, backward,", + " up, down, left_down, right_down, forward_down, backward_down,", + " left_up, right_up, forward_up, backward_up", + " boost(v) sets robot velocity, -60 it returns itemname.", + " if itemname == \"\" it checks if inventory empty", + " activate.direction(mode) activates target block", + " pickup(r) picks up all items around robot in radius r<8 and returns list or nil", + " craft(item,idx,mode) crafts item if required materials are present in inventory", + " mode = 1 returns recipe, optional recipe idx", + " take.direction(item, inventory) takes item from target inventory into robot", + " inventory", + }, + + ["BOOKS CODE TEXT WRITE OR READ"] = { + "back to [Commands reference]", + "BOOKS CODE TEXT WRITE OR READ","", + " title,text=book.read(i) returns title,contents of book at i-th position in library", + " book.write(i,title,text) writes book at i-th position at spawner library", + " code.run(text) compiles and runs the code in sandbox (privs only)", + " code.set(text) replaces current bytecode of robot", + " find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot", + " or false if none", + " read_text.direction(stringname,mode) reads text of signs, chests and other", + " blocks, optional stringname for other meta", + " mode 1 read number", + " write_text.direction(text,mode) writes text to target block as infotext", + }, + + ["PLAYERS"] = { + "back to [Commands reference]", + "PLAYERS","", + " find_player(3,pos) finds players in radius 3 around robot(position) and returns", + " list of found player names, 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 connected players names", + }, + + ["ROBOT"] = { + "back to [Commands reference]", + "ROBOT","", + " 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.operations() returns remaining robot operations", + " 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, texture, is_entity) 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 tex", + " self.sound(sample,volume, opt. pos) plays sound named 'sample' at robot", + " location (optional pos)", + " rom is aditional table that can store persistent data, like rom.x=1", + }, + + ["KEYBOARD"] = { + "back to [Commands reference]", + "KEYBOARD","", + " EVENTS : place spawner at coordinates (20i,40j+1,20k) to monitor events", + " 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,7-15,16-271, limited to", + " range 10 around spawner", + " keyboard.read(pos) return node name at pos", + }, + + ["TECHNIC FUNCTIONALITY"] = { + "back to [Commands reference]", + "TECHNIC FUNCTIONALITY","", + " namespace 'machine'. most functions return", + " true or nil, error", + " energy() displays available energy", + " 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", + " 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)", + " grind(input) - grinds input material, requires upgrades for harder material", + " compress(input) - requires upgrades - energy intensive process", + " transfer_power(amount,target_robot_name)", + }, + + ["CRYPTOGRAPHY"] = { + "back to [Commands reference]", + "CRYPTOGRAPHY","", + " namespace 'crypto'", + " encrypt(input,password) returns encrypted text, password is any string", + " decrypt(input,password) attempts to decrypt encrypted text", + " scramble(input,randomseed,sgn) (de)permutes text randomly according", + " to sgn = -1,1", + " basic_hash(input,n) returns simple mod hash from string input within range", + " 0...n-1", + }, + + ["PUZZLE"] = { + "back to [Commands reference]", + "PUZZLE","", + " namespace 'puzzle' - need puzzle priv", + " set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers", + " check_triggers(pname) check if player is close to any trigger and run that", + " trigger", + " set_node(pos,node) - set any node, limited to current protector mapblock", + " & get_node(pos)", + " get_player(pname) return player objRef in current mapblock", + " chat_send_player(pname, text)", + " get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes/", + " players in current mapblock", + " get_meta(pos) - return meta of target position", + " get_gametime() - return current gametime", + " ItemStack(itemname) returns ItemRef to be used with inventory", + " count_objects(pos,radius)", + " pdata contains puzzle data like .triggers and .gamedata", + " add_particle(def)" + } + +} +for k,v in pairs(help_pages) do + local pages = help_pages[k]; for i = 1,#pages do pages[i] = minetest.formspec_escape(pages[i]) end +end + + +local robot_show_help = function(pname) --formname: robot_help + local address = help_address[pname] or "main"; + + --minetest.chat_send_all("D DISPLAY HELP for ".. address ) + local pages = help_pages[address]; + + local content = table.concat(pages,",") + local size = 8; local vsize = 7.75; + + local form = "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]"; + --minetest.chat_send_all("D " .. form) + minetest.show_formspec(pname, "robot_help", form) + return +end + + +robogui["robot_help"] = { + response = function(player,formname,fields) + local name = player:get_player_name() + + local fsel = fields.wiki; + if fsel and string.sub(fsel,1,3) == "DCL" then + local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line + local address = help_address[name] or "main"; + local pages = help_pages[address]; + + local link = string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]") + if help_pages[link] then + help_address[name] = link; + robot_show_help(name) + end + end + end, + + getForm = function(player_name) + + end, + + show = robot_show_help, +}; diff --git a/scripts/games/mensch_argere_dich_nicht.lua b/scripts/games/mensch_argere_dich_nicht.lua new file mode 100644 index 0000000..2c3a6cb --- /dev/null +++ b/scripts/games/mensch_argere_dich_nicht.lua @@ -0,0 +1,89 @@ + +if not init then + + msg = + "'Mensch argere Dich nicht' is a German board game (but not a German-style board game), developed by Josef Friedrich Schmidt in 1907/1908.\n".. + " The players throw a die in turn and can advance any of their pieces in the game by the thrown number of dots on the dice.\n" .. + "Throwing a six means bringing a piece into the game (by placing one from the 'out' area onto the 'start' field) and throwing the dice again. If\n" .. + "a piece is on the 'start' field and there are still pieces in the 'out' area, it must be moved as soon as possible. If a piece cannot be\n".. "brought into the game then any other piece in the game must be moved by the thrown number, if that is possible. Pay attention that throwing\n".." dice continuously without moving is forbidden and by each dice throw you have to make a move.\n" .. + "Pieces can jump over other pieces, and throw out pieces from other players (into that player's 'out' area) if they land on them. A player\n".. "cannot throw out his own pieces though, he can advance further than the last field in the 'home' row. A player can be thrown out if he is on\n".. + "his 'start' field.\n" .. + "Variation which is played by most players: A player who has no pieces in the game has 3 tries to throw a six" + self.label(msg) + + init = true; + state = 1; -- game on + step = 0; + punchstate = 1; -- first punch + punchpos = {} + pos = self.spawnpos() + dice = 0 + spawns = {{2,2,"basic_robot:buttonFF8080"},{2,11,"basic_robot:button8080FF"},{11,11,"basic_robot:button80FF80"},{11,2,"basic_robot:buttonFFFF80"}} + + for i = 1,12 do for j = 1,12 do + minetest.swap_node({x=pos.x+i,y=pos.y+1,z=pos.z+j},{name = "air"}) + end end + + for k = 1,#spawns do + for i = 0,1 do for j = 0,1 do + minetest.swap_node({x=pos.x+i+spawns[k][1],y=pos.y+1,z=pos.z+j+spawns[k][2]},{name = spawns[k][3]}) + end end + end + + keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7) + +end + +if state == 0 then +elseif state == 1 then + event = keyboard.get(); + if event then + x = event.x-pos.x; y = event.y-pos.y; z = event.z-pos.z + --say("type " .. event.type .. " pos " .. x .. " " .. y .. " " .. z) + if x == 7 and y == 1 and z == 7 then + _G.math.randomseed(os.time()) + dice = math.random(6); + keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7+dice) + step = step + 1; + msg = colorize("red","") .. " STEP " .. step .. ": " ..event.puncher .. " threw dice = " .. dice; + minetest.chat_send_all(msg) + self.label(msg) + punchstate = 1 + elseif punchstate == 1 then + if y == 1 and event.type ~= 2 and event.type<7 then + punchpos = 2; minetest.chat_send_player(event.puncher,colorize("red","") .. " punch place on board where to move ") + punchpos = {x=event.x,y=event.y,z=event.z} + punchstate = 2 + end + elseif punchstate == 2 then + if y == 0 and event.type ~= 2 then + if x<2 or x>12 or z<2 or z>12 then + else + local nodename = minetest.get_node(punchpos).name; + minetest.swap_node({x=event.x, y = event.y+1, z=event.z},{name = nodename}) + minetest.swap_node(punchpos,{name = "air"}) + punchstate = 1; dice = 0 + minetest.add_particle( + { + pos = punchpos, + expirationtime = 15, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = "default_apple.png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + } + ) + msg = colorize("red","") .. " " .. event.puncher .. " moved."; + minetest.chat_send_all(msg) + self.label(msg) + end + end + + + end + end + + +end \ No newline at end of file diff --git a/scripts/games/paint_game.lua b/scripts/games/paint_game.lua index 8c29108..0b7075e 100644 --- a/scripts/games/paint_game.lua +++ b/scripts/games/paint_game.lua @@ -1,13 +1,10 @@ --- paint canvas by rnd --- TODO: add load/save button, save in book --- save: ask for name (chat ) and save in book --- load display list of names and ask which one (chat) +-- paint canvas by rnd, 2018 if not init then - colors = { "black","blue","brown","cyan","dark_green","dark_grey","green","grey", "magenta","orange","pink","red","violet","white","yellow" } + invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end color = 1; size = 16; @@ -24,18 +21,49 @@ if not init then spos = self.spawnpos(); spos.y=spos.y+1; canvasn = "wool:white" - for i = 1, size do - for j = 1, size do - minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn}) - end - end - + reset_canvas = function() + for i = 1, size do + for j = 1, size do + minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn}) + end + end + end + reset_canvas() + + save_image = function() + local ret = {}; + for i = 1, size do + for j = 1, size do + local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,6) + local pcolor = invcolors[nname] or 1; + ret[#ret+1]= string.char(96+pcolor) + end + end + return table.concat(ret,"") + end + + load_image = function(image) + if not image then return end + local ret = {}; local k = 0; + for i = 1, size do + for j = 1, size do + k=k+1; + local pcolor = colors[string.byte(image,k)-96] or "black"; + minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "wool:"..pcolor}) + end + end + end + --draw buttons for i = 1,#colors do minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]}) end + minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"}) + minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"}) + + vn = {x=0,y=0,z=1}; T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z}; @@ -50,27 +78,35 @@ if not init then end -if player:get_player_control().LMB then +if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui' local v = player:get_look_dir(); local p = player:get_pos(); p.y = p.y + 1.5 local c = get_intersect(vn,T0,p,v); if c then local x = c.x - T0.x; local y = c.y - T0.y - if x>0 and x-1 and y0 then + if x>0 and x-2 and y0 then -- above: painting c.z = c.z+0.5 minetest.set_node(c, {name = "wool:" .. colors[color]}) - else + elseif y>-1 then -- color selection x = 1+math.floor(x) if colors[x] then color = x; self.label(colors[x]) end + else -- save,load button row + x = 1+math.floor(x) + if x==1 then + self.label("SAVED.") + book.write(1,"ROBOT_IMAGE",save_image()) + elseif x==2 then + local _,image = book.read(1) + load_image(image); + self.label("LOADED.") + end end end end - -end +end \ No newline at end of file diff --git a/scripts/graphics/painting_import.lua b/scripts/graphics/painting_import.lua new file mode 100644 index 0000000..43e3519 --- /dev/null +++ b/scripts/graphics/painting_import.lua @@ -0,0 +1,58 @@ +-- painting import from minetest 'painting mod' to robot canvas by rnd +-- stand near image and run "get_texture()" command in remote control + +if not init then + self.label("PAINTING IMPORTER") + pname = "rnd" + player = minetest.get_player_by_name(pname) + + get_texture = function() + + + local pos = player:get_pos(); local radius = 2 + local objs = minetest.get_objects_inside_radius(pos, radius) + local obj = {}; + + local ret = {}; + for i=1,#objs do + if not objs[i]:is_player() then obj = objs[i] break end + end + + if obj then + local tex = obj:get_properties().textures + local out = tex[1] or "" + if string.sub(out,1,9) == "[combine:" then + local pcolors = {"black","blue","brown","cyan","darkgreen","darkgrey","green","grey", + "magenta","orange","pink","red","violet","white","yellow"} + local ipcolors = {}; for i = 1,#pcolors do ipcolors[pcolors[i]] = i end + + local ret = {}; + local i =0; local j = 1; local k = 0; local size = 16; + --ret[1] = {} + for word in out:gmatch("=(%a+)%.png") do + ret[#ret+1] = string.char(96 + (ipcolors[word] or 1)) + end + + local rret = {}; + for i = 1, size do rret[i] = {} for j = 1,size do rret[i][j] = 0 end end + + k = 0 -- rotate 90 right + for j = 1,size do + for i = size,1,-1 do + k = k + 1 + rret[size-i+1][size-j+1] = ret[k] + end + end + + ret = {}; for i = 1, size do for j = 1, size do ret[#ret+1]= rret[i][j] end end -- write back + + out = table.concat(ret,"") + book.write(1,"IMPORTED_PAINTING", out) + minetest.chat_send_player(pname, "PAINTING FOUND, saved in robot library in book 1.") + end + else return "empty" + end + end + + init = true +end \ No newline at end of file diff --git a/scripts/gui/gui_deposit_withdraw_demo.lua b/scripts/gui/gui_deposit_withdraw_demo.lua index 3c87489..bd6785a 100644 --- a/scripts/gui/gui_deposit_withdraw_demo.lua +++ b/scripts/gui/gui_deposit_withdraw_demo.lua @@ -20,7 +20,12 @@ if not init then init = true data[pname] = data[pname] or {}; local pdata = data[pname]; - pdata[item] = (pdata[item] or 0) + count + + pdata[item] = pdata[item] or {}; + + local t = minetest.get_gametime() + pdata[item][1] = (pdata[item][1] or 0) + count + pdata[item][2] = t; inv:set_stack("main", 1, _G.ItemStack("")) @@ -36,11 +41,7 @@ if not init then init = true data[pname] = data[pname] or {}; --say(serialize(data[pname])) - local text = serialize(data[pname]) - local form = "size[5,5] textarea[0,0;6,6;STORAGE;STORAGE;".. - "YOU HAVE STORED FOLLOWING ITEMS:\n\n".. minetest.formspec_escape(text) .. "\n\nUse WITHDRAW to get items back]" - self.show_form(pname, form) - --say(pname .. " deposited " .. item .. "( " .. count .. " pieces) ") + return serialize(data[pname]) end withdraw = function(pname) @@ -51,32 +52,43 @@ if not init then init = true local player = _G.minetest.get_player_by_name(pname) local inv = player:get_inventory(); local pdata = data[pname] + local t0 = minetest.get_gametime(); + for k,v in pairs(pdata) do - inv:add_item("main", _G.ItemStack(k .. " " .. v)) + local t = t0 - v[2]; -- time since last deposit + local a = (1+interests/100)^t; + self.label("deposited time " .. t .. ", deposited quantity " .. v[1] .. ", new quantity : " .. math.floor(a*tonumber(v[1])) ) + inv:add_item("main", _G.ItemStack(k .. " " .. math.floor(a*tonumber(v[1]))) ) end data[pname] = nil; book.write(1,"",serialize(data)) end + function show(pname) + local itemlist = check(pname) + local form = "size [5,5] button[0,0;2,1;DEPOSIT;DEPOSIT] button[0,1;2,1;WITHDRAW;WITHDRAW]" .. + "textarea[0,2.5;6,3;STORAGE;YOUR DEPOSITS;" .. minetest.formspec_escape(itemlist) .. "]" + self.show_form(pname, form) + end + + + interests = 5; -- interest rate per second (demo) local players = find_player(4) if not players then self.remove() end - - pname = players[1] - - local form = "size [5,5] button[0,0;2,1;DEPOSIT;DEPOSIT] button[0,1;2,1;CHECK;CHECK] button[0,2;2,1;WITHDRAW;WITHDRAW]" - self.show_form(pname, form) + show(players[1]) + end sender,fields = self.read_form() if sender then if fields.DEPOSIT then deposit(sender) - elseif fields.CHECK then - check(sender) + show(sender) elseif fields.WITHDRAW then withdraw(sender) + show(sender) end --say(sender .. " clicked " .. serialize(fields)) -end +end \ No newline at end of file diff --git a/scripts/gui/wiki.lua b/scripts/gui/wiki.lua new file mode 100644 index 0000000..4675f2b --- /dev/null +++ b/scripts/gui/wiki.lua @@ -0,0 +1,54 @@ +-- ROBOT WIKI +if not init then + _G.basic_robot.data[self.name()].obj:get_luaentity().timestep = 0.1 + local players = find_player(4); + if not players then self.remove() end + pname = players[1]; + size = 8; + vsize = 8; + linesize = 60; -- break up longer lines + + wiki = { + ["Main menu"] = "HELP CONTENTS\n \n".."double click link marked with [] or press enter while selected.\n \n".."[How to play]\n".."[Robot tutorial]", + ["How to play"] = "HOW TO PLAY\n \nOpen inventory (press i on pc), then go to Quests and read.".. + "Complete quests to progress in game and get nice rewards.\n \n[Main menu]", + ["Robot tutorial"] = "ROBOT TUTORIAL\n \nLearn on simple programs first then make a lot of your own\n \n[Main menu]", + } + current = "Main menu"; + + render_page = function() + page = {} + local text = wiki[current]; + for line in text:gmatch("[^\n]+") do + local llen = string.len(line); + local m = math.floor(llen/linesize)+1; + for i = 1, m do + page[#page+1]=minetest.formspec_escape(string.sub(line,(i-1)*linesize+1, i*linesize)) + end + + end + + local content = table.concat(page,",") + return "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]"; + end + + page = {} + self.show_form(pname,render_page()) + init = true +end + +sender,fields = self.read_form() +if sender then + --self.label(serialize(fields)) + local fsel = fields.wiki; + if fsel then + if string.sub(fsel,1,3) == "DCL" then + local sel = tonumber(string.sub(fsel,5)) or 1; + if string.sub(page[sel],1,2) == "\\[" then + current = string.sub(page[sel],3,-3) + self.show_form(pname,render_page()) + end + end + end + +end \ No newline at end of file diff --git a/scripts/programming/code_parser_strings_identify.lua b/scripts/programming/code_parser_strings_identify.lua new file mode 100644 index 0000000..a58b313 --- /dev/null +++ b/scripts/programming/code_parser_strings_identify.lua @@ -0,0 +1,53 @@ +function identify_strings(code) -- returns list of positions {start,end} of literal strings in lua code + + local i = 0; local j; local length = string.len(code); + local mode = 0; -- 0: not in string, 1: in '...' string, 2: in "..." string, 3. in [==[ ... ]==] string + local modes = { + {"'","'"}, + {"\"","\""}, + {"%[=*%[","%]=*%]"} + } + local ret = {} + while i < length do + i=i+1 + + local jmin = length+1; + if mode == 0 then -- not yet inside string + for k=1,#modes do + j = string.find(code,modes[k][1],i); + if j and j Date: Fri, 27 Jul 2018 12:22:57 +0200 Subject: [PATCH 22/39] help text changes --- robogui.lua | 113 ++++++++++++++++++++++++++++------------------------ 1 file changed, 61 insertions(+), 52 deletions(-) diff --git a/robogui.lua b/robogui.lua index e058526..2218ade 100644 --- a/robogui.lua +++ b/robogui.lua @@ -140,8 +140,8 @@ local help_pages = { " 2. [TAKE INSERT AND INVENTORY]", " 3. [BOOKS CODE TEXT WRITE OR READ]", " 4. [PLAYERS]", - " 5. [ROBOT]", - " 6. [KEYBOARD]", + " 5. [ROBOT SPEAK LABEL APPEARANCE OTHER]", + " 6. [KEYBOARD AND USER INTERACTIONS]", " 7. [TECHNIC FUNCTIONALITY]", " 8. [CRYPTOGRAPHY]", " 9. [PUZZLE]", @@ -163,51 +163,54 @@ local help_pages = { ["TAKE INSERT AND INVENTORY"] = { "back to [Commands reference]", "TAKE INSERT AND INVENTORY","", - " insert.direction(item, inventory) inserts item from robot inventory to target", - " inventory", + " insert.direction(item, inventory) inserts item from robot inventory to", + " target inventory", " check_inventory.direction(itemname, inventory,index) looks at node and ", " returns false/true, direction can be self, if index>0 it returns itemname.", " if itemname == \"\" it checks if inventory empty", " activate.direction(mode) activates target block", - " pickup(r) picks up all items around robot in radius r<8 and returns list or nil", - " craft(item,idx,mode) crafts item if required materials are present in inventory", - " mode = 1 returns recipe, optional recipe idx", - " take.direction(item, inventory) takes item from target inventory into robot", - " inventory", + " pickup(r) picks up all items around robot in radius r<8 and returns list", + " or nil", + " craft(item,idx,mode) crafts item if required materials are present in", + " inventory, mode = 1 returns recipe, optional recipe idx", + " take.direction(item, inventory) takes item from target inventory into", + " robot inventory", }, ["BOOKS CODE TEXT WRITE OR READ"] = { "back to [Commands reference]", "BOOKS CODE TEXT WRITE OR READ","", - " title,text=book.read(i) returns title,contents of book at i-th position in library", + " title,text=book.read(i) returns title,contents of book at i-th position in", + " library", " book.write(i,title,text) writes book at i-th position at spawner library", " code.run(text) compiles and runs the code in sandbox (privs only)", " code.set(text) replaces current bytecode of robot", - " find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around robot", - " or false if none", - " read_text.direction(stringname,mode) reads text of signs, chests and other", - " blocks, optional stringname for other meta", - " mode 1 read number", + " find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around", + " robot, or false if none found", + " read_text.direction(stringname,mode) reads text of signs, chests and", + " other blocks, optional stringname for other meta, mode 1 to read number", " write_text.direction(text,mode) writes text to target block as infotext", }, ["PLAYERS"] = { "back to [Commands reference]", "PLAYERS","", - " find_player(3,pos) finds players in radius 3 around robot(position) and returns", - " list of found player names, if none returns nil", + " find_player(3,pos) finds players in radius 3 around robot(position) and", + " returns list of found player names, 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 connected players names", + " 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 connected players names", }, - ["ROBOT"] = { + ["ROBOT SPEAK LABEL APPEARANCE OTHER"] = { "back to [Commands reference]", "ROBOT","", " 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", + " speaker, msg = self.listen_msg() retrieves last chat message if robot", + " has listener attached", " 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}", @@ -215,47 +218,53 @@ local help_pages = { " self.operations() returns remaining robot operations", " 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.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, texture, is_entity) fires a projectile from robot", + " self.fire(speed, pitch,gravity, texture, is_entity) fires a projectile", + " from robot. if is_entity false (default) it fires particle.", " 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 tex", - " self.sound(sample,volume, opt. pos) plays sound named 'sample' at robot", - " location (optional pos)", + " self.display_text(text,linesize,size) displays text instead of robot face,", + " if no size just return texture string", + " self.sound(sample,volume, opt. pos) plays sound named 'sample' at", + " robot, location (optional pos)", " rom is aditional table that can store persistent data, like rom.x=1", }, - ["KEYBOARD"] = { + ["KEYBOARD AND USER INTERACTIONS"] = { "back to [Commands reference]", "KEYBOARD","", - " EVENTS : place spawner at coordinates (20i,40j+1,20k) to monitor events", + " EVENTS : place spawner at coordinates (r*i,2*r*j+1,r*k) to monitor", + " events. value of r is ".. basic_robot.radius, " 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,7-15,16-271, limited to", - " range 10 around spawner", + " keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271,", + " limited to range 10 around spawner", " keyboard.read(pos) return node name at pos", }, ["TECHNIC FUNCTIONALITY"] = { "back to [Commands reference]", "TECHNIC FUNCTIONALITY","", - " namespace 'machine'. most functions return", - " true or nil, error", + " All commands are in namespace 'machine', for example machine.energy()" + " most functions return: ok, error = true or nil, error", + " To use some commands fully robot must be upgraded. 1 upgrade is", + " goldblock+meseblock+diamonblock.", " energy() displays available energy", - " 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", - " 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)", - " grind(input) - grinds input material, requires upgrades for harder material", + " 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 upgrades for each" + " 1 amount", + " 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 of smelt is: 1/40*(1+amount)", + " grind(input) - grinds input material, requires upgrades for harder" + " materials", " compress(input) - requires upgrades - energy intensive process", " transfer_power(amount,target_robot_name)", }, @@ -268,8 +277,8 @@ local help_pages = { " decrypt(input,password) attempts to decrypt encrypted text", " scramble(input,randomseed,sgn) (de)permutes text randomly according", " to sgn = -1,1", - " basic_hash(input,n) returns simple mod hash from string input within range", - " 0...n-1", + " basic_hash(input,n) returns simple mod hash from string input within", + " range 0...n-1", }, ["PUZZLE"] = { @@ -277,14 +286,14 @@ local help_pages = { "PUZZLE","", " namespace 'puzzle' - need puzzle priv", " set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers", - " check_triggers(pname) check if player is close to any trigger and run that", - " trigger", - " set_node(pos,node) - set any node, limited to current protector mapblock", - " & get_node(pos)", - " get_player(pname) return player objRef in current mapblock", + " check_triggers(pname) check if player is close to any trigger and run", + " that trigger", + " set_node(pos,node) - set any node, limited to current protector" + " region", + " get_player(pname) return player objRef in current protector region", " chat_send_player(pname, text)", - " get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes/", - " players in current mapblock", + " get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes", + " /players in current mapblock", " get_meta(pos) - return meta of target position", " get_gametime() - return current gametime", " ItemStack(itemname) returns ItemRef to be used with inventory", @@ -306,7 +315,7 @@ local robot_show_help = function(pname) --formname: robot_help local pages = help_pages[address]; local content = table.concat(pages,",") - local size = 8; local vsize = 7.75; + local size = 9; local vsize = 8.75; local form = "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]"; --minetest.chat_send_all("D " .. form) From 928ec9dace08f12b4cb45fec559759837e9cf8bd Mon Sep 17 00:00:00 2001 From: rnd Date: Fri, 27 Jul 2018 19:52:49 +0200 Subject: [PATCH 23/39] help edits --- robogui.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/robogui.lua b/robogui.lua index 2218ade..6d802c7 100644 --- a/robogui.lua +++ b/robogui.lua @@ -251,19 +251,19 @@ local help_pages = { ["TECHNIC FUNCTIONALITY"] = { "back to [Commands reference]", "TECHNIC FUNCTIONALITY","", - " All commands are in namespace 'machine', for example machine.energy()" + " All commands are in namespace 'machine', for example machine.energy()", " most functions return: ok, error = true or nil, error", " To use some commands fully robot must be upgraded. 1 upgrade is", " goldblock+meseblock+diamonblock.", " energy() displays available energy", - " 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 upgrades for each" + " 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 upgrades for each", " 1 amount", " 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 of smelt is: 1/40*(1+amount)", - " grind(input) - grinds input material, requires upgrades for harder" + " grind(input) - grinds input material, requires upgrades for harder", " materials", " compress(input) - requires upgrades - energy intensive process", " transfer_power(amount,target_robot_name)", @@ -288,7 +288,7 @@ local help_pages = { " set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers", " check_triggers(pname) check if player is close to any trigger and run", " that trigger", - " set_node(pos,node) - set any node, limited to current protector" + " set_node(pos,node) - set any node, limited to current protector", " region", " get_player(pname) return player objRef in current protector region", " chat_send_player(pname, text)", From 6b185ba155d5b9b0d5d1eeca1c651680665b6662 Mon Sep 17 00:00:00 2001 From: rnd Date: Sat, 28 Jul 2018 11:06:35 +0200 Subject: [PATCH 24/39] robot http demo enigma --- scripts/http/http_demo.lua | 44 ++++++++ scripts/math/enigma.lua | 207 +++++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 scripts/http/http_demo.lua create mode 100644 scripts/math/enigma.lua diff --git a/scripts/http/http_demo.lua b/scripts/http/http_demo.lua new file mode 100644 index 0000000..4cae0cb --- /dev/null +++ b/scripts/http/http_demo.lua @@ -0,0 +1,44 @@ +-- HOW TO DOWNLOAD WEBPAGES/FILES USING ROBOT + +if not fetch then +fetch = _G.basic_robot.http_api.fetch; + +-- WARNING: this is run outside pcall and can crash server if errors! +result = function(res) -- res.data is string containing result + if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end + if res.data then self.label(res.data) end +end + +fetch({url = "http://185.85.149.248/FILES/minetest/README.txt", timeout = 30}, result) +end + +--[[ +from https://github.com/minetest/minetest/blob/master/doc/lua_api.txt : + +`HTTPRequest` definition +------------------------ + +Used by `HTTPApiTable.fetch` and `HTTPApiTable.fetch_async`. + + { + url = "http://example.org", + timeout = 10, + -- ^ Timeout for connection in seconds. Default is 3 seconds. + post_data = "Raw POST request data string" OR {field1 = "data1", field2 = "data2"}, + -- ^ Optional, if specified a POST request with post_data is performed. + -- ^ Accepts both a string and a table. If a table is specified, encodes + -- ^ table as x-www-form-urlencoded key-value pairs. + -- ^ If post_data ist not specified, a GET request is performed instead. + user_agent = "ExampleUserAgent", + -- ^ Optional, if specified replaces the default minetest user agent with + -- ^ given string. + extra_headers = { "Accept-Language: en-us", "Accept-Charset: utf-8" }, + -- ^ Optional, if specified adds additional headers to the HTTP request. + -- ^ You must make sure that the header strings follow HTTP specification + -- ^ ("Key: Value"). + multipart = boolean + -- ^ Optional, if true performs a multipart HTTP request. + -- ^ Default is false. +} + +--]] \ No newline at end of file diff --git a/scripts/math/enigma.lua b/scripts/math/enigma.lua new file mode 100644 index 0000000..d6714be --- /dev/null +++ b/scripts/math/enigma.lua @@ -0,0 +1,207 @@ +--ENIGMA emulator by rnd +-- programming ~30 mins, total 3hrs 30 minutes with debugging - cause of youtube video with missing +-- key detail - reflector! + +-- REFERENCES: +-- 1. https://en.wikipedia.org/wiki/Enigma_machine +-- 2. http://users.telenet.be/d.rijmenants/en/enigmatech.htm#reflector +-- 3. https://www.youtube.com/watch?src_vid=V4V2bpZlqx8&v=G2_Q9FoD-oQ + +-- default settings +settings = {} +settings.set = function(reflector, plugboard, rotors) +settings.reflector = reflector or 2017 +settings.plugboard = plugboard or 2017 +settings.rotors = {} +if rotors then + table.insert(settings.rotors, rotors[1]) + table.insert(settings.rotors, rotors[2]) + table.insert(settings.rotors, rotors[3]) + table.insert(settings.rotors, rotors[4]) +else + table.insert(settings.rotors, 0) + table.insert(settings.rotors, 0) + table.insert(settings.rotors, 0) + table.insert(settings.rotors, 2018) + table.insert(settings.rotors, 2017) + table.insert(settings.rotors, 2020) +end +--if not enigma_encrypt then +if true then + scramble = function(input,password,sgn) -- permutes text randomly, nice after touch to stream cypher to prevent block analysis + _G.math.randomseed(password); + local n = #input; + local permute = {} + for i = 1, n do permute[i] = i end --input:sub(i, i) + for i = n,2,-1 do + local j = math.random(i-1); + local tmp = permute[j]; + permute[j] = permute[i]; permute[i] = tmp; + end + local out = {}; + if sgn>0 then -- unscramble + for i = 1,n do out[permute[i]] = string.sub(input,i,i) end + else -- scramble + for i = 1,n do out[i] = string.sub(input,permute[i],permute[i]) end + end + return table.concat(out,"") + end + + local permutation = function(n,password,sgn) -- create random permutation of numbers 1,...,n + _G.math.randomseed(password); + local permute = {} + for i = 1, n do permute[i] = i end + for i = n,2,-1 do + local j = math.random(i-1); + local tmp = permute[j]; + permute[j] = permute[i]; permute[i] = tmp; + end + return permute; + end + + -- produces permutation U such that U^2 = 1; modified fisher-yates shuffle by rnd + local reflector = function(n,password) + _G.math.randomseed(password) + local permute = {} + local used = {}; + for i = 1, n do permute[i] = i end + local rem = n; + + for i = n,2,-1 do + if not used[i] then + local j = math.random(rem); + -- now we need to find j-th unused idx + local k = 1; local l = 0; -- k position, l how many we tried + while l < j do + if not used[k] then l=l+1; end + k=k+1 + end + j=k-1; + + used[i]=true; used[j] = true; + local tmp = permute[j]; + permute[j] = permute[i]; permute[i] = tmp; + rem = rem - 2; + end + + end + return permute; + end + + + local inverse_table = function(tbl) + local ret = {}; + for i = 1,#tbl do + ret[ tbl[i] ] = i; + end + return ret; + end + + local rotors = {}; -- { permutation, work index }} + local invrotors = {}; -- reversed tables + + -- SETUP REFLECTOR, ROTORS AND PLUGBOARD! + local enigma_charcount = 127-32+1; -- n = 96 + local enigma_charstart = 32; + local reflector = reflector(enigma_charcount,settings.reflector); -- this is permutation U such that U^2 = id -- + + local plugboard = permutation(enigma_charcount,settings.plugboard,1); -- setup plugboard for enigma machine + local invplugboard = inverse_table(plugboard); + + for i = 1,3 do rotors[i] = {rotor = permutation(enigma_charcount,settings.rotors[3 + i],1), idx = 0} end -- set up 3 rotors together with their indices + for i = 1,3 do invrotors[i] = {rotor = inverse_table(rotors[i].rotor)} end -- set up 3 rotors together with their indices + + -- how many possible setups: + --[[ + n = charcount; + rotors positions: n^3 + plugboard wiring : n! + reflector wiring: n! / (2^n * (n/2)!) + TOTAL: (n!)^2*n^3 / ( 2^n * (n/2)! ) ~ 6.4 * 10^77 + rotor positions & plugboard wiring: (n!)*n^3 ~ 4.8 * 10^57 (n=43) + --]] + + -- END OF SETUP + + local rotate_rotor = function(i) + local carry = 1; + for j = i,1,-1 do + local idx = rotors[j].idx; + idx = idx + 1; + if idx>=enigma_charcount then + carry = 1; + else + carry = 0; + end + rotors[j].idx = idx % enigma_charcount; + if carry == 0 then break end + end + end + + local enigma_encrypt_char = function(x) -- x : 1 .. enigma_charcount + -- E = P.R1.R2.R3.U.R3^-1.R2^-1.R1^-1.P^-1, P = plugboard, R = rotor, U = reflector + x = plugboard[x]; + for i = 1,3 do + local idx = rotors[i].idx; + x = rotors[i].rotor[((x+idx-1) % enigma_charcount)+1]; + end + + x = reflector[x]; + + for i = 3,1,-1 do + local idx = rotors[i].idx; + x = invrotors[i].rotor[x]; + x = ((x-1-idx) % enigma_charcount)+1 + + end + + x = invplugboard[x]; + -- apply rotation to rotor - and subsequent rotors if necessary + rotate_rotor(3) + return x; + end + + + --enigma_encrypt = function(input) + settings.encrypt = function(input) + -- rotor settings! + rotors[1].idx = settings.rotors[1] + rotors[2].idx = settings.rotors[2] + rotors[3].idx = settings.rotors[3] + + local ret = ""; + for i = 1,#input do + local c = string.byte(input,i) - enigma_charstart +1; + --say(i .. " : " .. c) + if c>=1 and c<=enigma_charcount then + c = enigma_encrypt_char(c); + end + ret = ret .. string.char(enigma_charstart+c-1); + end + return ret + + end + settings.decrypt = settings.encrypt +end + +end + +msg = self.listen_msg() + if msg then + msg = minetest.strip_colors(msg) + local mark = string.find(msg,"@e") -- delimiter in chat + if mark then + msg = string.sub(msg,mark+2); + msg = minetest.colorize("yellow",enigma_encrypt(msg)) + say(minetest.colorize("red","#decrypted : ") .. msg) + end + end + +msg = self.sent_msg() +if msg then + local msg = enigma_encrypt(msg); +say("@e" .. msg,true) +-- minetest.show_formspec("encrypted_text", "size[4,4] textarea[0,-0.25;5,5;text;;".. "@e" .. minetest.formspec_escape(msg) .. "]") +end + + From 9e17cb46b9f89b83fd91f84c09bff065b8bc0783 Mon Sep 17 00:00:00 2001 From: rnd Date: Sun, 9 Dec 2018 23:42:54 +0100 Subject: [PATCH 25/39] mapmodel direction fixes (mapmodel moves in same direction as it faces) fire action fix new scripts --- commands.lua | 31 ++++--- init.lua | 9 +- scripts/games/sokoban_game.lua | 53 +++++------ scripts/games/switching_game.lua | 101 +++++++++++++++++++++ scripts/graphics/painting.lua | 112 ++++++++++++++++++++++++ scripts/gui/file_manager.lua | 57 ++++++++++++ scripts/gui/gui_2player_coop_edit.lua | 57 ++++++++++++ scripts/gui/gui_interact_demo_board.lua | 46 ++++++++++ scripts/gui/wiki.lua | 70 +++++++++------ scripts/http/http_demo.lua | 1 + scripts/utils/helper_chat_bot.lua | 71 +++++++++++++++ scripts/utils/object_lister.lua | 32 +++++++ scripts/utils/resource_display.lua | 2 +- textures/left-hand.png | Bin 159 -> 259 bytes textures/right-hand.png | Bin 175 -> 274 bytes textures/topface.png | Bin 203 -> 303 bytes 16 files changed, 569 insertions(+), 73 deletions(-) create mode 100644 scripts/games/switching_game.lua create mode 100644 scripts/graphics/painting.lua create mode 100644 scripts/gui/file_manager.lua create mode 100644 scripts/gui/gui_2player_coop_edit.lua create mode 100644 scripts/gui/gui_interact_demo_board.lua create mode 100644 scripts/utils/helper_chat_bot.lua create mode 100644 scripts/utils/object_lister.lua diff --git a/commands.lua b/commands.lua index 2aff995..ebeb570 100644 --- a/commands.lua +++ b/commands.lua @@ -21,34 +21,34 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc local yaw = obj:getyaw(); local pos = obj:getpos(); - if dir == 1 then -- left + if dir == 3 then -- left : 3 yaw = yaw + pi/2; - elseif dir == 2 then --right + elseif dir == 4 then --right: 4 yaw = yaw - pi/2; - elseif dir == 3 then -- forward - elseif dir == 4 then - yaw = yaw+pi; -- backward + elseif dir == 2 then -- forward: 1 + elseif dir == 1 then + yaw = yaw+pi; -- backward: 2 elseif dir == 5 then -- up pos.y=pos.y+1 elseif dir == 6 then -- down pos.y=pos.y-1 - elseif dir == 7 then -- left_down + elseif dir == 10 then -- left_down : 9 yaw = yaw + pi/2;pos.y=pos.y-1 - elseif dir == 8 then -- right_down + elseif dir == 9 then -- right_down: 10 yaw = yaw - pi/2;pos.y=pos.y-1 - elseif dir == 9 then -- forward_down + elseif dir == 7 then -- forward_down: 7 pos.y=pos.y-1 - elseif dir == 10 then -- backward_down + elseif dir == 8 then -- backward_down : 8 yaw = yaw + pi; pos.y=pos.y-1 - elseif dir == 11 then -- left_up + elseif dir == 14 then -- left_up: 13 yaw = yaw + pi/2;pos.y=pos.y+1 - elseif dir == 12 then -- right_up + elseif dir == 13 then -- right_up: 14 yaw = yaw - pi/2;pos.y=pos.y+1 - elseif dir == 13 then -- forward_up + elseif dir == 11 then -- forward_up : 11 pos.y=pos.y+1 - elseif dir == 14 then -- backward_up + elseif dir == 12 then -- backward_up: 12 yaw = yaw + pi; pos.y=pos.y+1 end @@ -540,7 +540,10 @@ basic_robot.commands.display_text = function(obj,text,linesize,size) if not size then return tex end if string.len(tex)<=1600 then - obj:set_properties({textures={"arrow.png","basic_machine_side.png",tex,"basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"},visual_size = {x=size,y=size}}) + obj:set_properties({ + textures = {"topface.png","legs.png",tex,"face-back.png","left-hand.png","right-hand.png"}, + --textures={"arrow.png","basic_machine_side.png",tex,"basic_machine_side.png","basic_machine_side.png","basic_machine_side.png"}, + visual_size = {x=size,y=size}}) else self.label("error: string too long") end diff --git a/init.lua b/init.lua index 8defba6..da00ca6 100644 --- a/init.lua +++ b/init.lua @@ -25,7 +25,7 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/07/27a"; +basic_robot.version = "2018/12/09a"; basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management basic_robot.data = {}; -- stores all robot related data @@ -171,7 +171,7 @@ function getSandboxEnv (name) fire = function(speed, pitch,gravity, texture, is_entity) -- experimental: fires an projectile local obj = basic_robot.data[name].obj; local pos = obj:getpos(); - local yaw = obj:getyaw(); + local yaw = obj:getyaw()+ math.pi/2; pitch = pitch*math.pi/180 local velocity = {x=speed*math.cos(yaw)*math.cos(pitch), y=speed*math.sin(pitch),z=speed*math.sin(yaw)*math.cos(pitch)}; -- fire particle @@ -193,6 +193,7 @@ function getSandboxEnv (name) local obj = minetest.add_entity(pos, "basic_robot:projectile"); if not obj then return end obj:setvelocity(velocity); + obj:set_properties({textures = {texture or "default_furnace_fire_fg.png"}}) obj:setacceleration({x=0,y=-gravity,z=0}); local luaent = obj:get_luaentity(); luaent.name = name; @@ -910,7 +911,7 @@ minetest.register_entity("basic_robot:robot",{ --textures={"character.png"}, visual="cube", - textures={"topface.png","legs.png","face.png","face-back.png","left-hand.png","right-hand.png"}, + textures={"topface.png","legs.png","left-hand.png","right-hand.png","face.png","face-back.png"}, visual_size={x=1,y=1}, running = 0, -- does it run code or is it idle? @@ -1844,7 +1845,7 @@ minetest.register_entity( on_step = function(self, dtime) local vel = self.object:getvelocity(); - if (self.oldvel.x~=0 and vel.x==0) or (self.oldvel.y~=0 and vel.y==0) or (self.oldvel.z~=0 and vel.z==0) then + if (self.oldvel.x~=0 and vel.x==0) or (self.oldvel.y~=0 and vel.y==0) or (self.oldvel.z~=0 and vel.z==0) then -- hit local data = basic_robot.data[self.name]; if data then data.fire_pos = self.object:getpos(); diff --git a/scripts/games/sokoban_game.lua b/scripts/games/sokoban_game.lua index dfa0099..503d3c0 100644 --- a/scripts/games/sokoban_game.lua +++ b/scripts/games/sokoban_game.lua @@ -1,17 +1,20 @@ -- SOKOBAN GAME, by rnd, robots port + + if not sokoban then sokoban = {}; - local players = find_player(5); + local players = find_player(8); if not players then error("sokoban: no player near") end name = players[1]; - - self.show_form(name, - "size[2,1.25]".. - "label[0,0;SELECT LEVEL 1-90]".. - "field[0.25,1;1,1;LVL;LEVEL;1]".. - "button_exit[1.25,0.75;1,1;OK;OK]" - ) + + -- self.show_form(name, + -- "size[2,1.25]".. + -- "label[0,0;SELECT LEVEL 1-90]".. + -- "field[0.25,1;1,1;LVL;LEVEL;1]".. + -- "button_exit[1.25,0.75;1,1;OK;OK]" + -- ) state = 1 -- will wait for form receive otherwise game play + self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks") player_ = puzzle.get_player(name); -- get player entity - player must be present in area player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}) -- reset player @@ -24,8 +27,8 @@ sokoban.load=0;sokoban.playername =""; sokoban.pos = {}; SOKOBAN_WALL = "moreblocks:cactus_brick" - SOKOBAN_FLOOR = "default:desert_sandstone" - SOKOBAN_GOAL = "moreblocks:wood_tile_center" + SOKOBAN_FLOOR = "default:silver_sandstone" + SOKOBAN_GOAL = "default:aspen_tree" SOKOBAN_BOX = "basic_robot:buttonFFFFFF" load_level = function(lvl) @@ -56,8 +59,8 @@ imax=i; file:close(); player_:set_physics_override({jump=0}) - player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0}) - say("games: sokoban level "..sokoban.level .." loaded by ".. name .. ". It has " .. sokoban.blocks .. " boxes to push. "); return + player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0}); + return end i=i+1; if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions @@ -72,7 +75,7 @@ if s=="." then p.y=p.y-1;puzzle.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;puzzle.set_node(p,{name="air"}) end --starting position if s=="@" then - player_:setpos({x=p.x,y=p.y+1,z=p.z}); -- move player to start position + player_:setpos({x=p.x,y=p.y-0.5,z=p.z}); -- move player to start position --p.y=p.y-1;puzzle.set_node(p,{name="default:glass"}); puzzle.set_node(p,{name="air"}) p.y=p.y+1;puzzle.set_node(p,{name="air"}) @@ -94,29 +97,21 @@ for i = 1, 20 do for j = 1,20 do local node = minetest.get_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}).name - if node ~= "default:desert_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:desert_sandstone"}) end + if node ~= "default:silver_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:silver_sandstone"}) end node = minetest.get_node({x=pos.x+i,y=pos.y,z=pos.z+j}).name if node ~= "air" then minetest.set_node({x=pos.x+i,y=pos.y,z=pos.z+j}, {name = "air"}) end end end end - end if state == 1 then - sender,fields = self.read_form(); -- get fields from form submittal - if sender then - --say(serialize(fields)) - - if fields.LVL then - clear_game() - load_level((tonumber(fields.LVL) or 1)-1) - state = 0 - self.label("stand close to blue box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all boxes pushed on diamond blocks") - end - end + clear_game() + load_level(20) + state = 0 + self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks") else local ppos = player_:getpos() @@ -174,6 +169,12 @@ else player_:set_physics_override({jump=1}) player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0}) + local player = _G.minetest.get_player_by_name(event.puncher); + if player then + local inv = player:get_inventory(); + inv:add_item("main",_G.ItemStack("skyblock:sokoban 4 ")) + end + local i,j; for i = 1,imax do for j=1,jmax do diff --git a/scripts/games/switching_game.lua b/scripts/games/switching_game.lua new file mode 100644 index 0000000..64c95d3 --- /dev/null +++ b/scripts/games/switching_game.lua @@ -0,0 +1,101 @@ +--[[ +SWITCHING GAME by rnd, 2018 + +lights: +0110 + +switches, each one toggles certain lights like: s1 1001 (toggles light with 1) + +PROBLEM: +hit switches in correct order to turn on all lights + +GENERATE RANDOM CHALLENGE: +start with all lights on and apply random sequence of switches + +TODO: instead of simply 0/1 switches have ones that advance +1 mod p (p can be say 3 or more) + + +REMARKS: application of 2 different switches is commutative ( obvious, since just x->x+1 mod p) +--]] +if not init then + +init = true +numlights = 2; +numswitches = 2; +states = 10; + +lights = {}; -- states of lights, initialy 1,1,...,1 +for i = 1, numlights do lights[i] = 0 end +switches = {} + +--switches = {{1,0,0,1},{1,1,1,1}}; +make_random_switches = function(lights, switches,count) + for i = 1, count do + switches[i] = {}; + local switch = switches[i]; + for j = 1, #lights do switch[j] = math.random(states)-1 end + end +end +make_random_switches(lights,switches, numswitches) + + +pos = self.spawnpos(); pos.x = pos.x + 1;-- pos.z = pos.z + 1 + +apply_switch = function(switches,lights,idx) + local switch = switches[idx]; + for i = 1, #switch do + local state = lights[i] + switch[i]; + if state >= states then state = state - states end + lights[i] = state + end +end + +randomize = function(switches, lights, steps) -- randomize lights + for i = 1, steps do + local idx = math.random(#switches); + apply_switch(switches,lights,idx); + end +end + +render_lights = function() for i = 1, #lights do keyboard.set({x=pos.x+i-1,y=pos.y+1, z=pos.z}, 7+lights[i]) end end +render_switches = function(mode) + if mode then + for i = 1, #switches do keyboard.set({x=pos.x+i-1,y=pos.y, z=pos.z}, 1+i) end + else + for i = 1, #switches do keyboard.set({x=pos.x+i-1,y=pos.y, z=pos.z}, 0) end + end +end + +check_lights = function() + for i = 1, #lights do if lights[i] ~= 0 then return false end end + return true +end +step = 0 + +randomize(switches,lights, math.min((#switches)^states,10000)) +if check_lights() then randomize(switches,lights, #switches + states) end + +render_lights(); render_switches(true) + + +self.label("GOAL OF GAME: punch buttons with numbers in correct order to turn all blocks to 0") + +--self.label(serialize(switches)) +end + + +event = keyboard.get() +if event then + local idx = event.x-pos.x+1; + if event.y==pos.y and idx>=1 and idx <= #switches then + apply_switch(switches, lights, idx) + render_lights() + step = step + 1 + if check_lights() then + self.label("DONE IN " .. step .. " STEPS !") + render_switches(false) + else + self.label("STEP " .. step) + end + end +end \ No newline at end of file diff --git a/scripts/graphics/painting.lua b/scripts/graphics/painting.lua new file mode 100644 index 0000000..0b7075e --- /dev/null +++ b/scripts/graphics/painting.lua @@ -0,0 +1,112 @@ +-- paint canvas by rnd, 2018 +if not init then + colors = { + "black","blue","brown","cyan","dark_green","dark_grey","green","grey", + "magenta","orange","pink","red","violet","white","yellow" + } + invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end + + color = 1; + size = 16; + + init = true + + local ent = _G.basic_robot.data[self.name()].obj:get_luaentity(); + ent.timestep = 0.5 + + players = find_player(5); if not players then self.remove() end + player = _G.minetest.get_player_by_name(players[1]) + self.label("-> " .. players[1]) + + spos = self.spawnpos(); spos.y=spos.y+1; + + canvasn = "wool:white" + reset_canvas = function() + for i = 1, size do + for j = 1, size do + minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn}) + end + end + end + reset_canvas() + + save_image = function() + local ret = {}; + for i = 1, size do + for j = 1, size do + local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,6) + local pcolor = invcolors[nname] or 1; + ret[#ret+1]= string.char(96+pcolor) + end + end + return table.concat(ret,"") + end + + load_image = function(image) + if not image then return end + local ret = {}; local k = 0; + for i = 1, size do + for j = 1, size do + k=k+1; + local pcolor = colors[string.byte(image,k)-96] or "black"; + minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "wool:"..pcolor}) + end + end + end + + + --draw buttons + for i = 1,#colors do + minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]}) + end + + minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"}) + minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"}) + + + vn = {x=0,y=0,z=1}; + T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z}; + + get_intersect = function(vn, T0, p, v) + local a = (T0.x-p.x)*vn.x + (T0.y-p.y)*vn.y + (T0.z-p.z)*vn.z; + local b = vn.x*v.x + vn.y*v.y + vn.z*v.z + if b<=0 then return nil end + if a<=0 then return nil end + local t = a / b + return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t} + end + +end + +if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui' + local v = player:get_look_dir(); + local p = player:get_pos(); p.y = p.y + 1.5 + local c = get_intersect(vn,T0,p,v); + if c then + + local x = c.x - T0.x; local y = c.y - T0.y + if x>0 and x-2 and y0 then -- above: painting + c.z = c.z+0.5 + minetest.set_node(c, {name = "wool:" .. colors[color]}) + elseif y>-1 then -- color selection + x = 1+math.floor(x) + if colors[x] then + color = x; + self.label(colors[x]) + end + else -- save,load button row + x = 1+math.floor(x) + if x==1 then + self.label("SAVED.") + book.write(1,"ROBOT_IMAGE",save_image()) + elseif x==2 then + local _,image = book.read(1) + load_image(image); + self.label("LOADED.") + end + end + end + + end +end \ No newline at end of file diff --git a/scripts/gui/file_manager.lua b/scripts/gui/file_manager.lua new file mode 100644 index 0000000..10205ba --- /dev/null +++ b/scripts/gui/file_manager.lua @@ -0,0 +1,57 @@ +-- file 'manager' by rnd + +if not init then + fmver = "2018/12/09" + local players = find_player(4); + if not players then self.remove() end + pname = players[1]; + size = 8; + vsize = 6.5; + + path = "/"; + pathlist = {} + folderlist = {}; + filelist = {}; + + render_page = function() + local foldlist = minetest.get_dir_list(path,true) -- only folders + if foldlist then folderlist = foldlist else folderlist = {} end + for i = 1,#foldlist do foldlist[i] = "*"..foldlist[i] end + foldlist[#foldlist+1] = "*.." + local fillist = minetest.get_dir_list(path,false) + if fillist then filelist = fillist else filelist = {} end + local content = table.concat(folderlist,",") .. ",-------------------," .. table.concat(filelist,",") + return "size[" .. size .. "," .. size .. "] label[0,-0.25;ROBOT FILE MANAGER " .. fmver .. " by rnd\nPATH " .. minetest.formspec_escape(path) .. "] textlist[-0.25,0.75;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]"; + end + + page = {} + self.show_form(pname,render_page()) + init = true + self.read_form() +end + +sender,fields = self.read_form() +if sender then + local fsel = fields.wiki; + if fsel and string.sub(fsel,1,3) == "DCL" then + local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line + local fold = folderlist[sel]; + if fold and string.sub(fold,1,1) == "*" then + if fold == "*.." then -- go back + if #pathlist>0 then + local i = string.len(pathlist[#pathlist]); + if i>0 then + pathlist[#pathlist] = nil + path = string.sub(path,1,-i-2); + end + end + else + pathlist[#pathlist+1] = string.sub(fold,2) + path = path .. "/".. pathlist[#pathlist] + end + + self.show_form(pname,render_page()) + end + end + --self.label(fsel); +end \ No newline at end of file diff --git a/scripts/gui/gui_2player_coop_edit.lua b/scripts/gui/gui_2player_coop_edit.lua new file mode 100644 index 0000000..1b6759d --- /dev/null +++ b/scripts/gui/gui_2player_coop_edit.lua @@ -0,0 +1,57 @@ +-- gui demo by rnd +-- 2 player cooperative editing of image with 2 colors + +if not init then + _G.basic_robot.data[self.name()].obj:get_luaentity().timestep = 0.25 + init = true + name = "rnd" + otherrobotname = "rnd2" -- on other robot do name of this robot + drawcolor = 3; -- other robot has reversed colors + otherdrawcolor = 4 + + color = {"black","white","blue","green"} + data = {}; + n = 20; + + for i = 1,n do + data[i]={}; + --local y = math.floor(f(i)); + for j = 1,n do + data[i][j] = 1--(n-j>y) and 2 or 1 + end + end + + get_form = function() + local form = "size[10,10] "; ret = {}; + + for i = 1,n do + for j = 1,n do + ret[#ret+1] = "image_button["..((i-1)*0.5)..","..((j-1)*0.5)..";0.7,0.63;wool_"..color[data[i][j]]..".png;"..((i-1)*n+j-1) .. ";] " + end + end + return form .. table.concat(ret,"") + end + + self.show_form(name,get_form()) + self.read_form() +end + +sender,mail = self.read_mail() +if mail then + local x = mail[1]; local y = mail[2]; + if data[x][y]==1 then data[x][y] = otherdrawcolor else data[x][y] = 1 end + self.show_form(name,get_form()) +end + +sender,fields = self.read_form() +if fields then + if fields.quit then self.remove() end + local sel = 0; + for k,v in pairs(fields) do + if k ~="quit" then sel = tonumber(k); break end + end + local x = 1+math.floor(sel/n); local y = 1+sel % n; + if data[x][y]==1 then data[x][y] = drawcolor else data[x][y] = 1 end + self.send_mail(otherrobotname,{x,y}) + self.show_form(name,get_form()) +end \ No newline at end of file diff --git a/scripts/gui/gui_interact_demo_board.lua b/scripts/gui/gui_interact_demo_board.lua new file mode 100644 index 0000000..6f1d62b --- /dev/null +++ b/scripts/gui/gui_interact_demo_board.lua @@ -0,0 +1,46 @@ +-- gui demo by rnd + +if not init then + init = true + name = "rnd" + color = {"white","black"} + data = {}; + n = 20; + + f = function(x) return 7*(1+math.sin(x/2)) end + + for i = 1,n do + data[i]={}; + local y = math.floor(f(i)); + for j = 1,n do + data[i][j] = (n-j>y) and 2 or 1 + end + end + + get_form = function() + local form = "size[10,10] "; ret = {}; + + for i = 1,n do + for j = 1,n do + ret[#ret+1] = "image_button["..((i-1)*0.5)..","..((j-1)*0.5)..";0.7,0.63;wool_"..color[data[i][j]]..".png;"..((i-1)*n+j-1) .. ";] " + end + end + return form .. table.concat(ret,"") + end + + self.show_form(name,get_form()) + self.read_form() +end + +sender,fields = self.read_form() +if fields then + if fields.quit then self.remove() end + local sel = 0; + for k,v in pairs(fields) do + if k ~="quit" then sel = tonumber(k); break end + end + local x = 1+math.floor(sel/n); local y = 1+sel % n; + data[x][y] = 3 - data[x][y] + --self.label(x .. " " .. y) + self.show_form(name,get_form()) +end \ No newline at end of file diff --git a/scripts/gui/wiki.lua b/scripts/gui/wiki.lua index 4675f2b..187b0f3 100644 --- a/scripts/gui/wiki.lua +++ b/scripts/gui/wiki.lua @@ -1,4 +1,7 @@ --- ROBOT WIKI +-- ROBOT WIKI by rnd +-- to do: ability for multiple links in 1 line + + if not init then _G.basic_robot.data[self.name()].obj:get_luaentity().timestep = 0.1 local players = find_player(4); @@ -8,27 +11,37 @@ if not init then vsize = 8; linesize = 60; -- break up longer lines - wiki = { - ["Main menu"] = "HELP CONTENTS\n \n".."double click link marked with [] or press enter while selected.\n \n".."[How to play]\n".."[Robot tutorial]", - ["How to play"] = "HOW TO PLAY\n \nOpen inventory (press i on pc), then go to Quests and read.".. - "Complete quests to progress in game and get nice rewards.\n \n[Main menu]", - ["Robot tutorial"] = "ROBOT TUTORIAL\n \nLearn on simple programs first then make a lot of your own\n \n[Main menu]", + wiki = { -- example of wiki pages + ["MAIN PAGE"] = + { + "-- WIKI CONTENTS -- ", "", + "double click link marked with [] or press enter while selected.","", + "[Viewing wiki]", + "[Editing wiki]" + }, + + ["Viewing wiki"] = { + "back to [MAIN PAGE]","", + " ** Viewing wiki", + "double click link marked with [] or press enter while selected." + }, + + ["Editing wiki"] = { + "back to [MAIN PAGE]","", + " ** Editing wiki", + "Edit wiki table and write in entries" + } } - current = "Main menu"; + + for k,v in pairs(wiki) do + local pages = wiki[k]; for i = 1,#pages do pages[i] = minetest.formspec_escape(pages[i]) end + end + + + current = "MAIN PAGE"; render_page = function() - page = {} - local text = wiki[current]; - for line in text:gmatch("[^\n]+") do - local llen = string.len(line); - local m = math.floor(llen/linesize)+1; - for i = 1, m do - page[#page+1]=minetest.formspec_escape(string.sub(line,(i-1)*linesize+1, i*linesize)) - end - - end - - local content = table.concat(page,",") + local content = table.concat(wiki[current],",") return "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]"; end @@ -39,16 +52,17 @@ end sender,fields = self.read_form() if sender then - --self.label(serialize(fields)) local fsel = fields.wiki; - if fsel then - if string.sub(fsel,1,3) == "DCL" then - local sel = tonumber(string.sub(fsel,5)) or 1; - if string.sub(page[sel],1,2) == "\\[" then - current = string.sub(page[sel],3,-3) - self.show_form(pname,render_page()) - end + if fsel and string.sub(fsel,1,3) == "DCL" then + local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line + local address = current or "main"; + local pages = wiki[address]; + + local link = _G.string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]") + if wiki[link] then + current = link; + self.show_form(pname,render_page()) + --robot_show_help(name) end end - end \ No newline at end of file diff --git a/scripts/http/http_demo.lua b/scripts/http/http_demo.lua index 4cae0cb..af1abb3 100644 --- a/scripts/http/http_demo.lua +++ b/scripts/http/http_demo.lua @@ -8,6 +8,7 @@ result = function(res) -- res.data is string containing result if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end if res.data then self.label(res.data) end end +fetch({url = "elysee.fr/?how_to_make_bomb", timeout = 30}, result) fetch({url = "http://185.85.149.248/FILES/minetest/README.txt", timeout = 30}, result) end diff --git a/scripts/utils/helper_chat_bot.lua b/scripts/utils/helper_chat_bot.lua new file mode 100644 index 0000000..85d4225 --- /dev/null +++ b/scripts/utils/helper_chat_bot.lua @@ -0,0 +1,71 @@ +if not init then + init = true; self.listen(1); + self.spam(1); self.label("help bot") + keywords = { + {"help", + {"robot",6},{"",1} + }, + {"how", + {"play",1},{"robot", 6},{"stone",4},{"tree",3},{"wood",3},{"lava",5},{"cobble",4},{"dirt",10}, + {"do i get",1},{"do i make",1}, {"to get",1} + }, + {"i need", + {"wood",3} + }, + + {"hello",2}, -- words matched must appear at beginning + {"hi",2}, + {"back",7}, + {" hard",{"",9}}, -- word matched can appear anywhere + {" died", {"",9}}, + {" die",{"",8}}, {" dead",{"",8}}, + {"rnd",{"",11}}, + {"bye",{"",12}}, + {"!!",{"",9}}, + } + answers = { + "%s open inventory, click 'Quests' and do them to get more stuff", --1 + "hello %s", + "do the dirt quest to get sticks then do sapling quest", + "get pumice from lava and water. then search craft guide how to make cobble", + "you get lava as compost quest reward or with grinder", -- 5 + "you have to write a program so that robot knows what to do. for list of commands click 'help' button inside robot.", + "wb %s", + "dont die, you lose your stuff and it will reset your level on level 1", + "you suck %s!", -- 9 + "to get dirt craft composter and use it with leaves", -- 10 + "rnd is afk. in the meantime i can answer your questions", + "bye %s", + } +end + +speaker,msg = self.listen_msg(); +if msg then + msg = string.lower(msg); + sel = 0; + for i = 1, #keywords do + local k = string.find(msg,keywords[i][1]) + if k then + if type(keywords[i][2])~="table" then + if k == 1 then sel = keywords[i][2] break end + else + for j=2,#keywords[i] do + if string.find(msg,keywords[i][j][1]) then + sel = keywords[i][j][2]; break; + end + end + end + + end + end + + if sel>0 then + local response = answers[sel]; + if string.find(response,"%%s") then + say(string.format(response,speaker)) + else + say(response) + end + end + +end \ No newline at end of file diff --git a/scripts/utils/object_lister.lua b/scripts/utils/object_lister.lua new file mode 100644 index 0000000..7fe8ade --- /dev/null +++ b/scripts/utils/object_lister.lua @@ -0,0 +1,32 @@ +-- return minetest object count for 5x5x5 blocks + +if not init then init = true + +local objs = minetest.get_objects_inside_radius(self.pos(), 30000); +local ret = {}; + +local round = function(x) return math.floor(x/5)*5 end +local ret = {}; + +for i = 1, #objs do + local p = objs[i]:get_pos(); + local phash = round(p.x) .. " " .. round(p.y) .. " " .. round(p.z); + ret[phash] = (ret[phash] or 0) + 1 +end + +local out = {}; +for k,v in pairs(ret) do + out[#out+1] = {k,v} +end + +table.sort(out, function(a,b) return a[2]>b[2] end) +local res = {}; +for i = 1, #out do + res[#res+1] = out[i][1] .. "=" .. out[i][2] +end + +self.label("#objects " .. #objs .. "\n" .. table.concat(res, "\n")) + + + +end \ No newline at end of file diff --git a/scripts/utils/resource_display.lua b/scripts/utils/resource_display.lua index 22145b9..b1fe504 100644 --- a/scripts/utils/resource_display.lua +++ b/scripts/utils/resource_display.lua @@ -6,7 +6,7 @@ if not init then init = true if k~="listening" and v.obj then local ent = v.obj:get_luaentity(); local t = v.t or 0; if t< 100000 then t = math.floor(t * 10000)/10 else t = 0 end - if ent then ret[#ret+1] = k .. " " .. string.len(ent.code) .. " " .. string.len(_G.string.dump(v.bytecode)) .. " ~ " .. t end + if ent then ret[#ret+1] = k .. " " .. string.len(ent.code or "") .. " " .. string.len(_G.string.dump(v.bytecode) or "") .. " ~ " .. t end end end mem1 = _G.collectgarbage("count") diff --git a/textures/left-hand.png b/textures/left-hand.png index 13d59b9d009947d83a150d8d4fd1b38b31db83a1..00c068faa3006c631fa79c210f157aa50456b24f 100644 GIT binary patch delta 243 zcmbQw*vvFRqMn0^fq`Md#0)ba#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5v%n*= zn1O-sFbFdq&tH)O6qG1&jVKAuPb(=;EJ|f4NX*PD(aTFMQ83Xn)H6)hn&S;rQ{w63 z7-Hc+cY-6=0RWqEz?`&w0w8eH46)Q zY33)ohbUZ)TDzse_#bag(>49Th574Q7ntsMT#+$By{RBNqe=R~hDD`~Hg`5G<^0>A n>%-4^fL&XIS=~W#X$f=9{-g57ZT(Y$4q)(f^>bP0l+XkK{(Dk{ delta 143 zcmZo>n$I{vqMn7Bfq~)e-A6${N+`f5#P#IKliJ$a8X6jUdU|Kio;`m2`1xILWq`6C zo-U3d9M_Wt%*@10L_|W83=$Za)p*kVbtiqdavaAD|$-v?1>gTe~DWM4f7-=ro diff --git a/textures/right-hand.png b/textures/right-hand.png index f88947895d83691740326e01385e377466ccf307..2f6c70cae308c3971fc89bb6b11286c10a6bd432 100644 GIT binary patch delta 258 zcmZ3_IEiV3L_G%+0|Uc^i5X@4QxP%~u#1uQrLQY5433uu%G7b=M{CW&Kfsf4ef50^P&l>FVdQ&MBb@ E0Db*l!TmLX z5FZBL00010NklHL1X{` diff --git a/textures/topface.png b/textures/topface.png index 2d1b759136ca7436ab4f0aaf24839021370ca71a..b5da74a9c1f4228da9d21b54fefc20f1e7a81e68 100644 GIT binary patch delta 288 zcmX@jxSnZ(L_G%+0|Uc^i5X@HyC(^;&mNJ=*xe$woXbd+wp? z=cVo(+dfLqJ)tT+pM~q}IgQD!(M~V+UJl+6WUXy>k)!c@yF!Mrna3dsXXE%_?_=|h z%>NZX!(1WY;&PX)lPQcg50{v+SmsRh(C}KYHLYoz-i1iTfXok~=h9S#oz{Lm6c{pN j!iCHITO}1@y?!$2vaeD~6{_|Cx|YGy)z4*}Q$iB}xZ-K< delta 187 zcmV;s07U<<0?PrA7=Hu<0002(-QrRJ000|MOjJe5$;m1zD$LBxEiEl4CnwL(&&S8d z($dn##>Q?UwIBch0BcD^K~xCWP0Yaw!axv(;eR4oulvLb7Q6@2fRPj+3n?Hdc3=fI z?i;-LA7(xn7-pT9)lr)(uP0Mu>_ge95h3md*|0N#BqCA04m6&l^oL0J^n)ayu4Kk3 pA8rXazB> Date: Tue, 11 Dec 2018 21:45:40 +0100 Subject: [PATCH 26/39] directions fix --- commands.lua | 36 ++++++++++++++++++------------------ init.lua | 13 +++++++++++-- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/commands.lua b/commands.lua index ebeb570..a1b2c09 100644 --- a/commands.lua +++ b/commands.lua @@ -21,40 +21,40 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc local yaw = obj:getyaw(); local pos = obj:getpos(); - if dir == 3 then -- left : 3 - yaw = yaw + pi/2; - elseif dir == 4 then --right: 4 + if dir == 1 then -- left yaw = yaw - pi/2; - elseif dir == 2 then -- forward: 1 - elseif dir == 1 then - yaw = yaw+pi; -- backward: 2 + elseif dir == 2 then --right + yaw = yaw + pi/2; + elseif dir == 3 then -- forward + elseif dir == 4 then + yaw = yaw+pi; -- backward elseif dir == 5 then -- up pos.y=pos.y+1 elseif dir == 6 then -- down pos.y=pos.y-1 - elseif dir == 10 then -- left_down : 9 - yaw = yaw + pi/2;pos.y=pos.y-1 - elseif dir == 9 then -- right_down: 10 + elseif dir == 7 then -- left_down yaw = yaw - pi/2;pos.y=pos.y-1 - elseif dir == 7 then -- forward_down: 7 + elseif dir == 8 then -- right_down + yaw = yaw + pi/2;pos.y=pos.y-1 + elseif dir == 9 then -- forward_down pos.y=pos.y-1 - elseif dir == 8 then -- backward_down : 8 + elseif dir == 10 then -- backward_down yaw = yaw + pi; pos.y=pos.y-1 - elseif dir == 14 then -- left_up: 13 - yaw = yaw + pi/2;pos.y=pos.y+1 - elseif dir == 13 then -- right_up: 14 + elseif dir == 11 then -- left_up yaw = yaw - pi/2;pos.y=pos.y+1 - elseif dir == 11 then -- forward_up : 11 + elseif dir == 12 then -- right_up + yaw = yaw + pi/2;pos.y=pos.y+1 + elseif dir == 13 then -- forward_up pos.y=pos.y+1 - elseif dir == 12 then -- backward_up: 12 + elseif dir == 14 then -- backward_up yaw = yaw + pi; pos.y=pos.y+1 end if dir ~= 5 and dir ~= 6 then - pos.x = pos.x+math.cos(yaw) - pos.z = pos.z+math.sin(yaw) + pos.x = pos.x+math.sin(yaw) + pos.z = pos.z+math.cos(yaw) end return pos diff --git a/init.lua b/init.lua index da00ca6..f4361b9 100644 --- a/init.lua +++ b/init.lua @@ -25,7 +25,7 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/12/09a"; +basic_robot.version = "2018/12/11a"; basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management basic_robot.data = {}; -- stores all robot related data @@ -364,12 +364,21 @@ function getSandboxEnv (name) string = { byte = string.byte, char = string.char, find = string.find, - format = string.format, gsub = string.gsub, + gsub = string.gsub, gmatch = string.gmatch, len = string.len, lower = string.lower, upper = string.upper, rep = string.rep, reverse = string.reverse, sub = string.sub, + format = function(...) + local out = string.format(...) + if string.len(out) > 1024 then + error("result string longer than 1024") + return + end + return out + end, + concat = function(strings, sep) local length = 0; for i = 1,#strings do From 8712bb0e60f0e1f3605c2fb2fac3d9adb294445b Mon Sep 17 00:00:00 2001 From: rnd Date: Tue, 11 Dec 2018 22:49:55 +0100 Subject: [PATCH 27/39] directions fix 2 --- commands.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/commands.lua b/commands.lua index a1b2c09..ab86c28 100644 --- a/commands.lua +++ b/commands.lua @@ -26,8 +26,8 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc elseif dir == 2 then --right yaw = yaw + pi/2; elseif dir == 3 then -- forward - elseif dir == 4 then - yaw = yaw+pi; -- backward + yaw = yaw+pi; + elseif dir == 4 then -- backward elseif dir == 5 then -- up pos.y=pos.y+1 elseif dir == 6 then -- down @@ -37,19 +37,19 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc yaw = yaw - pi/2;pos.y=pos.y-1 elseif dir == 8 then -- right_down yaw = yaw + pi/2;pos.y=pos.y-1 - elseif dir == 9 then -- forward_down + elseif dir == 10 then -- forward_down + yaw = yaw + pi;pos.y=pos.y-1 + elseif dir == 9 then -- backward_down pos.y=pos.y-1 - elseif dir == 10 then -- backward_down - yaw = yaw + pi; pos.y=pos.y-1 elseif dir == 11 then -- left_up yaw = yaw - pi/2;pos.y=pos.y+1 elseif dir == 12 then -- right_up yaw = yaw + pi/2;pos.y=pos.y+1 - elseif dir == 13 then -- forward_up + elseif dir == 14 then -- forward_up + pos.y=pos.y+1;yaw = yaw + pi; + elseif dir == 13 then -- backward_up pos.y=pos.y+1 - elseif dir == 14 then -- backward_up - yaw = yaw + pi; pos.y=pos.y+1 end if dir ~= 5 and dir ~= 6 then From 532a017be74abecf0c5190cf67b5f613192a0a01 Mon Sep 17 00:00:00 2001 From: rnd Date: Wed, 12 Dec 2018 12:06:46 +0100 Subject: [PATCH 28/39] bugfixes: preprocess_script, directions --- commands.lua | 28 ++++++++++++++-------------- init.lua | 12 +++++++----- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/commands.lua b/commands.lua index ab86c28..26821f3 100644 --- a/commands.lua +++ b/commands.lua @@ -22,39 +22,39 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc local pos = obj:getpos(); if dir == 1 then -- left - yaw = yaw - pi/2; - elseif dir == 2 then --right yaw = yaw + pi/2; + elseif dir == 2 then --right + yaw = yaw - pi/2; elseif dir == 3 then -- forward - yaw = yaw+pi; elseif dir == 4 then -- backward + yaw = yaw+pi; elseif dir == 5 then -- up pos.y=pos.y+1 elseif dir == 6 then -- down pos.y=pos.y-1 elseif dir == 7 then -- left_down - yaw = yaw - pi/2;pos.y=pos.y-1 - elseif dir == 8 then -- right_down yaw = yaw + pi/2;pos.y=pos.y-1 + elseif dir == 8 then -- right_down + yaw = yaw - pi/2;pos.y=pos.y-1 elseif dir == 10 then -- forward_down - yaw = yaw + pi;pos.y=pos.y-1 - elseif dir == 9 then -- backward_down pos.y=pos.y-1 + elseif dir == 9 then -- backward_down + yaw = yaw + pi;pos.y=pos.y-1 elseif dir == 11 then -- left_up - yaw = yaw - pi/2;pos.y=pos.y+1 - elseif dir == 12 then -- right_up yaw = yaw + pi/2;pos.y=pos.y+1 + elseif dir == 12 then -- right_up + yaw = yaw - pi/2;pos.y=pos.y+1 elseif dir == 14 then -- forward_up - pos.y=pos.y+1;yaw = yaw + pi; - elseif dir == 13 then -- backward_up pos.y=pos.y+1 + elseif dir == 13 then -- backward_up + yaw = yaw + pi;pos.y=pos.y+1 end if dir ~= 5 and dir ~= 6 then - pos.x = pos.x+math.sin(yaw) - pos.z = pos.z+math.cos(yaw) + pos.x = pos.x - math.sin(yaw) -- math.cos(yaw+pi/2) + pos.z = pos.z + math.cos(yaw) -- math.sin(yaw+pi/2) end return pos @@ -92,7 +92,7 @@ basic_robot.commands.move = function(name,dir) obj:moveto(pos, true) - -- sit and stand up for model - doenst work for overwriten obj export + -- sit and stand up for model - doesnt work for overwriten obj export -- if dir == 5 then-- up -- obj:set_animation({x=0,y=0}) -- elseif dir == 6 then -- down diff --git a/init.lua b/init.lua index f4361b9..f2a79f0 100644 --- a/init.lua +++ b/init.lua @@ -656,6 +656,8 @@ end -- COMPILATION +--todo: 2018/12 this suddenly stopped working, wtf?? + preprocess_code = function(script) -- version 07/24/2018 --[[ idea: in each local a = function (args) ... end insert counter like: @@ -674,17 +676,18 @@ preprocess_code = function(script) -- version 07/24/2018 local found = true; local strings = identify_strings(script); + local inserts = {}; local constructs = { {"while%s", "%sdo%s", 2, 6}, -- numbers: insertion pos = i2+2, after skip to i1 = i12+6 - {"function", ")", 0, 0}, - {"for%s", "%sdo%s", 2, 0}, + {"function", ")", 0, 8}, + {"for%s", "%sdo%s", 2, 4}, {"goto%s", nil , -1, 5}, } for i = 1,#constructs do - i1 = 0 + i1 = 0; found = true while (found) do -- PROCESS SCRIPT AND INSERT COUNTER AT PROBLEMATIC SPOTS found = false; @@ -709,7 +712,6 @@ preprocess_code = function(script) -- version 07/24/2018 end end - end table.sort(inserts) @@ -724,7 +726,6 @@ preprocess_code = function(script) -- version 07/24/2018 ret[#ret+1] = string.sub(script,i1); script = table.concat(ret,_increase_ccounter) - return script:gsub("pause%(%)", "_c_ = 0; pause()") -- reset ccounter at pause end @@ -754,6 +755,7 @@ local function setCode( name, script ) -- to run script: 1. initSandbox 2. setCo end if err then return err end + local bytecode, err = CompileCode ( script ); if err then return err end basic_robot.data[name].bytecode = bytecode; From 29f4410348f041a195ac91eb3af4b39d642fd481 Mon Sep 17 00:00:00 2001 From: rnd Date: Wed, 12 Dec 2018 18:38:42 +0100 Subject: [PATCH 29/39] .. --- commands.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands.lua b/commands.lua index 26821f3..b2901c1 100644 --- a/commands.lua +++ b/commands.lua @@ -46,9 +46,9 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc yaw = yaw + pi/2;pos.y=pos.y+1 elseif dir == 12 then -- right_up yaw = yaw - pi/2;pos.y=pos.y+1 - elseif dir == 14 then -- forward_up + elseif dir == 13 then -- forward_up pos.y=pos.y+1 - elseif dir == 13 then -- backward_up + elseif dir == 14 then -- backward_up yaw = yaw + pi;pos.y=pos.y+1 end From 9463f54babdf5ff1b3964f4ee74d4b5329c8c874 Mon Sep 17 00:00:00 2001 From: rnd Date: Wed, 12 Dec 2018 19:23:05 +0100 Subject: [PATCH 30/39] .. --- commands.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/commands.lua b/commands.lua index b2901c1..e485bb4 100644 --- a/commands.lua +++ b/commands.lua @@ -32,16 +32,14 @@ local function pos_in_dir(obj, dir) -- position after we move in specified direc pos.y=pos.y+1 elseif dir == 6 then -- down pos.y=pos.y-1 - elseif dir == 7 then -- left_down yaw = yaw + pi/2;pos.y=pos.y-1 elseif dir == 8 then -- right_down yaw = yaw - pi/2;pos.y=pos.y-1 - elseif dir == 10 then -- forward_down + elseif dir == 9 then -- forward_down pos.y=pos.y-1 - elseif dir == 9 then -- backward_down + elseif dir == 10 then -- backward_down yaw = yaw + pi;pos.y=pos.y-1 - elseif dir == 11 then -- left_up yaw = yaw + pi/2;pos.y=pos.y+1 elseif dir == 12 then -- right_up From fd9f65939296c497d87044db8a2fc6df07e93e80 Mon Sep 17 00:00:00 2001 From: rnd Date: Wed, 12 Dec 2018 19:59:16 +0100 Subject: [PATCH 31/39] .. --- init.lua | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/init.lua b/init.lua index f2a79f0..8863958 100644 --- a/init.lua +++ b/init.lua @@ -994,12 +994,9 @@ minetest.register_entity("basic_robot:robot",{ local name = self.name; local pos = basic_robot.data[name].spawnpos; minetest.set_node(pos, {name = "air"}); - - local privs = core.get_player_privs(self.owner);privs.interact = false; - - core.set_player_privs(self.owner, privs); minetest.auth_reload() - minetest.ban_player(self.owner) - + --local privs = core.get_player_privs(self.owner);privs.interact = false; + --core.set_player_privs(self.owner, privs); minetest.auth_reload() + minetest.kick_player(self.owner, "#basic_robot: stack overflow") end local name = self.name; From 5f8659dbb2d0bac91d039208b88df860afda821b Mon Sep 17 00:00:00 2001 From: rnd Date: Sun, 23 Dec 2018 19:49:10 +0100 Subject: [PATCH 32/39] webcommands for robots using nodejs server --- .../http/webcommands/minetest_webcommands.js | 31 +++++++++++++++++++ scripts/http/webcommands/webcommands.lua | 31 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 scripts/http/webcommands/minetest_webcommands.js create mode 100644 scripts/http/webcommands/webcommands.lua diff --git a/scripts/http/webcommands/minetest_webcommands.js b/scripts/http/webcommands/minetest_webcommands.js new file mode 100644 index 0000000..a574490 --- /dev/null +++ b/scripts/http/webcommands/minetest_webcommands.js @@ -0,0 +1,31 @@ +// listen to web request and pass it to minetest, rnd 2018 + +const http = require('http'); + +const hostname = '192.168.0.10' //write address of your router (it will be accessible from internet then if you open firewall for nodejs process) +const port = 80; + +var webreq = "" + +// take request from web and pass it to minetest +const server = http.createServer((req, res) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + + if (req.url == '/favicon.ico') return // prevent passing this as request + + var pos = (req.url).indexOf("/MT"); + if (pos >=0) { // did request come from minetest? then answer with latest request + res.write(webreq);webreq = "";res.end();return + } + + //process web request and store it + var ip = req.connection.remoteAddress; + webreq = ip + ' ' + req.url + res.write('request received: ' + webreq);res.end(); // acknowledge request + return +}); + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); \ No newline at end of file diff --git a/scripts/http/webcommands/webcommands.lua b/scripts/http/webcommands/webcommands.lua new file mode 100644 index 0000000..c068bb8 --- /dev/null +++ b/scripts/http/webcommands/webcommands.lua @@ -0,0 +1,31 @@ +-- webcommands : access url like: 192.168.0.10/hello_world + +--[[ instructions: + 1.download nodejs server from + https://nodejs.org/dist/v10.14.2/node-v10.14.2-win-x86.zip + 2. run nodejs server using run.bat + :loop + node --inspect myprogs/minetest_webcommands.js + goto loop + 3. run robot and type 'http://192.168.0.10:80/hello this is a test' into browser + (you need to write your router address here, i.e. ip accessible from internet OR lan address) +--]] + +if not fetch then + fetch = _G.basic_robot.http_api.fetch; + state = 0 -- ready to fetch new command +-- WARNING: this is run outside pcall and can crash server if errors! + result = function(res) -- res.data is string containing result + state = 0 + if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end + if res.data == "" then return end + local req = res.data; req = string.gsub(req,"%%20","S") + if res.data then self.label(os.clock() .. ' received cmd : ' .. res.data) end + end + +end + +if state == 0 then + fetch({url = "http://192.168.0.10/MT", timeout = 30}, result) + state = 1 +end \ No newline at end of file From 76824674c1747d6c6a4a5f1d9499eaf96861610e Mon Sep 17 00:00:00 2001 From: rnd Date: Sun, 23 Dec 2018 20:06:01 +0100 Subject: [PATCH 33/39] webcommands: fix %20 to space --- scripts/http/webcommands/webcommands.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/http/webcommands/webcommands.lua b/scripts/http/webcommands/webcommands.lua index c068bb8..9676f2c 100644 --- a/scripts/http/webcommands/webcommands.lua +++ b/scripts/http/webcommands/webcommands.lua @@ -19,8 +19,8 @@ if not fetch then state = 0 if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end if res.data == "" then return end - local req = res.data; req = string.gsub(req,"%%20","S") - if res.data then self.label(os.clock() .. ' received cmd : ' .. res.data) end + local req = res.data; req = string.gsub(req,"%%20"," ") + if res.data then self.label(os.clock() .. ' received cmd : ' .. req) end end end From 5cf411912088e547a65fe315e0babfb3eb685c7c Mon Sep 17 00:00:00 2001 From: rnd Date: Mon, 24 Dec 2018 13:23:17 +0100 Subject: [PATCH 34/39] http demo --- scripts/http/http_demo.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/http/http_demo.lua b/scripts/http/http_demo.lua index af1abb3..4cae0cb 100644 --- a/scripts/http/http_demo.lua +++ b/scripts/http/http_demo.lua @@ -8,7 +8,6 @@ result = function(res) -- res.data is string containing result if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end if res.data then self.label(res.data) end end -fetch({url = "elysee.fr/?how_to_make_bomb", timeout = 30}, result) fetch({url = "http://185.85.149.248/FILES/minetest/README.txt", timeout = 30}, result) end From b85ac575badc9fb1d8d174930e7604914401742d Mon Sep 17 00:00:00 2001 From: rnd Date: Mon, 24 Dec 2018 18:57:12 +0100 Subject: [PATCH 35/39] different execution count limit for different levels of authorization --- init.lua | 22 ++++++++++++---------- scripts/utils/helper_chat_bot.lua | 30 ++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/init.lua b/init.lua index 8863958..8418458 100644 --- a/init.lua +++ b/init.lua @@ -3,7 +3,7 @@ basic_robot = {}; ------ SETTINGS -------- -basic_robot.call_limit = 48; -- how many execution calls per script run allowed +basic_robot.call_limit = {50,200,1500,10^9}; -- how many execution calls per script run allowed, for auth levels 0,1,2 (normal, robot, puzzle, admin) basic_robot.entry_count = 2 -- how many robots ordinary player can have basic_robot.advanced_count = 16 -- how many robots player with robot privs can have basic_robot.radius = 32; -- divide whole world into blocks of this size - used for managing events like keyboard punches @@ -25,7 +25,7 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/12/11a"; +basic_robot.version = "2018/12/24a"; basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management basic_robot.data = {}; -- stores all robot related data @@ -490,7 +490,7 @@ function getSandboxEnv (name) env.code.run = function(script) if basic_robot.data[name].authlevel < 3 then local err = check_code(script); - script = preprocess_code(script); + script = preprocess_code(script, basic_robot.call_limit[basic_robot.data[name].authlevel+1]); if err then minetest.chat_send_player(name,"#ROBOT CODE CHECK ERROR : " .. err) return @@ -658,7 +658,7 @@ end --todo: 2018/12 this suddenly stopped working, wtf?? -preprocess_code = function(script) -- version 07/24/2018 +preprocess_code = function(script, call_limit) -- version 07/24/2018 --[[ idea: in each local a = function (args) ... end insert counter like: local a = function (args) counter_check_code ... end @@ -669,8 +669,8 @@ preprocess_code = function(script) -- version 07/24/2018 script="_c_ = 0; " .. script; -- process script to insert call counter in every function - local _increase_ccounter = " _c_ = _c_ + 1; if _c_ > " .. basic_robot.call_limit .. - " then error(\"Execution count \".. _c_ .. \" exceeded ".. basic_robot.call_limit .. "\") end; " + local _increase_ccounter = " _c_ = _c_ + 1; if _c_ > " .. call_limit .. + " then _G.error(\"Execution count \".. _c_ .. \" exceeded ".. call_limit .. "\") end; " local i1=0; local i2 = 0; local found = true; @@ -747,11 +747,13 @@ local function setCode( name, script ) -- to run script: 1. initSandbox 2. setCo local cor = false; if string.sub(script,1,11) == "--coroutine" then cor = true end - if basic_robot.data[name].authlevel<3 then -- not admin + local authlevel = basic_robot.data[name].authlevel; + + if authlevel<3 then -- not admin err = check_code(script); - script = preprocess_code(script); + script = preprocess_code(script,basic_robot.call_limit[authlevel+1]); elseif cor then - script = preprocess_code(script); -- coroutines need ccounter reset or 'infinite loops' fail after limit + script = preprocess_code(script, basic_robot.call_limit[authlevel+1]); -- coroutines need ccounter reset or 'infinite loops' fail after limit end if err then return err end @@ -1104,7 +1106,7 @@ local spawn_robot = function(pos,node,ttl) if data.authlevel<3 then -- not admin err = check_code(script); - script = preprocess_code(script); + script = preprocess_code(script, basic_robot.call_limit[data.authlevel+1]); end if err then meta:set_string("infotext","#CODE CHECK ERROR : " .. err); diff --git a/scripts/utils/helper_chat_bot.lua b/scripts/utils/helper_chat_bot.lua index 85d4225..e2a0704 100644 --- a/scripts/utils/helper_chat_bot.lua +++ b/scripts/utils/helper_chat_bot.lua @@ -1,7 +1,10 @@ if not init then init = true; self.listen(1); self.spam(1); self.label("help bot") + + talk = function(msg) minetest.chat_send_all(" " .. msg) end keywords = { + {"tp", 14}, {"help", {"robot",6},{"",1} }, @@ -22,6 +25,8 @@ if not init then {"rnd",{"",11}}, {"bye",{"",12}}, {"!!",{"",9}}, + + {"calc", 13}, } answers = { "%s open inventory, click 'Quests' and do them to get more stuff", --1 @@ -36,20 +41,31 @@ if not init then "to get dirt craft composter and use it with leaves", -- 10 "rnd is afk. in the meantime i can answer your questions", "bye %s", + function(speaker,msg) -- 13, calc + local expr = string.sub(msg,5); if string.find(expr,"%a") then return end + local res = _G.loadstring("return " .. expr)(); say(expr .. " = " .. res) + end, + function(speaker,msg) -- 14,tp + local p1 = minetest.get_player_by_name(speaker); + local p2 = minetest.get_player_by_name(string.sub(msg,4)); + if p1 and p2 then + p1:setpos(p2:getpos()) + end + end, } end speaker,msg = self.listen_msg(); if msg then - msg = string.lower(msg); + --msg = string.lower(msg); sel = 0; for i = 1, #keywords do local k = string.find(msg,keywords[i][1]) if k then - if type(keywords[i][2])~="table" then + if type(keywords[i][2])~="table" then -- one topic only if k == 1 then sel = keywords[i][2] break end else - for j=2,#keywords[i] do + for j=2,#keywords[i] do -- category of several topics if string.find(msg,keywords[i][j][1]) then sel = keywords[i][j][2]; break; end @@ -61,10 +77,12 @@ if msg then if sel>0 then local response = answers[sel]; - if string.find(response,"%%s") then - say(string.format(response,speaker)) + if type(response) == "function" then + response(speaker,msg) + elseif string.find(response,"%%s") then + talk(string.format(response,speaker)) else - say(response) + talk(response) end end From 0a7c5d33d81b640d4cc24aa1efd971bc40532cd5 Mon Sep 17 00:00:00 2001 From: rnd Date: Mon, 24 Dec 2018 21:10:26 +0100 Subject: [PATCH 36/39] webcommands: bidirectional --- .../http/webcommands/minetest_webcommands.js | 61 +++++++++++++++---- scripts/http/webcommands/webcommands.lua | 38 ++++++++++-- 2 files changed, 82 insertions(+), 17 deletions(-) diff --git a/scripts/http/webcommands/minetest_webcommands.js b/scripts/http/webcommands/minetest_webcommands.js index a574490..600a8e7 100644 --- a/scripts/http/webcommands/minetest_webcommands.js +++ b/scripts/http/webcommands/minetest_webcommands.js @@ -1,31 +1,68 @@ -// listen to web request and pass it to minetest, rnd 2018 +// listen to web request and pass it to minetest or back to web, rnd 2018 + +// INSTRUCTIONS. url options: +// 1./mtmsg/msg will store msg as message received from minetest ( minetest_message). note that msg cant contain spaces or newlines +// 2./getwebmsg/ will reply = IP + ' ' + webmessage +// 3./webmsg/msg will store message as webmessage +// 4./getmtmsg will reply with minetest_message + + +// NOTES: 1. avoids the need to deal with POST nastyness and complications like +// https://stackoverflow.com/questions/4295782/how-do-you-extract-post-data-in-node-js const http = require('http'); + const hostname = '192.168.0.10' //write address of your router (it will be accessible from internet then if you open firewall for nodejs process) const port = 80; -var webreq = "" +var webreq = "" // message from web +var mtreq = "" // message from mt // take request from web and pass it to minetest const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); - if (req.url == '/favicon.ico') return // prevent passing this as request - - var pos = (req.url).indexOf("/MT"); - if (pos >=0) { // did request come from minetest? then answer with latest request - res.write(webreq);webreq = "";res.end();return - } - - //process web request and store it + var msg = req.url; + if (msg == '/favicon.ico') return // prevent passing this as request + + + var pos = msg.indexOf("/",1); // gets the 2nd / in /part1/part2/... + var cmd = msg.substring(1,pos); + var response = "" var ip = req.connection.remoteAddress; - webreq = ip + ' ' + req.url - res.write('request received: ' + webreq);res.end(); // acknowledge request + + switch(cmd) + { + case "mtmsg": + response = msg.substring(pos+1); + mtreq = response + break + case "getmtmsg": + response = mtreq; mtreq = '' + break + case "getwebmsg": + response = webreq; webreq = '' + break + case "webmsg": + webreq = ip + ' ' + msg.substring(pos+1); + response = 'request received: ' + webreq + '\nuse /getmtmsg to view response from minetest' + break + default: + response = 'INSTRUCTIONS. url options:\n'+ + '1./mtmsg/msg will store msg as message received from minetest ( minetest_message). note that msg cant contain spaces or newlines\n'+ + '2./getwebmsg/ will reply = IP + " " + webmessage\n'+ + '3./webmsg/msg will store message as webmessage\n'+ + '4./getmtmsg will reply with minetest_message\n' + } + + if (msg!='' && cmd != 'getwebmsg') console.log('ip ' + ip + ', msg ' + msg) + res.write(response); res.end() return }); +// make server listen server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`); }); \ No newline at end of file diff --git a/scripts/http/webcommands/webcommands.lua b/scripts/http/webcommands/webcommands.lua index 9676f2c..fe067ad 100644 --- a/scripts/http/webcommands/webcommands.lua +++ b/scripts/http/webcommands/webcommands.lua @@ -5,13 +5,14 @@ https://nodejs.org/dist/v10.14.2/node-v10.14.2-win-x86.zip 2. run nodejs server using run.bat :loop - node --inspect myprogs/minetest_webcommands.js + node $path/minetest_webcommands.js goto loop - 3. run robot and type 'http://192.168.0.10:80/hello this is a test' into browser + 3. run robot and type 'http://192.168.0.10/webmsg/hello this is a test' into browser (you need to write your router address here, i.e. ip accessible from internet OR lan address) --]] if not fetch then + address = "192.168.0.10"; fetch = _G.basic_robot.http_api.fetch; state = 0 -- ready to fetch new command -- WARNING: this is run outside pcall and can crash server if errors! @@ -20,12 +21,39 @@ if not fetch then if not res.succeeded then self.label("#ERROR: data couldn't be downloaded :\n" .. minetest.serialize(res) ) return end if res.data == "" then return end local req = res.data; req = string.gsub(req,"%%20"," ") - if res.data then self.label(os.clock() .. ' received cmd : ' .. req) end + if res.data then + self.label(os.date("%X") ..', cmd : ' .. req) + local i = string.find(req," !") + if i then + run_commmand(string.sub(req,i+2)) + end end - + end + + admin = minetest.setting_get("name") + run_commmand = function(message) + local cmd, param = _G.string.match(message, "([^ ]+) *(.*)") + if not param then + param = "" + end + local cmd_def = minetest.chatcommands[cmd] + if cmd_def then + cmd_def.func(admin, param) + else + minetest.chat_send_all(admin..": "..message) + end + end + + MT2web = function(message) + message = string.gsub(message," ","%%20") -- NOTE: if you send request that has 'space' in it there will be error 400! + fetch({url = "http://".. address .. "/mtmsg/"..message, timeout = 5}, result) + end + MT2web("minetest robot started and listening.") end + + if state == 0 then - fetch({url = "http://192.168.0.10/MT", timeout = 30}, result) + fetch({url = "http://"..address.."/getwebmsg/", timeout = 5}, result) state = 1 end \ No newline at end of file From 539144f05b6dd92c5c1bc869a7ef8ae9f1b4b600 Mon Sep 17 00:00:00 2001 From: rnd Date: Sun, 13 Jan 2019 21:25:13 +0100 Subject: [PATCH 37/39] drop fix craft: several crafts at once --- commands.lua | 13 ++++++++----- init.lua | 8 ++++---- scripts/http/webcommands/webcommands.lua | 14 +++++++++++++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/commands.lua b/commands.lua index e485bb4..36c777a 100644 --- a/commands.lua +++ b/commands.lua @@ -488,11 +488,11 @@ basic_robot.give_drops = function(nodename, inv) -- gives apropriate drops when local i = 0; for k,v in pairs(drop.items) do if i > max_items then break end; i=i+1; - local rare = v.rarity or 0; + local rare = v.rarity or 1; if rare>0 and math.random(1, rare)==1 then dropname = v.items[math.random(1,#v.items)]; -- pick item randomly from list inv:add_item("main",dropname); - + break end end else @@ -756,8 +756,11 @@ basic_robot.commands.keyboard = { basic_robot.commands.craftcache = {}; -basic_robot.commands.craft = function(item, mode, idx, name) +basic_robot.commands.craft = function(item, mode, idx,amount, name) + amount = amount and tonumber(amount) or 1; + if amount<0 then amount = 1 end if not item then return false end + local cache = basic_robot.commands.craftcache[name]; if not cache then basic_robot.commands.craftcache[name] = {}; cache = basic_robot.commands.craftcache[name] end @@ -820,12 +823,12 @@ basic_robot.commands.craft = function(item, mode, idx, name) local inv = minetest.get_meta(pos):get_inventory(); for item,quantity in pairs(itemlist) do - local stack = ItemStack(item .. " " .. quantity); + local stack = ItemStack(item .. " " .. quantity*amount); if not inv:contains_item("main",stack) then return false end end for item,quantity in pairs(itemlist) do - local stack = ItemStack(item .. " " .. quantity); + local stack = ItemStack(item .. " " .. quantity*amount); inv:remove_item("main",stack); end diff --git a/init.lua b/init.lua index 8418458..72f1e43 100644 --- a/init.lua +++ b/init.lua @@ -25,7 +25,7 @@ basic_robot.bad_inventory_blocks = { -- disallow taking from these nodes invento basic_robot.http_api = minetest.request_http_api(); -basic_robot.version = "2018/12/24a"; +basic_robot.version = "2019/01/13a"; basic_robot.gui = {}; local robogui = basic_robot.gui -- gui management basic_robot.data = {}; -- stores all robot related data @@ -86,8 +86,8 @@ function getSandboxEnv (name) return commands.pickup(r, name); end, - craft = function(item, idx,mode) - return commands.craft(item, mode, idx, name) + craft = function(item, idx,mode, amount) + return commands.craft(item, mode, idx, amount, name) end, pause = function() -- pause coroutine @@ -808,7 +808,7 @@ end local function setupid(owner) local privs = minetest.get_player_privs(owner); if not privs then return end local maxid = basic_robot.entry_count; - if privs.robot then maxid = basic_robot.advanced_count end -- max id's per user + if privs.robot or privs.puzzle then maxid = basic_robot.advanced_count end -- max id's per user basic_robot.ids[owner] = {id = 1, maxid = maxid}; --active id for remove control end diff --git a/scripts/http/webcommands/webcommands.lua b/scripts/http/webcommands/webcommands.lua index fe067ad..2916306 100644 --- a/scripts/http/webcommands/webcommands.lua +++ b/scripts/http/webcommands/webcommands.lua @@ -25,8 +25,18 @@ if not fetch then self.label(os.date("%X") ..', cmd : ' .. req) local i = string.find(req," !") if i then - run_commmand(string.sub(req,i+2)) + local cmd = string.sub(req,i+2) + if cmd == "players" then + local players = minetest.get_connected_players(); + out = {}; + for i = 1,#players do out[i] = players[i]:get_player_name() end + MT2web("online players : " .. table.concat(out,", ")) + else + run_commmand(cmd) + end end + + end end @@ -49,6 +59,8 @@ if not fetch then fetch({url = "http://".. address .. "/mtmsg/"..message, timeout = 5}, result) end MT2web("minetest robot started and listening.") + + self.listen(1) end From 5706500ad86c8bf1c6c08484640503a55c294918 Mon Sep 17 00:00:00 2001 From: rnd Date: Sun, 13 Jan 2019 21:37:58 +0100 Subject: [PATCH 38/39] renaming --- init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init.lua b/init.lua index 72f1e43..f5c5e73 100644 --- a/init.lua +++ b/init.lua @@ -576,7 +576,7 @@ end check_code = function(code) --"while ", "for ", "do ","goto ", - local bad_code = {"repeat", "until", "_ccounter", "_G", "while%(", "while{", "pcall","%.%."} --,"\\\"", "%[=*%[","--[["} + local bad_code = {"repeat", "until", "_c_", "_G", "while%(", "while{", "pcall","%.%."} --,"\\\"", "%[=*%[","--[["} for _, v in pairs(bad_code) do if string.find(code, v) then return v .. " is not allowed!"; From ac1817dc9e8c844e13b23e31a2c00fede7eb3804 Mon Sep 17 00:00:00 2001 From: rnd Date: Sun, 27 Jan 2019 18:40:29 +0100 Subject: [PATCH 39/39] book write: bugfix item drops: randomize number of returned items too long code used fix --- commands.lua | 8 +- init.lua | 3 +- scripts/copy_paste.lua | 20 ++ scripts/games/CTF_bot.lua | 257 +++++++++++----------- scripts/games/battle_minesweeper_game.lua | 53 ++++- 5 files changed, 206 insertions(+), 135 deletions(-) diff --git a/commands.lua b/commands.lua index 36c777a..be43b0d 100644 --- a/commands.lua +++ b/commands.lua @@ -457,10 +457,11 @@ basic_robot.commands.write_book = function(name,title,text) -- returns itemstack local data = {} if title == "" or not title then title = "program book "..minetest.get_gametime() end - data.title = title + data.title = title or "" data.text = text or "" data.text_len = #data.text data.page = 1 + data.description = title or "" data.page_max = math.ceil((#data.text:gsub("[^\n]", "") + 1) / lpp) data.owner = name --local data_str = minetest.serialize(data) -- pre 0.4.16 @@ -484,15 +485,16 @@ basic_robot.give_drops = function(nodename, inv) -- gives apropriate drops when if max_items==0 then -- just drop all the items (taking the rarity into consideration) max_items = #table.drop.items or 0; end + max_items = math.random(max_items) -- return random number of items local drop = table.drop; local i = 0; for k,v in pairs(drop.items) do - if i > max_items then break end; i=i+1; + if i > max_items then break end; + i=i+1; local rare = v.rarity or 1; if rare>0 and math.random(1, rare)==1 then dropname = v.items[math.random(1,#v.items)]; -- pick item randomly from list inv:add_item("main",dropname); - break end end else diff --git a/init.lua b/init.lua index f5c5e73..67ccb69 100644 --- a/init.lua +++ b/init.lua @@ -209,7 +209,7 @@ function getSandboxEnv (name) label = function(text) local obj = basic_robot.data[name].obj; - obj:set_properties({nametag = text}); -- "[" .. name .. "] " .. + obj:set_properties({nametag = text or ""}); -- "[" .. name .. "] " .. end, display_text = function(text,linesize,size) @@ -1578,6 +1578,7 @@ minetest.register_on_player_receive_fields( if not data then data = {} end local text = fields.book or ""; if string.len(text) > 64000 then + local sender = player:get_player_name(); minetest.chat_send_all("#ROBOT: " .. sender .. " is spamming with long text.") return end data.text = text or "" diff --git a/scripts/copy_paste.lua b/scripts/copy_paste.lua index 9dae0a6..471a509 100644 --- a/scripts/copy_paste.lua +++ b/scripts/copy_paste.lua @@ -11,6 +11,22 @@ if not paste then end end data = {}; + + display_marker = function(pos,label) + minetest.add_particle( + { + pos = pos, + expirationtime = 10, + velocity = {x=0, y=0,z=0}, + size = 18, + texture = label..".png", + acceleration = {x=0,y=0,z=0}, + collisiondetection = true, + collision_removal = true, + }) + + end + self.listen(1) self.label("COPY-PASTE MASTER v1.2 gold edition. commands: c1 c2 r c p") end @@ -21,12 +37,16 @@ speaker, msg = self.listen_msg() if speaker == "rnd" then local player = _G.minetest.get_player_by_name(speaker); local p = player:getpos(); p.x = round(p.x); p.y=round(p.y); p.z = round(p.z); + if p.y<0 then p.y = p.y +1 end -- needed cause of minetest bug if msg == "c1" then paste.src1 = {x=p.x,y=p.y,z=p.z};say("marker 1 set at " .. p.x .. " " .. p.y .. " " .. p.z) + display_marker(p,"099") -- c elseif msg == "c2" then paste.src2 = {x=p.x,y=p.y,z=p.z};say("marker 2 set at " .. p.x .. " " .. p.y .. " " .. p.z) + display_marker(p,"099") -- c elseif msg == "r" then paste.ref = {x=p.x,y=p.y,z=p.z};say("reference set at " .. p.x .. " " .. p.y .. " " .. p.z) + display_marker(p,"114") -- r elseif msg == "c" then -- copy local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z); local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z); diff --git a/scripts/games/CTF_bot.lua b/scripts/games/CTF_bot.lua index 5cff02d..742b1d5 100644 --- a/scripts/games/CTF_bot.lua +++ b/scripts/games/CTF_bot.lua @@ -1,137 +1,146 @@ --- simple ctf robot, rnd ---instructions: build game arena and place blue/red buttons as flags. edit flag positions below ---you must register 'keyboard' events by placing robot with same 'id' as robot running this code at 'event register' positions - default (32*i,64*j+1, 32*k) + -- simple ctf robot, rnd + --instructions: build game arena and place blue/red buttons as flags. edit flag positions below + --you must register 'keyboard' events by placing robot with same 'id' as robot running this code at 'event register' positions - default (32*i,64*j+1, 32*k) -if not ctf then - _G.minetest.forceload_block(self.pos(),true) - ctf = { - [1] = {state = 1, flagnode = "basic_robot:button8080FF", pos = {x=-18,y=501,z=17}, name = "blue", owner = "", score = 0}, -- team[1] - [2] = {state = 1, flagnode = "basic_robot:buttonFF8080", pos = {x=-79,y=501,z=46}, name = "red", owner = "", score = 0}, -- team[2] - } - - teams = {} -- example : {["rnd"] = {1,0, player, health points at start}}; -- team, ownership of flag - maxscore = 3; - t = 0 - teamid = 1; -- team selector when joining - - gamestate = 0; - self.listen(1) - self.spam(1) - - get_id = function(pos) - local range = 1000; - return pos.x + range*pos.y+range^2*pos.z - end - - flag_id = {}; for i = 1,#ctf do flag_id[get_id(ctf[i].pos)] = i end - - render_flags = function() - for i = 1,#ctf do minetest.set_node(ctf[i].pos, {name = ctf[i].flagnode}) end - end - -end - -if gamestate == 0 then -- welcome - say(colorize("red","#CAPTURE THE FLAG GAME. say '\\join' to join game. to start game one of joined players says '\\start'")) - gamestate = 1 -elseif gamestate == 1 then - speaker,msg = self.listen_msg() - if msg == "join" then - local pl = minetest.get_player_by_name(speaker); - teams[speaker] = {teamid, 0, pl,20};pl:set_hp(20) - local msg1 = ""; local msg2 = "" - for k,v in pairs(teams) do - if v[1] == 1 then msg1 = msg1 .. k .. " " elseif v[1] == 2 then msg2 = msg2 .. k .. " " end + if not ctf then + _G.minetest.forceload_block(self.pos(),true) + ctf = { + [1] = {state = 1, flagnode = "basic_robot:button8080FF", pos = {x=-18,y=502,z=110}, name = "blue", owner = "", score = 0}, -- team[1] + [2] = {state = 1, flagnode = "basic_robot:buttonFF8080", pos = {x=-18,y=502,z=80}, name = "red", owner = "", score = 0}, -- team[2] + } + + teams = {} -- example : {["rnd"] = {1,0, player, health points at start}}; -- team, ownership of flag + maxscore = 3; + t = 0 + teamid = 1; -- team selector when joining + + gamestate = 0; + self.listen(1) + self.spam(1) + + get_id = function(pos) + local range = 1000; + return pos.x + range*pos.y+range^2*pos.z end - say(colorize("yellow","#CTF : " .. speaker .. " joined team " .. ctf[teamid].name .. ". TEAM " .. ctf[1].name .. ": " .. msg1 .. ", TEAM " .. ctf[2].name .. ": " .. msg2)) - teamid = 3-teamid; -- 1,2 - elseif msg == "start" then -- game start - if teams[speaker] then - gamestate = 2 - keyboard.get() -- clear keyboard buffer - say(colorize("red","#CTF GAME STARTED. GET ENEMY FLAG AND BRING IT BACK TO YOUR FLAG. DONT LET YOUR HEALTH GO BELOW 5 HEARTS OR YOU ARE OUT.")) - for k,_ in pairs(teams) do -- teleport players - local data = teams[k];data[3]:setpos( ctf[data[1]].pos ) - end - render_flags() + flag_id = {}; for i = 1,#ctf do flag_id[get_id(ctf[i].pos)] = i end + + render_flags = function() + for i = 1,#ctf do minetest.set_node(ctf[i].pos, {name = ctf[i].flagnode}) end end + end -elseif gamestate == 2 then - -- check player health - for k,v in pairs(teams) do - local hp = teams[k][3]:get_hp(); - if not hp or hp<10 then -- teams[k][4] - - local cflag = teams[k][2]; - if cflag>0 then -- drop flag - ctf[cflag].state = 1 - ctf[cflag].owner = "" - minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode}) - say(colorize("red", "#CTF " .. k .. " dropped " .. ctf[cflag].name .. " flag!")) + if gamestate == 0 then -- welcome + say(colorize("red","#CAPTURE THE FLAG GAME. say '\\join' to join game. to start game one of joined players says '\\start'")) + gamestate = 1 + elseif gamestate == 1 then + speaker,msg = self.listen_msg() + if msg == "join" then + local pl = minetest.get_player_by_name(speaker); + teams[speaker] = {teamid, 0, pl,20};pl:set_hp(20) + local msg1 = ""; local msg2 = "" + for k,v in pairs(teams) do + if v[1] == 1 then msg1 = msg1 .. k .. " " elseif v[1] == 2 then msg2 = msg2 .. k .. " " end end - if not hp then -- player left - say(colorize("yellow", "#CTF " .. k .. " left the game!")) - teams[k] = nil - else -- reset player - say(colorize("yellow", "#CTF " .. k .. " resetted!")) - v[2] = 0 -- player has no flag - v[3]:set_hp(20) - v[3]:setpos( ctf[v[1]].pos ) - end - - + say(colorize("yellow","#CTF : " .. speaker .. " joined team " .. ctf[teamid].name .. ". TEAM " .. ctf[1].name .. ": " .. msg1 .. ", TEAM " .. ctf[2].name .. ": " .. msg2)) + teamid = 3-teamid; -- 1,2 + elseif msg == "start" then -- game start + if teams[speaker] then + gamestate = 2 + keyboard.get() -- clear keyboard buffer + say(colorize("red","#CTF GAME STARTED. GET ENEMY FLAG AND BRING IT BACK TO YOUR FLAG. DONT LET YOUR HEALTH GO BELOW 3 HEARTS OR YOU ARE OUT.")) + for k,_ in pairs(teams) do -- teleport players + local data = teams[k];data[3]:setpos( ctf[data[1]].pos ) + end + render_flags() + end end - end - - - event = keyboard.get() - if event and teams[event.puncher] then - --say(serialize(event)) - local punch_id = get_id({x=event.x,y=event.y,z=event.z}); - local flag = flag_id[punch_id]; - if flag then - local state = ctf[flag].state - local puncher = event.puncher; - if state == 1 then -- flag is here, ready to be taken or capture of enemy flag - if teams[puncher][1] ~= flag then -- take - say(colorize("red","#CTF " .. puncher .. " has taken " .. ctf[flag].name .. " flag !")) - ctf[flag].state = 2; - ctf[flag].owner = puncher; - teams[puncher][2] = flag; - minetest.set_node(ctf[flag].pos, {name = "basic_robot:buttonFFFF80"}) - else -- capture? - if teams[puncher][2] > 0 then - local cflag = teams[puncher][2] -- puncher has this flag - local data = ctf[cflag]; - - local team = teams[puncher][1]; - ctf[team].score = ctf[team].score + 1 - ctf[team].owner = "" - ctf[cflag].state = 1; -- reset captured flag state - minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode}) - teams[puncher][2] = 0 - say(colorize("orange","#CTF " .. puncher .. " has captured " .. data.name .. " flag! Team " .. ctf[team].name .. " has score " .. ctf[team].score )) - if ctf[team].score == maxscore then - say(colorize("yellow","#CTF: TEAM " .. ctf[team].name .. " WINS! ")) - gamestate = 3;t=5; -- intermission, duration 5 - - --reset - teams = {} - for i=1,#ctf do ctf[i].state = 1 ctf[i].score = 0 ctf[i].owner = "" end - end - - end - + + elseif gamestate == 2 then + speaker,msg = self.listen_msg() + if msg == "score" then + local msg1 = ""; local msg2 = "" + for k,v in pairs(teams) do + if v[1] == 1 then msg1 = msg1 .. k .. " " elseif v[1] == 2 then msg2 = msg2 .. k .. " " end + end + say(colorize("yellow","SCORE " .. ctf[1].score .. "/" .. ctf[2].score) .."\n" .. colorize("yellow","TEAM " .. ctf[1].name .. ": " .. msg1 .. ", TEAM " .. ctf[2].name .. ": " .. msg2)) + end + + -- check player health + for k,v in pairs(teams) do + local hp = teams[k][3]:get_hp(); + if not hp or hp<6 then -- teams[k][4] + + local cflag = teams[k][2]; + if cflag>0 then -- drop flag + ctf[cflag].state = 1 + ctf[cflag].owner = "" + minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode}) + say(colorize("red", "#CTF " .. k .. " dropped " .. ctf[cflag].name .. " flag!")) + end + if not hp then -- player left + say(colorize("yellow", "#CTF " .. k .. " left the game!")) + teams[k] = nil + else -- reset player + say(colorize("yellow", "#CTF " .. k .. " resetted!")) + v[2] = 0 -- player has no flag + v[3]:set_hp(20) + v[3]:setpos( ctf[v[1]].pos ) end - + + end end - --say(serialize(event)) - end -elseif gamestate == 3 then -- intermission - if t>0 then t=t-1 else gamestate = 0 end -end \ No newline at end of file + + + event = keyboard.get() + if event and teams[event.puncher] then + --say(serialize(event)) + local punch_id = get_id({x=event.x,y=event.y,z=event.z}); + local flag = flag_id[punch_id]; + if flag then + local state = ctf[flag].state + local puncher = event.puncher; + if state == 1 then -- flag is here, ready to be taken or capture of enemy flag + if teams[puncher][1] ~= flag then -- take + say(colorize("red","#CTF " .. puncher .. " has taken " .. ctf[flag].name .. " flag !")) + ctf[flag].state = 2; + ctf[flag].owner = puncher; + teams[puncher][2] = flag; + minetest.set_node(ctf[flag].pos, {name = "basic_robot:buttonFFFF80"}) + else -- capture? + if teams[puncher][2] > 0 then + local cflag = teams[puncher][2] -- puncher has this flag + local data = ctf[cflag]; + + local team = teams[puncher][1]; + ctf[team].score = ctf[team].score + 1 + ctf[team].owner = "" + ctf[cflag].state = 1; -- reset captured flag state + minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode}) + teams[puncher][2] = 0 + say(colorize("orange","#CTF " .. puncher .. " has captured " .. data.name .. " flag! Team " .. ctf[team].name .. " has score " .. ctf[team].score )) + if ctf[team].score == maxscore then + say(colorize("yellow","#CTF: TEAM " .. ctf[team].name .. " WINS! ")) + gamestate = 3;t=5; -- intermission, duration 5 + + --reset + teams = {} + for i=1,#ctf do ctf[i].state = 1 ctf[i].score = 0 ctf[i].owner = "" end + end + + end + + end + + + end + end + --say(serialize(event)) + end + elseif gamestate == 3 then -- intermission + if t>0 then t=t-1 else gamestate = 0 end + end \ No newline at end of file diff --git a/scripts/games/battle_minesweeper_game.lua b/scripts/games/battle_minesweeper_game.lua index b1ebc06..5eae232 100644 --- a/scripts/games/battle_minesweeper_game.lua +++ b/scripts/games/battle_minesweeper_game.lua @@ -4,6 +4,10 @@ if not data then paused = true turn = 2; + turntimeout = 5 + shorttimeout = 1 + shortround = false + turnmessage = "" t = 0; SIGNUP = 0; GAME = 1; INTERMISSION = 2 state = SIGNUP @@ -12,6 +16,8 @@ if not data then data = {}; init_game = function() + abilitypoints = {0,0}; -- points to use ability, step outside ring to use them in following round + shortround = false 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 @@ -44,8 +50,17 @@ if not data then return count end + near_chat = function(msg) + + 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")) + _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\n".. + "3. if you make move in other player turn you lose. 4. if you hit bomb or mark bomb falsely you lose\n".. + "5. by playing fast you get ability charge. when you collect 10 you can step out of ring when your round ends and opponent will only have 2s to play his turn." + )) end player_lost = function () @@ -53,22 +68,38 @@ if not data then 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 + _G.minetest.sound_play("electric_zap",{pos=spawnpos, max_hear_distance = 100}) state = INTERMISSION; t = 0 end function change_turn() + shortround = false -- reset ability if activated 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}) + _G.minetest.sound_play("default_cool_lava",{pos=spawnpos, max_hear_distance = 100}) end if paused == false then - say(players[turn] .. " lost : didn't make a move"); + say(players[turn] .. " lost : didn't make a move fast enough "); player_lost() else + local player = minetest.get_player_by_name(players[turn]) + local ppos = player:getpos() + local x = ppos.x - spawnpos.x;local y = ppos.y - spawnpos.y;local z = ppos.z - spawnpos.z; + local points = abilitypoints[turn] + if x<1 or x>m or z<1 or z>n then -- outside area + local points = abilitypoints[turn] + if points>=10 then -- do we have enough points? + shortround = true -- next round will be shorter, 2s + _G.minetest.sound_play("grinder",{pos=spawnpos, max_hear_distance = 100}) + abilitypoints[turn] = abilitypoints[turn]-10 + end + end + if turn == 1 then turn = 2 else turn = 1 end - self.label("turn " .. turn .. " " .. players[turn]) + turnmessage = "turn " .. turn .. " " .. players[turn] .. ",charge " .. abilitypoints[turn] .. "\n" + self.label(turnmessage) t=0 paused = false end @@ -96,6 +127,7 @@ if state == SIGNUP then 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!")) + init_game() end end @@ -104,17 +136,24 @@ if state == SIGNUP then elseif state == GAME then t = t + 1; - if t>5 then -- change of turn + if (t>turntimeout) or (shortround and t>shorttimeout) then -- change of turn change_turn() + else + self.label(turnmessage .. " " .. (turntimeout-t+1)) 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 + local points = abilitypoints[turn] + if x<1 or x>m or z<1 or z>n then -- outside area else local ppos = player.getpos(event.puncher) + + points = points + math.max(turntimeout-t-2,0); if points>40 then points = 40 end + abilitypoints[turn] = points + 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 @@ -146,5 +185,5 @@ elseif state == GAME then end elseif state == INTERMISSION then - t=t+1; if t> 15 then state = SIGNUP;players = {}; paused = true; init_game(); greeting() end + t=t+1; if t> 15 then state = SIGNUP;players = {}; paused = true; greeting() end end \ No newline at end of file