work in progress libraries are "client" and "server" stuff in both is also put in "common" note, behaviour might be subtly different all files must use "/" as a path separator (Unix Master Race) valid characters are listed here (ignoring the path separator): -.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz anything else is invalid and will just be outright rejected furthermore, no paths will be accepted where a . follows a / this means that hidden files on unixlikes will not be accepted! files that are meant to be intercepted should be prefixed with a * e.g. "*thismap" maximum path length is 128 stuff that's actually implemented will be marked with an @ lua base library stuff: math.* @ string.* @ pcall @ error @ print @ loadstring @ loadfile @ dofile @ require @ _G @ nothing else yet (TODO: work out what we want to keep, as there's stuff being used that isn't listed here!) check http://www.lua.org/manual/5.1/ for more info on these some notes: - dofile(x) = loadfile(x)() - loadfile(x) = common.fetch_block("lua", x) - common.fetch_block(ftype, x) = local obj = common.fetch_start(ftype, x) if obj ~= true then return obj end while true do local obj = common.fetch_poll() if obj ~= false then return obj end yield() end - require(x) uses loadfile() with a predefined list of paths and caches result - that means don't expect full "package" support - just about every "load" function is based on common.fetch_block common.base_dir @ current base directory (minus the "pkg/" bit) writing to this string will *not* change the base dir, so *don't write to it* or else you'll just screw up your own code! server.port @ port we are bound to there is NO alias to common here! if you want it, do it manually. common.version = {cmp={w,x,y,a,z},num,str="w.x.ya-z"} @ current engine version cmp is for the individual version components num is a comparable number (bit count for each: 5,5,7,5,10) str is the version as a string client.renderer @ current renderer used, "softgm" or "gl" there is NO alias to common here! if you want it, do it manually. note, as of 0.1.2-11, "softgm" has been removed. client/server.hook_tick = fn(sec_curtime, sec_delta)->sec_wait @ sets a hook called every "tick" returns a number telling it how long it wants to wait for "sec_curtime" is the current time elapsed from the start "sec_delta" is the time elapsed from the start of the last call to the hooked function setting this to nil will quit your game NOTE: DO NOT USE COMMON FOR THIS HOOK! (only client/server is accepted) server.hook_file = fn(neth, ftype, fname)->object sets a hook to intercept networked file requests "neth" is the socket handle pertaining to the file request can return nil to cancel the request can return true to perform the default request can return a string to load a different file instead otherwise, return an object to be serialised currently supported values for ftype: - "icemap" the rest must be either nil, true, or a string. client.hook_render = fn() @ sets a hook called every frame this is the only place where it is safe to render stuff such as models and HUDs and whatnot client.hook_key = fn(key, state, modif, uni) @ sets a hook called every time a key changes state is either true or false uni is an index of a unicode character, which is 0 if there is no unicode decoding client.hook_mouse_button = fn(button, state) @ sets a hook called every time a mouse button changes state is either true or false client.hook_mouse_motion = fn(x, y, dx, dy) @ sets a hook called every time the mouse is moved x, y are the current position dx, dy are the delta positions you will probably use the latter pair if the mouse is "locked" as x, y are not defined the same on all platforms! server.hook_connect = fn(neth, addrinfo) @ sets a hook called every time a client connects note, neth == true for "local multiplayer" mode "addrinfo" is a table which may or may not contain: - "proto" / "addr": protocol / address used, one of the following: - "local" / nil - "tcp/ip" or "tcp/ip6" / { ["ip"] = address, ["host"] = hostname or nil, ["cport"] = client port, ["sport"] = server port, } note, not all of the features are supported. server.hook_disconnect = fn(neth, server_force, reason) @ sets a hook called every time a client disconnects "server_force" is true where the server forces the disconnect client.mouse_lock_set(state) @ locks / unlocks the mouse depending on "state" client.mouse_visible_set(state) @ shows / hides the mouse depending on "state" client.mouse_warp(x, y) @ moves the mouse to x,y obj = common.fetch_start(ftype, fname) initiates a file fetch "ftype" is one of the following: - "lua": lua script - "map": map (autodetect) - "icemap": map (icemap) - in-memory maps are serialised as THIS. - "vxl": map (vxl) - CANNOT SAVE IN THIS FORMAT. - "pmf": pmf model - "tga": tga image - "png": png image - CANNOT SAVE IN THIS FORMAT (yet). - "json": json data - "wav": wav sound - "it": it music module - "bin": raw data - "ply": Stanford PLY model - "log": log data (TODO) for the server, this just loads the file from the disk. for the client, all clsave/* stuff is taken from the disk, but all other files are downloaded from the server. returns true if the fetch has started, nil if there is an error, or the object if this was an immediate load. if there is already a file in the queue, this will return "nil". obj, csize, usize, amount = common.fetch_poll() polls the "obj" is one of the following: - "nil" if transfer aborted or nothing is being fetched - in this case, all other fields will be nil - "false" if still downloading - the object you requested - in this case, another poll will just return nils "amount" is in the range 0 <= "amount" <= 1, and indicates how much is downloaded "csize" is the compressed size of the file "usize" is the uncompressed size the two sizes will be nil while unknown. note, all vxl maps will be converted to icemap before sending. obj = common.fetch_block(ftype, fname) @ fetches a file using common.fetch_* simply returns "nil" on error if there is already something being fetched, it will return "nil", too client.gfx_alpha_test(enable) @ (enable ? glEnable : glDisable)(GL_ALPHA_TEST); this is reset to true by the start of each frame client.gfx_clear_color() @ glClear(GL_COLOR_BUFFER_BIT); clears the screen (or FBO if we're using that) client.gfx_clear_depth() @ glClear(GL_DEPTH_BUFFER_BIT); clears the depth buffer AKA z buffer client.gfx_clear_stencil() @ glClear(GL_STENCIL_BUFFER_BIT); clears the stencil buffer client.gfx_depth_mask(enable) @ glDepthMask(enable ? GL_TRUE : GL_FALSE); enables (or disables) writing to the depth buffer AKA z buffer this is reset to true by the start of each frame client.gfx_depth_test(enable) @ (enable ? glEnable : glDisable)(GL_DEPTH_TEST); this is reset to true by the start of each frame this is ALSO reset to true after calling client.img_blit so watch out! client.gfx_stencil_func(func, ref, mask) @ glStencilFunc(func, ref, mask) func is a string containing one of these: - "0" - GL_NEVER - "<" - GL_LESS - "<=" - GL_LEQUAL - ">" - GL_GREATER - ">=" - GL_GEQUAL - "==" - GL_EQUAL - "~=" - GL_NOTEQUAL - "1" - GL_ALWAYS ref and mask are as per the OpenGL spec client.gfx_stencil_op(mask) @ glStencilOp(fail, zfail, zpass); mask is a 3-char string, each char representing fail, zfail and zpass respectively meaning of each char: - ; - GL_KEEP - 0 - GL_ZERO - = - GL_REPLACE - + - GL_INCR - - - GL_DECR - ~ = GL_INVERT client.gfx_stencil_test(enable) @ (enable ? glEnable : glDisable)(GL_STENCIL_TEST); this is reset to FALSE by the start of each frame client.gfx_viewport(x, y, width, height) @ glViewport(x, y, width, height); sets up the drawing region note, this also corrects the projection matrix WARNING: (x,y) denotes the *bottom*-left of the "screen"! this is reset to full screen by the start of each frame tex_units = client.gfx_tex_available() @ returns the number of texture units available note, this will be clamped by the engine limit, which is currently 8 uses GL_MAX_TEXTURE_UNITS_ARB will eventually use GL_MAX_TEXTURE_IMAGE_UNITS_ARB once shader support is in place success = client.gfx_fbo_available() @ returns if FBOs are available fbo = client.fbo_create(w, h, use_stencil) @ creates a new FBO with color/depth/stencil information returns nil on failure if use_stencil is set, create a GL_DEPTH_STENCIL_ATTACHMENT, otherwise just a GL_DEPTH_ATTACHMENT. WARNING: this also clears the current FBO status so if you're using an FBO, you'll have to call fbo_use again! (it does the equivalent of client.fbo_use(nil)) (why are you creating FBOs once you get around to actually using them anyway?) FBO note: the width and height you get will NOT be expanded; that is, a 320x200 FBO really IS 320x200, rather than 512x256. client.fbo_use(fbo) @ uses the given FBO "fbo" can be nil, in which case it uses the screen glver = client.gfx_glsl_available() @ returns if GLSL shaders are available return values: - "2.1" if at least GL 2.1 - "2.0" if GL 2.0 - nil otherwise shader, result = client.glsl_create(vertex_src_string, fragment_src_string, attr_array = {}) @ creates a shader attr_array is a list of strings to bind to attributes returns a shader handle in "shader" on success returns nil on failure shaders are garbage collected, so watch out! "result" is just the shader compiler output client.glsl_use(shader) @ uses a shader pass nil if you want to switch to default GL 1 behaviour note, fog is disabled while using a shader this does NOT mean that the far plane is extended for you! idx = client.glsl_get_uniform_loc(shader, name) @ gets the location of a uniform value given a shader returns nil on failure client.glsl_set_uniform_f(uniform_idx, val1[, val2[, val3[, val4]]]) @ sets a float uniform client.glsl_set_uniform_i(uniform_idx, val1[, val2[, val3[, val4]]]) @ sets an int uniform client.glsl_set_uniform_ui(uniform_idx, val1[, val2[, val3[, val4]]]) @ sets an unsigned int uniform client.map_enable_autorender(enabled) @ enable drawing of the map at the start of each frame enabled by default client.map_enable_ao(enabled) @ enable built-in ambient occlusion (unless smoothlighting is off) enabled by default client.map_enable_side_shading(enabled) @ enable built-in voxlap-style side shading enabled by default map = common.map_load(fname, fmt = "auto") @ loads a map, either "vxl" or "icemap" use "auto" to autodetect this will return nil if it fails. map = common.map_new(lx, ly, lz) @ creates a new map note, ly is the *height* of the new map also all dimensions must be powers of two, otherwise this will fail horribly! this will throw a lua error if it fails. common.map_free(map) @ free the given map if you don't do this then it's memoryleaktopia (plus i'm allowed to kill you) ALSO DON'T DO THIS TO THE CURRENT "SET" MAP TODO: clean up all models on game kill client.map_set_render_format(map, format) @ defines what render info a map will have format is as per common.va_make, except these are the formats supported: - 3v - vertex - must have exactly one - 3c - colour - must have at most one - 3n - normal - must have at most one - 2t - texcoord - must have at most one default formats: - 3v,3c,3n - gl_shaders enabled - 3v,3c - gl_shaders disabled you should probably just leave it at the defaults when shaders are disabled client.map_render(map, px, py, pz, tex = nil, afmt = nil, alpha = 1.0) @ render a map at global position px, py, pz see client.va_render_global for the meanings of the other arguments NOTE: at the moment it's probably not safe to render two of the same map unless the px,py,pz offset is small common.map_set(map) @ sets the "current" map to "map" note, "map" can be nil to disable rendering anything map = common.map_get() @ sets the "current" map may return "nil" common.map_save(map, fname, fmt = "icemap") @ saves a map to a file, either "vxl" or "icemap" this will throw a lua error if it fails. r, g, b, dist = client.map_fog_get() @ gets the current fog colour / distance client.map_fog_set(r, g, b, dist) @ sets the current fog colour / distance lx, ly, lz = common.map_get_dims() @ gets the map's dimensions these will be nil if there is no map loaded table = common.map_pillar_get(px, pz) @ returns a full pillar of data, skipping the total size header this will be nil if there is no map loaded note, the data wraps around here (wrt px,pz)! common.map_pillar_set(px, pz, table) @ sets a full pillar of data this will be nil if there is no map loaded the data is checked before setting, and will throw a lua error if it fails. note, the data wraps around here (wrt px,pz)! WARNING: You MUST update the side pillars in your code! You MUST ALSO fill in the newly-exposed blocks, as well as remove the newly-unexposed blocks! Otherwise, there WILL be gaps! WE'RE NOT DOING IT FOR YOU! common.map_mapents_get() @ gets the MapEnts chunk data (as a hopefully-json string) this will be nil if there is no map loaded or no MapEnts chunk common.map_mapents_set(mapents) @ sets the MapEnts chunk data mapents should be a json string client.camera_point(dx, dy, dz, zoom = 1.0, roll = 0.0) @ points the camera in a direction with zoom factor "zoom" and roll "roll" (in radians, sorry) client.camera_point_sky(dx, dy, dz, zoom = 1.0, sx = 0.0, sy = -1.0, sz = 0.0) @ points the camera in a direction with zoom factor "zoom" and sky arrow sx,sy,sz client.camera_move_local(dx, dy, dz) @ moves the camera in the camera-local direction (dx,dy,dz) client.camera_move_global(dx, dy, dz) @ moves the camera in the world direction (dx,dy,dz) client.camera_move_to(px, py, pz) @ moves the camera to the world position (px,py,pz) px, py, pz = client.camera_get_pos() @ gets the camera's position dx, dy, dz = client.camera_get_forward() @ gets the camera's forward vector client.camera_shade_set(xn, yn, zn, xp, yp, zp) sets the face shading levels per face (0 <= v <= 1) sides relative to map are: east, bottom, south, west, top, north pmf = common.model_new(bonemax = 5) @ creates a new model [ paragraph does not apply to 0.1.2-10 and higher ] remember to free it when you're done as this is only a light userdata "bonemax" is the initial maximum number of "bones" this model can have initially useful if you know exactly how many you'll need currently the number of bones is limited to 256 pmf = common.model_load_pmf(fname) @ loads a pmf from a file [ paragraph does not apply to 0.1.2-10 and higher ] remember to free it when you're done as this is only a light userdata limits of bones / points still applies here; files which exceed these WILL be rejected! returns nil on failure. success = common.model_save_pmf(pmf, fname) @ saves a pmf to a file common.model_free(pmf) @ free the given model [ paragraph does not apply to 0.1.2-10 and higher ] if you don't do this then it's memoryleaktopia (plus i'm allowed to kill you) TODO: clean up all models on game kill len = common.model_len(pmf) @ get the number of bones in this model pmf, boneidx = common.model_bone_new(pmf, ptmax = 20) @ creates a new bone "ptmax" is the initial maximum number of "points" this bone can have initially useful if you know exactly how many you'll need currently the number of points is limited to 4096 WARNING: YOU *MUST* TAKE THE pmf VALUE RETURNED AND *NOT* USE THE OLD ONE ANY LONGER! this is because realloc() is called on this dynamic list and it can seriously crash badly JUST SAYING common.model_bone_free(pmf, boneidx) @ removes a bone from the model name, table = common.model_bone_get(pmf, boneidx) @ gets a table with every point in the given bone each entry in the table has the following keys: uint16_t radius; // fixed point 8.8 int16_t x,y,z; // fixed point 8.8 uint8_t r,g,b; the reserved field of each point is inaccessible from this API common.model_bone_set(pmf, boneidx, name, table) @ replaces the bone's contents with that in the table note, bones will be rejected if: - name is > 15 chars long - radius,x,y,z,r,g,b are missing - 0 <= radius < 65536 fails - -32768 <= x,y,z < 32768 fails - 0 <= r,g,b < 256 fails these exceptions will throw a lua error boneidx = common.model_bone_find(pmf, name) @ finds the first bone with the given name note, this is case sensitive if it cannot be found, this returns nil client.model_render_bone_global(pmf, boneidx, px, py, pz, ry, rx, ry2, scale) @ renders a bone at world position (px,py,pz), rotated around Y by "ry" radians, then around X by "rx" radians, then around Y by "ry2" radians, and scaled "scale" times client.model_render_bone_local(pmf, boneidx, px, py, pz, ry, rx, ry2, scale) @ renders a bone at camera-local position (px,py,pz), rotated around Y by "ry" radians, then around X by "rx" radians, then around Y by "ry2" radians, and scaled "scale" times width, height = client.screen_get_dims() @ gets the dimensions of the screen screen_width, screen_height global vars set in main_client.lua and thus available in any client mod img, width, height = common.img_load(fname, fmt = "tga") @ loads an image with filename "fname" [ paragraph does not apply to 0.1.2-10 and higher ] remember to free it when you're done as this is only a light userdata format is either "tga" or "png" use "auto" to autodetect [ NOT SUPPORTED YET ] if this fails, img, width, height will all be nil img = common.img_new(width, height) @ creates a new 32bpp RGBA image [ paragraph does not apply to 0.1.2-10 and higher ] remember to free it when you're done as this is only a light userdata if this fails, img will be nil common.img_pixel_set(img, x, y, color) @ sets the pixel x,y on image "img" to "color" drawing is clipped common.img_pixel_get(img, x, y) @ gets the pixel x,y colour on image "img" x, y clipped common.img_fill(img, color) @ fills an image with a solid colour common.img_free(img) @ free the given image [ paragraph does not apply to 0.1.2-10 and higher ] if you don't do this then it's memoryleaktopia (plus i'm allowed to kill you) width, height = common.img_get_dims(img) @ gets the image's dimensions common.img_rect_fill(img, x, y, width, height, color) @ fills a sub-rect of an image with a solid colour client.img_blit(img, dx, dy, width = iwidth, height = iheight, sx = 0, sy = 0, color = 0xFFFFFFFF, scalex = 1.0, scaley = 1.0) @ blits an image onto screen position dx, dy (img can also be an FBO here) "color" indicates a base 0xAARRGGBB colour to use this is clipped to fit! use scalex/scaley to rescale source width/height to your likeness (sw = dw/scalex; sh = dh/scaley) client.img_blit_to(target, img, dx, dy, width = iwidth, height = iheight, sx = 0, sy = 0, color = 0xFFFFFFFF, scalex = 1.0, scaley = 1.0) @ blits an image onto another image at position dx, dy "color" indicates a base 0xAARRGGBB colour to use this is clipped to fit! use scalex/scaley to rescale source width/height to your likeness (sw = dw/scalex; sh = dh/scaley) tab = common.json_parse(str) @ parses the JSON string "str" yes, this parser is anal-retentive, and only allows one specific case where it breaks the rules! it also errors if it encounters a char 0 in the middle of a string. returns nil on error tab = common.json_load(fname) @ loads a JSON file and parses it returns nil on error common.json_write(fname, value) @ takes a value and saves it to a file as JSON makes the most sense if value is a table the following types are supported: - numbers - strings - true, false, nil - tables - arrays and therefore the following types are NOT supported: - functions/closures of ANY sort (C or Lua) - userdata (light or otherwise) - tables with a metatable (the metatable will be stripped) and i think that's about it for types really to tell the difference between tables and arrays it checks for the existence of the key "1" common.prng_new(seed, stream) @ creates a new pseudo-random number generator instance seed and stream are both optional returns a table of the following functions: - seed(seed, stream) - allows reseeding of the RNG, both arguments are optional - random() - random(max) - random(min, max) - returns a random number between min and max, defaulting to values of 0 and 1 - jump(step) - jump ahead by step amount, can be negative str = common.net_pack(fmt, ...) @ packs data into a string WARNING: signed/unsigned kinda does matter. if you give any values out of range, behaviour is undefined! (in other words, ARM behaves differently from x86) format is as such: b/B = signed/unsigned 8-bit [def 0] h/H = signed/unsigned 16-bit [def 0] i/I = signed/unsigned 32-bit [def 0] f = single-precision 32-bit float [def 0.0] d = double-precision 64-bit float [def 0.0] z = zero-terminated string [def ""] #s = fixed-length string (replace # with a decimal number) [def ""] throws a lua error if the fmt syntax is invalid. spews defaults if not enough arguments are provided. str = common.net_pack_array(fmt, len, arr) @ packs an array to a string fmt is one of the formats as seen in net_pack and ONLY ONE CHARACTER len is the amount of data to unpack ..., remain = common.net_unpack(fmt, str) @ unpacks data from a string will attempt to decode from start to end "remain" is the remainder of the string which was not decoded returns nil for fields that could not be decoded array = common.net_unpack_array(fmt, len, str) @ unpacks an array from a string see net_pack_array success = common.net_send(neth, str, unreliable = false) @ sends a packet "neth" is ignored C->S and should be nil S->C local multiplayer should set "neth" to "true" "unreliable" only makes sense on ENet connections; it is ignored for the TCP protocol. if "unreliable" is set to "true", we don't care if it arrives or not. COMPATIBILITY WARNING: before 0.0-50, neth pertains to an ACTUAL socket. from 0.0-50 onwards, neth pertains to a CLIENT HANDLE. this should not be of any concern to you unless you do Really Stupid Stuff. having said that, DO NOT EXPECT THIS TO BE A ONE-TO-ONE MAPPING TO CLIENT INDICES. oh, and as expected, same applies to common.net_recv and every other case you see a neth. str, neth = common.net_recv() @ receives a packet for C->S "neth" is nil S->C local multiplayer will result in "neth" being "true" returns nil, nil if nothing is there returns false, nil on the client if the connection was terminated server.net_kick(neth, reason) @ kicks a client from the server fails silently if neth is invalid (but still a plausible parameter), or the socket is closed / errors out throws a lua error for other weird errors. client.hook_kick = fn(reason) @ sets a hook called when the player gets kicked tcpsockfd = common.tcp_connect(hostname, port) @ creates a raw TCP socket returns nil on connection failure throws an error in other weird situations data = common.tcp_recv(tcpsockfd) @ receives from a TCP socket returns an empty string if nothing arrived returns false on error, such as disconnection WARNING: DO NOT MAKE UP YOUR OWN SOCKFDS! DOING THAT CAN REALLY SCREW UP YOUR CONNECTIONS AND WHATNOT. SAME WARNING APPLIES TO SENDING. DO NOT MIX TCPSOCKFDS UP WITH UDPSOCKFDS. IT DOESN'T WORK. THAT MEANS DON'T BE A SMARTARSE - LEAVE STDIN/OUT/ERR ALONE. NOT EVERYONE DOESN'T USE WINDOWS. remain = common.tcp_send(tcpsockfd, data) @ sends data to a TCP socket returns the data that wasn't quite sent just yet, which can be an empty string returns false on error, such as disconnection common.tcp_close(tcpsockfd) @ closes a TCP socket udpsockfd = common.udp_open() @ creates a raw UDP socket returns nil on socket creation failure throws an error in other weird situations data, host, port = common.udp_recvfrom(udpsockfd) @ receives from a UDP socket returns an empty string + nils if nothing arrived returns false on error, such as disconnection remain = common.udp_sendto(udpsockfd, data, host, port) @ sends data to a host using a UDP socket returns the data that wasn't quite sent just yet, which can be an empty string returns false on error, such as disconnection common.udp_close(udpsockfd) @ closes a UDP socket va = common.va_make(vlist, oldva = nil, format = "3v,3c") @ makes a vertex array of triangles vlist is a list of lists of floats in a format like this: - {{x,y,z,r,g,b}, ...} ("3v,3c" since 0.2a-1, format arg unsupported there!) - {{x,y,z,r,g,b,u,v}, ...} ("3v,3c,2t" since 0.2a-2) - {{x,y,z,r,g,b,a}, ...} ("3v,4c" since 0.2.1-1) - {{x,y,z,r,g,b,a,u,v}, ...} ("3v,4c,2t" since 0.2.1-1) - {{x,y,z,u,v}, ...} ("3v,2t" since 0.2a-2) anything else requires 0.2.1-7 here are the formats you can use: - 2v,3v,4v - vertex - must have exactly one - 3c,4c - colour - must have at most one - 3n - normal - must have at most one - 1t,2t,3t,4t - texcoord - must have at most 8 - 1a,2a,3a,4a - vertex attribute - must have at most 32 (note for 0.1.2-7: attributes aren't actually supported, but they *are* parsed. wait for shader support before using them!) if you don't follow your given format this can throw an error, so WATCH OUT. oldva, when set, replaces an existing VA with new data, and then this function will (theoretically) return oldva. client.va_render_global(va, px, py, pz, ry, rx, ry2, scale, tex = nil, afmt = nil, alpha = 1.0) @ draws a vertex array relative to the world tex must be a power-of-two texture, or nil, or a list of either of those two current max for a texture list is 8 afmt consists of two characters, or is nil note, if afmt is nil, blending is disabled! (i.e. it behaves like you used "10") first character defines source from one of these: 01CRahAHs second character defines dest from one of these: 01crahAH meanings of each char: - sd 0 = GL_ZERO - sd 1 = GL_ONE - .d c = GL_SRC_COLOR - s. C = GL_DST_COLOR - .d r = GL_ONE_MINUS_SRC_COLOR - s. R = GL_ONE_MINUS_DST_COLOR - sd a = GL_SRC_ALPHA - sd A = GL_DST_ALPHA - sd h = GL_ONE_MINUS_SRC_ALPHA - sd H = GL_ONE_MINUS_DST_ALPHA - s. s = GL_SRC_ALPHA_SATURATE useful blend modes: - additive: "11" - multiplicative: "C0" (?) - translucent: "ah" note, "alpha" only works with VAs that don't provide a colour client.va_render_local(va, px, py, pz, ry, rx, ry2, scale, tex = nil, afmt = nil, alpha = 1.0) @ draws a vertex array relative to the camera see above for notes wav = common.wav_load(fname) @ loads a sound with filename "fname" supported codecs are PCM (0x0001) and (as of 0.2.1-26) IMA ADPCM (0x0011) [ paragraph does not apply to 0.1.2-10 and higher ] remember to free it when you're done as this is only a light userdata common.wav_free(wav) @ free the given sound [ paragraph does not apply to 0.1.2-10 and higher ] if you don't do this then it's memoryleaktopia (plus i'm allowed to kill you) client.wav_cube_size(size) @ sets the size of a block in metres for sound calculations chn = client.wav_play_global(wav, x, y, z, vol = 1.0, freq_mod = 1.0, vol_spread = ?) @ play the given sound at the given world position TODO: define vol_spread properly returns an index of a channel returns nil on error chn = client.wav_play_local(wav, x = 0.0, y = 0.0, z = 0.0, vol = 1.0, freq_mod = 1.0, vol_spread = ?) @ play the given sound at the given camera-local position returns an index of a channel returns nil on error exists = client.wav_chn_exists(chn) @ checks if an allocated channel still exists if a channel stops, it is garbage collected success = client.wav_chn_update(chn, x = nil, y = nil, z = nil, vol = nil, freq_mod = nil, vol_spread = nil) @ updates information pertaining to a channel any field which is nil is not affected returns false if the channel no longer exists client.wav_kill(chn) @ stops and removes a channel if chn == "true", kills all channels mus = common.mus_load_it(fname) @ loads an ImpulseTracker module with filename "fname" remember to free it when you're done as this is only a light userdata common.mus_free(mus) @ free the given atrosity of demofunk if you don't do this then it's memoryleaktopia (plus i'm allowed to kill you) MAKE SURE YOU HAVE CALLED client.mus_stop IF YOU WERE PLAYING THIS FILE otherwise expect a crash. success = client.mus_play(mus, order=0, row=0) @ starts playing module "mus" at given order/row client.mus_stop() @ stops playing music client.mus_vol_set(vol) @ sets the mixing volume for the music str = common.bin_load(fname) @ loads a raw file into a string common.bin_save(fname, str) @ saves a raw string to a file common.mk_compat_disable() @ disables backwards compat with 0.1.2-10 and earlier what this currently does is it just removes the border wrap. client.mk_set_title(title) @ changes the window title default is "iceball" client.mk_sys_execv(arglist) @ relaunches Iceball with the given argv[] list THIS CAN ONLY BE DONE IN -s 0 MODE! see pkg/iceball/launch/main_client.lua.