diff --git a/pkg/base/obj_player.lua b/pkg/base/obj_player.lua index a8d6dde..959e9a4 100644 --- a/pkg/base/obj_player.lua +++ b/pkg/base/obj_player.lua @@ -1,1971 +1,1971 @@ ---[[ - This file is part of Ice Lua Components. - - Ice Lua Components is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Ice Lua Components is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with Ice Lua Components. If not, see . -]] - -function new_player(settings) - local this = {} this.this = this this.this.this = this this = this.this - - this.team = settings.team or math.floor(math.random()*2) - this.squad = settings.squad or nil - this.weapon = settings.weapon or WPN_RIFLE - this.pid = settings.pid or error("pid must be set when creating player!") - this.alive = false - this.spawned = false - this.zooming = false - - this.mdl_block = common.model_new(1) - this.mdl_block = common.model_bone_new(this.mdl_block) - - this.mdl_player = common.model_new(4) - this.mdl_player = common.model_bone_new(this.mdl_player) - this.mdl_player = common.model_bone_new(this.mdl_player) - this.mdl_player = common.model_bone_new(this.mdl_player) - this.mdl_player = common.model_bone_new(this.mdl_player) - - this.score = 0 - this.kills = 0 - this.deaths = 0 - - local function prv_recolor_team(r,g,b) - if not client then return end - local mname,mdata - mname,mdata = common.model_bone_get(mdl_player, mdl_player_head) - recolor_component(r,g,b,mdata) - common.model_bone_set(this.mdl_player, mdl_player_head, mname, mdata) - mname,mdata = common.model_bone_get(mdl_player, mdl_player_body) - recolor_component(r,g,b,mdata) - common.model_bone_set(this.mdl_player, mdl_player_body, mname, mdata) - mname,mdata = common.model_bone_get(mdl_player, mdl_player_arm) - recolor_component(r,g,b,mdata) - common.model_bone_set(this.mdl_player, mdl_player_arm, mname, mdata) - mname,mdata = common.model_bone_get(mdl_player, mdl_player_leg) - recolor_component(r,g,b,mdata) - common.model_bone_set(this.mdl_player, mdl_player_leg, mname, mdata) - end - - function this.recolor_team() - local c = teams[this.team].color_mdl - local r,g,b - r,g,b = c[1],c[2],c[3] - prv_recolor_team(r,g,b) - end - - local function prv_recolor_block(r,g,b) - if not client then return end - local mname,mdata - mname,mdata = common.model_bone_get(mdl_block, mdl_block_bone) - recolor_component(r,g,b,mdata) - common.model_bone_set(this.mdl_block, mdl_block_bone, mname, mdata) - end - - prv_recolor_block(0,0,0) - do - local c = teams[this.team].color_mdl - local r,g,b - r,g,b = c[1],c[2],c[3] - prv_recolor_team(r,g,b) - end - - function this.block_recolor() - prv_recolor_block(this.blk_color[1],this.blk_color[2],this.blk_color[3]) - end - - function this.input_reset() - this.ev_forward = false - this.ev_back = false - this.ev_left = false - this.ev_right = false - - this.ev_jump = false - this.ev_crouch = false - this.ev_sneak = false - - this.ev_lmb = false - this.ev_rmb = false - end - - this.input_reset() - - function this.free() - if this.mdl_block then common.model_free(this.mdl_block) end - if this.mdl_player then common.model_free(this.mdl_player) end - end - - this.t_rcirc = nil - - function this.prespawn() - this.alive = false - this.spawned = false - - this.grounded = false - this.crouching = false - - this.arm_rest_right = 0.0 - this.arm_rest_left = 1.0 - - this.t_respawn = nil - this.t_switch = nil - this.t_nadeboom = nil - this.t_newnade = nil - this.t_newblock = nil - this.t_newspade1 = nil - this.t_newspade2 = nil - this.t_step = nil - - this.dangx, this.dangy = 0, 0 - this.vx, this.vy, this.vz = 0, 0, 0 - - this.blx1, this.bly1, this.blz1 = nil, nil, nil - this.blx2, this.bly2, this.blz2 = nil, nil, nil - - this.sx, this.sy, this.sz = 0, -1, 0 - this.drunkx, this.drunkz = 0, 0 - this.drunkfx, this.drunkfz = 0, 0 - - this.blk_color = {0x7F,0x7F,0x7F} - this.block_recolor() - this.blk_color_x = 3 - this.blk_color_y = 0 - - this.jerkoffs = 0.0 - - this.zoom = 1.0 - this.zooming = false - - this.health = 100 - this.blocks = 25 - this.grenades = 2 - - this.wpn = weapons[this.weapon](this) - - this.tool = 2 - - this.has_intel = nil - end - - local function prv_spawn_cont1() - this.prespawn() - - this.alive = true - this.spawned = true - this.t_switch = true - end - - function this.spawn_at(x,y,z,ya,xa) - this.x = x - this.y = y - this.z = z - this.angy = ya - this.angx = xa - - return prv_spawn_cont1() - end - - function this.spawn() - local xlen,ylen,zlen - xlen,ylen,zlen = common.map_get_dims() - - while true do - this.x = math.floor(math.random()*xlen/4.0)+0.5 - this.z = math.floor(math.random()*zlen)+0.5 - if this.team == 1 then this.x = xlen - this.x end - this.y = (common.map_pillar_get(this.x, this.z))[1+1] - if this.y < ylen-1 then break end - end - this.y = this.y - 3.0 - this.angy, this.angx = math.pi/2.0, 0.0 - if this.team == 1 then this.angy = this.angy-math.pi end - - return prv_spawn_cont1() - end - - this.name = settings.name or "Noob" - if server then - this.spawn() - else - this.prespawn() - end - - function this.tool_switch(tool) - if not this.alive then return end - - if this.tool == TOOL_GUN then - if this.wpn then - this.wpn.firing = false - this.wpn.reloading = false - end - this.zooming = false - this.arm_rest_right = 0 - end - this.t_switch = true - if client and this == players[players.current] and this.tool ~= tool then - common.net_send(nil, common.net_pack("BBB" - , 0x17, 0x00, tool)) - end - this.tool = tool - this.ev_lmb = false - this.ev_rmb = false - - -- hud - if this.tools_align then - this.tools_align.visible = true - this.tools_align.static_alarm{name='viz', - time=3.0, on_trigger=function() this.tools_align.visible = false end} - end - - end - - function this.tool_switch_next() - new_tool = (this.tool + 1) % (TOOL_NADE + 1) -- Nade is last weapon - this.tool_switch(new_tool) - end - - function this.tool_switch_prev() - new_tool = (this.tool - 1) % (TOOL_NADE + 1) -- Nade is last weapon - this.tool_switch(new_tool) - end - - --[[ - keys are: - 0x01: up - 0x02: down - 0x04: left - 0x08: right - 0x10: sneak | scope - 0x20: crouch - 0x40: jump - 0x80: * RESERVED * - ]] - - function this.get_pos() - return this.x, this.y, this.z - end - - function this.set_pos_recv(x, y, z) - this.x = x - this.y = y - this.z = z - end - - function this.get_orient() - local keys = 0 - if this.ev_forward then keys = keys + 0x01 end - if this.ev_back then keys = keys + 0x02 end - if this.ev_left then keys = keys + 0x04 end - if this.ev_right then keys = keys + 0x08 end - if this.ev_sneak or this.zooming then keys = keys + 0x10 end - if this.ev_crouch then keys = keys + 0x20 end - if this.ev_jump then keys = keys + 0x40 end - --if this.ev_aimbot then keys = keys + 0x80 end - - return this.angy, this.angx, keys - end - - function this.set_orient_recv(ya, xa, keys) - this.angy = ya - this.angx = xa - - this.ev_forward = bit_and(keys,0x01) ~= 0 - this.ev_back = bit_and(keys,0x02) ~= 0 - this.ev_left = bit_and(keys,0x04) ~= 0 - this.ev_right = bit_and(keys,0x08) ~= 0 - this.ev_sneak = bit_and(keys,0x10) ~= 0 - this.ev_crouch = bit_and(keys,0x20) ~= 0 - this.ev_jump = bit_and(keys,0x40) ~= 0 - --this.ev_aimbot = bit_and(keys,0x80) ~= 0 - end - - function this.recoil(sec_current, recoil_y, recoil_x) - local xrec = recoil_x*math.cos(sec_current*math.pi*2)*math.pi*20 - local ydip = math.sin(this.angx) - local ycos = math.cos(this.angx) - local yrec = recoil_y + ydip - local ydist = math.sqrt(ycos*ycos+yrec*yrec) - this.angy = this.angy + xrec - this.angx = math.asin(yrec/ydist) - end - - function this.update_score() - net_broadcast(nil, common.net_pack("BBBBhhhzz", - 0x05, this.pid, - this.team, this.weapon, - this.score, this.kills, this.deaths, - this.name, this.squad)) - sort_players() - end - - function this.tent_restock() - this.health = 100 - this.blocks = 100 - this.grenades = 4 - if this.wpn then - this.wpn.ammo_clip = this.wpn.cfg.ammo_clip - this.wpn.ammo_reserve = this.wpn.cfg.ammo_reserve - end - if server then - net_broadcast(nil, common.net_pack("BB", 0x15, this.pid)) - end - end - - function this.set_health_damage(amt, kcol, kmsg, enemy) - this.health = amt - - if this.health <= 0 and this.alive then - if server then - this.intel_drop() - this.deaths = this.deaths + 1 - if enemy == nil then - -- do nothing -- - elseif enemy == this then - enemy.score = enemy.score + SCORE_SUICIDE - elseif enemy.team == this.team then - enemy.score = enemy.score + SCORE_TEAMKILL - else - enemy.score = enemy.score + SCORE_KILL - enemy.kills = enemy.kills + 1 - end - if enemy ~= nil and enemy ~= this then - enemy.update_score() - end - this.update_score() - net_broadcast(nil, common.net_pack("BIz", 0x0F, kcol, kmsg)) - end - --chat_add(chat_killfeed, nil, kmsg, kcol) - this.health = 0 - this.alive = false - end - - if server then - net_broadcast(nil, common.net_pack("BBB", 0x14, this.pid, this.health)) - end - end - - function this.damage(amt, kcol, kmsg, enemy) - return this.set_health_damage( - this.health - amt, kcol, kmsg, enemy) - end - - function this.fall_damage(amt) - --print("damage",this.name,part,amt) - local l = teams[this.team].color_chat - r,g,b = l[1],l[2],l[3] - - local c = argb_split_to_merged(r,g,b) - - local kmsg = this.name.." found a high place" - this.damage(amt, c, kmsg, this) - end - - function this.gun_damage(part, amt, enemy) - --print("damage",this.name,part,amt) - - if not server then - return - end - - local midmsg = " killed " - if this.team == enemy.team then - midmsg = " teamkilled " - end - - local r,g,b - r,g,b = 0,0,0 - - local l = teams[enemy.team].color_chat - r,g,b = l[1],l[2],l[3] - - local c = argb_split_to_merged(r,g,b) - - local kmsg = enemy.name..midmsg..this.name - this.damage(amt, c, kmsg, enemy) - end - - function this.spade_damage(part, amt, enemy) - --print("damage",this.name,part,amt) - - if not server then - return - end - - local midmsg = " spaded " - if this.team == enemy.team then - error("THIS SHOULD NEVER HAPPEN WORST PYSPADES BUG EVER") - end - - local r,g,b - r,g,b = 0,0,0 - - local l = teams[enemy.team].color_chat - r,g,b = l[1],l[2],l[3] - - local c = argb_split_to_merged(r,g,b) - - local kmsg = enemy.name..midmsg..this.name - this.damage(amt, c, kmsg, enemy) - end - - function this.grenade_damage(amt, enemy) - --print("damage",this.name,part,amt) - local midmsg = " grenaded " - if this.team == enemy.team and this ~= enemy then - error("THIS SHOULD NEVER HAPPEN") - end - - local r,g,b - r,g,b = 0,0,0 - - local l = teams[enemy.team].color_chat - r,g,b = l[1],l[2],l[3] - - local c = argb_split_to_merged(r,g,b) - - local kmsg = enemy.name..midmsg..this.name - if enemy == this then - kmsg = this.name.." exploded" - end - - this.damage(amt, c, kmsg, enemy) - end - - function this.intel_pickup(intel) - if this.has_intel or intel.team == this.team then - return false - end - - if server then - local x,y,z,f - x,y,z = intel.get_pos() - intel.visible = false - f = intel.get_flags() - net_broadcast(nil, common.net_pack("BHhhhB", 0x12, intel.iid, x,y,z,f)) - net_broadcast(nil, common.net_pack("BHB", 0x16, intel.iid, this.pid)) - local s = "* "..this.name.." has picked up the "..teams[intel.team].name.." intel." - net_broadcast(nil, common.net_pack("BIz", 0x0E, 0xFF800000, s)) - this.has_intel = intel - end - - return true - end - - function this.intel_drop() - if server then - local intel = this.has_intel - --print("dropped", intel) - if not intel then - return - end - - intel.intel_drop() - this.has_intel = nil - - local s = "* "..this.name.." has dropped the "..teams[intel.team].name.." intel." - net_broadcast(nil, common.net_pack("BIz", 0x0E, 0xFF800000, s)) - end - end - - function this.intel_capture(sec_current) - if server then - local intel = this.has_intel - if not intel then - return - end - - intel.intel_capture(sec_current) - this.has_intel = nil - - local s = "* "..this.name.." has captured the "..teams[intel.team].name.." intel." - net_broadcast(nil, common.net_pack("BIz", 0x0E, 0xFF800000, s)) - net_broadcast_team(this.team, common.net_pack("B", 0x1C)) - end - end - - function this.throw_nade(sec_current) - local sya = math.sin(this.angy) - local cya = math.cos(this.angy) - local sxa = math.sin(this.angx) - local cxa = math.cos(this.angx) - local fwx,fwy,fwz - fwx,fwy,fwz = sya*cxa, sxa, cya*cxa - - local n = new_nade({ - x = this.x, - y = this.y, - z = this.z, - vx = fwx*MODE_NADE_SPEED*MODE_NADE_STEP+this.vx*MODE_NADE_STEP, - vy = fwy*MODE_NADE_SPEED*MODE_NADE_STEP+this.vy*MODE_NADE_STEP, - vz = fwz*MODE_NADE_SPEED*MODE_NADE_STEP+this.vz*MODE_NADE_STEP, - fuse = math.max(0, this.t_nadeboom - sec_current) - }) - nade_add(n) - common.net_send(nil, common.net_pack("BhhhhhhH", - 0x1B, - math.floor(n.x*32+0.5), - math.floor(n.y*32+0.5), - math.floor(n.z*32+0.5), - math.floor(n.vx*256+0.5), - math.floor(n.vy*256+0.5), - math.floor(n.vz*256+0.5), - math.floor(n.fuse*100+0.5))) - end - - function this.tick_listeners(sec_current, sec_delta) - if this.scene then - this.scene.pump_listeners(sec_delta, input_events) - end - end - - function this.tick(sec_current, sec_delta) - local xlen,ylen,zlen - xlen,ylen,zlen = common.map_get_dims() - - if not this.spawned then - return - end - - if (not this.alive) and (not this.t_respawn) then - this.t_respawn = sec_current + MODE_RESPAWN_TIME - this.input_reset() - this.wpn.firing = false - this.wpn.reloading = false - this.zooming = false - end - - if this.t_respawn then - if server and this.t_respawn <= sec_current then - --print("server respawn!") - this.t_respawn = nil - this.spawn() - net_broadcast(nil, common.net_pack("BBfffBB", - 0x10, this.pid, - this.x, this.y, this.z, - this.angy*128/math.pi, this.angx*256/math.pi)) - else - -- any last requests? - end - end - - if not this.alive then - this.input_reset() - end - - local inwater = (this.y > ylen-3) - - if this.t_switch == true then - this.t_switch = sec_current + MODE_DELAY_TOOL_CHANGE - end - - if this.t_rcirc and sec_current >= this.t_rcirc then - this.t_rcirc = nil - end - - if this.alive and this.t_switch then - if sec_current > this.t_switch then - this.t_switch = nil - this.arm_rest_right = 0 - else - local delta = this.t_switch-sec_current - this.arm_rest_right = math.max(0.0,delta/0.2) - end - end - - if this.t_newblock and sec_current >= this.t_newblock then - this.t_newblock = nil - end - - if this.t_newspade1 and sec_current >= this.t_newspade1 then - this.t_newspade1 = nil - end - - if this.t_newnade and sec_current >= this.t_newnade then - this.t_newnade = nil - end - - if this.t_nadeboom then - if (not this.ev_lmb) or sec_current >= this.t_nadeboom then - this.throw_nade(sec_current) - this.t_newnade = sec_current + MODE_DELAY_NADE_THROW - this.t_nadeboom = nil - this.ev_lmb = false - end - end - - if not this.ev_rmb then - this.t_newspade2 = nil - end - - if this.t_newspade2 and sec_current >= this.t_newspade2 and this.blx2 then - if this.blx2 >= 0 and this.blx2 < xlen and this.blz2 >= 0 and this.blz2 < zlen then - if this.bly2-1 <= ylen-3 then - common.net_send(nil, common.net_pack("BHHH", - 0x0A, - this.blx2, this.bly2, this.blz2)) - end - end - - this.t_newspade2 = nil - end - - if client then - local moving = (this.ev_left or this.ev_right or this.ev_forward or this.ev_back) - local sneaking = (this.ev_crouch or this.ev_sneak or this.zooming) - - if moving and not sneaking then - if not this.t_step then - this.t_step = sec_current + 0.5 - end - if this.t_step < sec_current then - local freq_mod = (inwater and 0.25) or 1.0 - local tdiff = 0.01 - if this.grounded then - client.wav_play_global(wav_steps[ - math.floor(math.random()*#wav_steps)+1], - this.x, this.y, this.z, - 1.0, freq_mod) - tdiff = 0.5 - end - this.t_step = this.t_step + tdiff - if this.t_step < sec_current then - this.t_step = sec_current + tdiff - end - end - else - this.t_step = nil - end - - if this.wpn.reloading then - this.reload_msg.visible = false - end - end - - -- calc X delta angle - local nax = this.angx + this.dangx - if nax > math.pi*0.49 then - nax = math.pi*0.49 - elseif nax < -math.pi*0.49 then - nax = -math.pi*0.49 - end - this.dangx = (nax - this.angx) - - -- apply delta angles - if MODE_DRUNKCAM_LOCALTURN and this.dangy ~= 0 then - this.angx = this.angx + this.dangx - - local fx,fy,fz -- forward - local sx,sy,sz -- sky - local ax,ay,az -- horiz side - local bx,by,bz -- vert side - - local sya = math.sin(this.angy) - local cya = math.cos(this.angy) - local sxa = math.sin(this.angx) - local cxa = math.cos(this.angx) - - -- get vectors - fx,fy,fz = vnorm(sya*cxa, sxa, cya*cxa) - sx,sy,sz = vnorm(this.sx, this.sy, this.sz) - ax,ay,az = vnorm(vcross(fx,fy,fz,sx,sy,sz)) - bx,by,bz = vnorm(vcross(fx,fy,fz,ax,ay,az)) - - - -- rotate forward and sky - - fx,fy,fz = vrotate(this.dangy,fx,fy,fz,bx,by,bz) - sx,sy,sz = vrotate(this.dangy,sx,sy,sz,bx,by,bz) - - -- normalise F and S - fx,fy,fz = vnorm(fx,fy,fz) - sx,sy,sz = vnorm(sx,sy,sz) - - -- stash sky arrow - this.sx = sx - this.sy = sy - this.sz = sz - - -- convert forward from vector to polar - this.angx = math.asin(fy) - local langx = this.angx - - if math.cos(langx) <= 0.0 then - fx = -fx - fz = -fz - end - - this.angy = math.atan2(fx,fz) - - --print("polar",this.angx, this.angy) - - else - this.angx = this.angx + this.dangx - this.angy = this.angy + this.dangy - end - this.dangx = 0 - this.dangy = 0 - - if this.zooming then - this.zoom = 3.0 - else - this.zoom = 1.0 - end - - -- set camera direction - local sya = math.sin(this.angy) - local cya = math.cos(this.angy) - local sxa = math.sin(this.angx) - local cxa = math.cos(this.angx) - local fwx,fwy,fwz - fwx,fwy,fwz = sya*cxa, sxa, cya*cxa - - if client and this.alive and (not this.t_switch) then - if this.ev_lmb then - if this.tool == TOOL_BLOCK and this.blx1 then - if (not this.t_newblock) and this.blocks > 0 then - if this.blx1 >= 0 and this.blx1 < xlen and this.blz1 >= 0 and this.blz1 < zlen then - if this.bly1 <= ylen-3 and map_is_buildable(this.blx1, this.bly1, this.blz1) then - common.net_send(nil, common.net_pack("BHHHBBBB", - 0x08, - this.blx1, this.bly1, this.blz1, - this.blk_color[3], - this.blk_color[2], - this.blk_color[1], - 1)) - this.blocks = this.blocks - 1 - this.t_newblock = sec_current + MODE_DELAY_BLOCK_BUILD - this.t_switch = this.t_newblock - end - end - end - elseif this.tool == TOOL_SPADE then - if (not this.t_newspade1) then - - -- see if there's anyone we can kill - local d = this.bld2 or 5 -- NOTE: cannot spade through walls anymore. Sorry guys :/ - local hurt_idx = nil - local hurt_part = nil - local hurt_part_idx = 0 - local hurt_dist = d*d - local i,j - - for i=1,players.max do - local p = players[i] - if p and p ~= this and p.alive and p.team ~= this.team then - local dx = p.x-this.x - local dy = p.y-this.y+0.1 - local dz = p.z-this.z - - for j=1,3 do - local dd = dx*dx+dy*dy+dz*dz - - local dotk = dx*fwx+dy*fwy+dz*fwz - local dot = math.sqrt(dd-dotk*dotk) - if dot < 0.55 and dd < hurt_dist then - hurt_idx = i - hurt_dist = dd - hurt_part_idx = j - hurt_part = ({"head","body","legs"})[j] - - break - end - dy = dy + 1.0 - end - end - end - - if hurt_idx then - if server then - players[hurt_idx].spade_damage( - hurt_part, 1000, this) - else - common.net_send(nil, common.net_pack("BBB" - , 0x13, hurt_idx, hurt_part_idx)) - end - elseif this.blx2 then - if this.blx2 >= 0 and this.blx2 < xlen and this.blz2 >= 0 and this.blz2 < zlen then - if this.bly2 <= ylen-3 then - bhealth_damage(this.blx2, this.bly2, this.blz2, MODE_BLOCK_DAMAGE_SPADE) - this.t_newspade1 = sec_current + MODE_DELAY_SPADE_HIT - end - end - end - - end - elseif this.tool == TOOL_NADE then - if (not this.t_newnade) and this.grenades > 0 then - if (not this.t_nadeboom) then - this.grenades = this.grenades - 1 - this.t_nadeboom = sec_current + MODE_NADE_FUSE - end - end - else - - end - elseif this.ev_rmb then - if this.tool == TOOL_BLOCK and this.blx3 and this.alive then - local ct,cr,cg,cb - ct,cr,cg,cb = map_block_pick(this.blx3, this.bly3, this.blz3) - if ct ~= nil then - this.blk_color = {cr,cg,cb} - common.net_send(nil, common.net_pack("BBBBB", - 0x18, 0x00, - this.blk_color[1],this.blk_color[2],this.blk_color[3])) - end - this.ev_rmb = false - elseif this.tool == TOOL_SPADE and this.blx2 and this.alive then - if (not this.t_newspade2) then - this.t_newspade2 = sec_current - + MODE_DELAY_SPADE_DIG - end - end - end - end - - -- move along - local mvx = 0.0 - local mvy = 0.0 - local mvz = 0.0 - - if this.ev_forward then - mvz = mvz + 1.0 - end - if this.ev_back then - mvz = mvz - 1.0 - end - if this.ev_left then - mvx = mvx + 1.0 - end - if this.ev_right then - mvx = mvx - 1.0 - end - - if this.ev_crouch then - if this.grounded and not this.crouching then - if MODE_SOFTCROUCH then this.jerkoffs = this.jerkoffs - 1 end - this.y = this.y + 1 - end - this.crouching = true - end - if this.ev_jump and this.alive and (MODE_CHEAT_FLY or this.grounded) then - this.vy = -7 - this.ev_jump = false - if client then - client.wav_play_global(wav_jump_up, this.x, this.y, this.z) - end - end - - -- normalise mvx,mvz - local mvd = math.max(0.00001,math.sqrt(mvx*mvx + mvz*mvz)) - mvx = mvx / mvd - mvz = mvz / mvd - - -- apply base slowdown - local mvspd = 8.0 - local mvchange = 10.0 - mvx = mvx * mvspd - mvz = mvz * mvspd - - -- apply extra slowdowns - if not this.grounded then - mvx = mvx * 0.6 - mvz = mvz * 0.6 - mvchange = mvchange * 0.3 - end - if inwater then - mvx = mvx * 0.6 - mvz = mvz * 0.6 - end - if this.crouching then - mvx = mvx * 0.5 - mvz = mvz * 0.5 - end - if this.zooming or this.ev_sneak then - mvx = mvx * 0.5 - mvz = mvz * 0.5 - end - - -- apply rotation - mvx, mvz = mvx*cya+mvz*sya, mvz*cya-mvx*sya - - this.vx = this.vx + (mvx - this.vx)*(1.0-math.exp(-sec_delta*mvchange)) - this.vz = this.vz + (mvz - this.vz)*(1.0-math.exp(-sec_delta*mvchange)) - this.vy = this.vy + 2*9.81*sec_delta - - local ox, oy, oz - local nx, ny, nz - local tx1,ty1,tz1 - ox, oy, oz = this.x, this.y, this.z - this.x, this.y, this.z = this.x + this.vx*sec_delta, this.y + this.vy*sec_delta, this.z + this.vz*sec_delta - nx, ny, nz = this.x, this.y, this.z - this.jerkoffs = this.jerkoffs * math.exp(-sec_delta*15.0) - - local by1, by2 - by1, by2 = -0.3, 2.5 - if this.crouching then - if (not this.ev_crouch) and box_is_clear( - ox-0.39, oy-0.8, oz-0.39, - ox+0.39, oy-0.3, oz+0.39) then - this.crouching = false - oy = oy - 1 - if this.grounded then - ny = ny - 1 - if MODE_SOFTCROUCH then this.jerkoffs = this.jerkoffs + 1 end - end - end - end - if this.crouching or MODE_AUTOCLIMB then - by2 = by2 - 1 - if MODE_AUTOCLIMB then - by2 = by2 - 0.01 - end - end - - if this.alive then - tx1,ty1,tz1 = trace_map_box( - ox, oy, oz, - nx, ny, nz, - -0.4, by1, -0.4, - 0.4, by2, 0.4, - false) - else - tx1,ty1,tz1 = nx,ny,nz - end - - if this.alive and MODE_AUTOCLIMB and not this.crouching then - by2 = by2 + 1.01 - end - - if this.alive and MODE_AUTOCLIMB and not this.crouching then - local jerky = ty1 - - local h1a,h1b,h1c,h1d - local h2a,h2b,h2c,h2d - local h1,h2,_ - _,h2 = trace_gap(tx1,ty1+1.0,tz1) - h1a,h2a = trace_gap(tx1-0.39,ty1+1.0,tz1-0.39) - h1b,h2b = trace_gap(tx1+0.39,ty1+1.0,tz1-0.39) - h1c,h2c = trace_gap(tx1-0.39,ty1+1.0,tz1+0.39) - h1d,h2d = trace_gap(tx1+0.39,ty1+1.0,tz1+0.39) - - if (not h1a) or (h1b and h1a < h1b) then h1a = h1b end - if (not h1a) or (h1c and h1a < h1c) then h1a = h1c end - if (not h1a) or (h1d and h1a < h1d) then h1a = h1d end - if (not h2a) or (h2b and h2a > h2b) then h2a = h2b end - if (not h2a) or (h2c and h2a > h2c) then h2a = h2c end - if (not h2a) or (h2d and h2a > h2d) then h2a = h2d end - - h1 = h1a - h2 = h2a - - local dh1 = (h1 and -(h1 - ty1)) - local dh2 = (h2 and (h2 - ty1)) - - if dh2 and dh2 < by2 and dh2 > 0 then - --print("old", ty1, dh2, by2, h1, h2) - - if (dh1 and dh1 < -by1) then - -- crouch - this.crouching = true - ty1 = ty1 + 1 - else - -- climb - ty1 = h2 - by2 - local jdiff = jerky - ty1 - if math.abs(jdiff) > 0.1 then - this.jerkoffs = this.jerkoffs + jdiff - end - end - - --print("new", ty1, this.vy) - --if this.vy > 0 then this.vy = 0 end - end - end - - if MODE_DRUNKCAM_VELOCITY then - local xdiff = tx1-ox - local zdiff = tz1-oz - local dfac = math.sqrt(1.0-fwy*fwy) * 2.0 - xdiff = xdiff * dfac - zdiff = zdiff * dfac - this.drunkfx = this.drunkfx + (xdiff - this.drunkfx)*(1.0-math.exp(-5.0*sec_delta)) - this.drunkfz = this.drunkfz + (zdiff - this.drunkfz)*(1.0-math.exp(-5.0*sec_delta)) - xdiff = this.drunkfx - zdiff = this.drunkfz - this.sx = this.sx - (xdiff-this.drunkx)*20.0*sec_delta - this.sz = this.sz - (zdiff-this.drunkz)*20.0*sec_delta - this.drunkx = this.drunkx + (xdiff - this.drunkx)*(1.0-math.exp(-10.0*sec_delta)) - this.drunkz = this.drunkz + (zdiff - this.drunkz)*(1.0-math.exp(-10.0*sec_delta)) - end - this.x, this.y, this.z = tx1, ty1, tz1 - - local fgrounded = not box_is_clear( - tx1-0.39, ty1+by2, tz1-0.39, - tx1+0.39, ty1+by2+0.1, tz1+0.39) - - --print(fgrounded, tx1,ty1,tz1,by2) - - local wasgrounded = this.grounded - this.grounded = (MODE_AIRJUMP and this.grounded) or fgrounded - - if this.alive and this.vy > 0 and fgrounded then - this.vy = 0 - if client and not wasgrounded then - client.wav_play_global(wav_jump_down, this.x, this.y, this.z) - end - end - - -- trace for stuff - do - local td - local _ - - local camx,camy,camz - camx = this.x+0.4*math.sin(this.angy) - camy = this.y - camz = this.z+0.4*math.cos(this.angy) - - td, - this.blx1, this.bly1, this.blz1, - this.blx2, this.bly2, this.blz2 - = trace_map_ray_dist(camx,camy,camz, fwx,fwy,fwz, 5, false) - - - this.bld1 = td - this.bld2 = td - - _, - _, _, _, - this.blx3, this.bly3, this.blz3 - = trace_map_ray_dist(camx,camy,camz, fwx,fwy,fwz, 127.5) - end - - -- update gun - if this.wpn then this.wpn.tick(sec_current, sec_delta) end - end - - function this.camera_firstperson(sec_current, sec_delta) - -- set camera position - client.camera_move_to(this.x, this.y + this.jerkoffs, this.z) - - -- calc camera forward direction - local sya = math.sin(this.angy) - local cya = math.cos(this.angy) - local sxa = math.sin(this.angx) - local cxa = math.cos(this.angx) - local fwx,fwy,fwz - fwx,fwy,fwz = sya*cxa, sxa, cya*cxa - - -- drunkencam correction - this.sy = this.sy - MODE_DRUNKCAM_CORRECTSPEED*sec_delta - local ds = math.sqrt(this.sx*this.sx + this.sy*this.sy + this.sz*this.sz) - this.sx = this.sx / ds - this.sy = this.sy / ds - this.sz = this.sz / ds - - -- set camera direction - client.camera_point_sky(fwx, fwy, fwz, this.zoom, this.sx, this.sy, this.sz) - - -- offset by eye pos - -- slightly cheating here. - client.camera_move_global(sya*0.4, 0, cya*0.4) - end - - function this.render() - local ays,ayc,axs,axc - ays = math.sin(this.angy) - ayc = math.cos(this.angy) - axs = math.sin(this.angx) - axc = math.cos(this.angx) - - local mdl = nil - - local hand_x1 = -ayc*0.4 - local hand_y1 = 0.5 - local hand_z1 = ays*0.4 - - local hand_x2 = ayc*0.4 - local hand_y2 = 0.5 - local hand_z2 = -ays*0.4 - - local leg_x1 = -ayc*0.2 - local leg_y1 = 1.5 - local leg_z1 = ays*0.2 - - local leg_x2 = ayc*0.2 - local leg_y2 = 1.5 - local leg_z2 = -ays*0.2 - - if this.crouching then - -- TODO make this look less crap - leg_y1 = leg_y1 - 1 - leg_y2 = leg_y2 - 1 - end - - local swing = math.sin(rotpos/30*2) - *math.min(1.0, math.sqrt( - this.vx*this.vx - +this.vz*this.vz)/8.0) - *math.pi/4.0 - - local rax_right = (1-this.arm_rest_right)*(this.angx) - + this.arm_rest_right*(-swing+math.pi/2) - local rax_left = (1-this.arm_rest_left)*(this.angx) - + this.arm_rest_left*(swing+math.pi/2) - - local mdl_x = hand_x1+math.cos(rax_right)*ays*0.8 - local mdl_y = hand_y1+math.sin(rax_right)*0.8 - local mdl_z = hand_z1+math.cos(rax_right)*ayc*0.8 - if not this.alive then - -- do nothing -- - elseif this.tool == TOOL_SPADE then - client.model_render_bone_global(mdl_spade, mdl_spade_bone, - this.x+mdl_x, this.y+this.jerkoffs+mdl_y, this.z+mdl_z, - --0.0, -this.angx-math.pi/2*0.90, this.angy, 1) - 0.0, -this.angx, this.angy, 1) - elseif this.tool == TOOL_BLOCK then - if this.blocks > 0 then - client.model_render_bone_global(this.mdl_block, mdl_block_bone, - this.x+mdl_x, this.y+this.jerkoffs+mdl_y, this.z+mdl_z, - 0.0, -this.angx, this.angy, 1) - end - elseif this.tool == TOOL_GUN then - this.wpn.draw(this.x+mdl_x, this.y+this.jerkoffs+mdl_y, this.z+mdl_z, - math.pi/2, -this.angx, this.angy) - elseif this.tool == TOOL_NADE then - client.model_render_bone_global(mdl_nade, mdl_nade_bone, - this.x+mdl_x, this.y+this.jerkoffs+mdl_y, this.z+mdl_z, - 0.0, -this.angx, this.angy, 1.0) - end - - client.model_render_bone_global(this.mdl_player, mdl_player_arm, - this.x+hand_x1, this.y+this.jerkoffs+hand_y1, this.z+hand_z1, - 0.0, rax_right-math.pi/2, - this.angy-math.pi, 2.0) - client.model_render_bone_global(this.mdl_player, mdl_player_arm, - this.x+hand_x2, this.y+this.jerkoffs+hand_y2, this.z+hand_z2, - 0.0, rax_left-math.pi/2, - this.angy-math.pi, 2.0) - - client.model_render_bone_global(this.mdl_player, mdl_player_leg, - this.x+leg_x1, this.y+this.jerkoffs+leg_y1, this.z+leg_z1, - 0.0, swing, this.angy-math.pi, 2.2) - client.model_render_bone_global(this.mdl_player, mdl_player_leg, - this.x+leg_x2, this.y+this.jerkoffs+leg_y2, this.z+leg_z2, - 0.0, -swing, this.angy-math.pi, 2.2) - - client.model_render_bone_global(this.mdl_player, mdl_player_head, - this.x, this.y+this.jerkoffs, this.z, - 0.0, this.angx, this.angy-math.pi, 1) - - client.model_render_bone_global(this.mdl_player, mdl_player_body, - this.x, this.y+this.jerkoffs+0.8, this.z, - 0.0, 0.0, this.angy-math.pi, 1.5) - - if this.has_intel then - this.has_intel.render_backpack() - end - end - - --[[create static widgets for hud. - FIXME: share 1 instance across all players? (This makes ticking trickier) - ]] - function this.create_hud() - local scene = gui_create_scene(client.screen_get_dims()) - local root = scene.root - local w = root.width - local h = root.height - - -- tools - - this.tools_align = scene.display_object{x=root.l, y=root.t, visible=false} - local bone_wslot1 = scene.bone{model=mdl_spade, bone=mdl_spade_bone, - x=0.1*w*5/8} - local bone_wslot2 = scene.bone{model=this.mdl_block, bone=this.mdl_block_bone, - x=0.25*w*5/8} - local bone_wslot3 = scene.bone{model=this.wpn.get_model(), bone=0, - x=0.4*w*5/8} - local bone_wslot4 = scene.bone{model=mdl_nade, bone=mdl_nade_bone, - x=0.55*w*5/8} - scene.root.add_child(this.tools_align) - this.tools_align.add_child(bone_wslot1) - this.tools_align.add_child(bone_wslot2) - this.tools_align.add_child(bone_wslot3) - this.tools_align.add_child(bone_wslot4) - - local tool_mappings = {TOOL_SPADE,TOOL_BLOCK,TOOL_GUN,TOOL_NADE} - local tool_y = {0.3,0.25,0.25,0.25} - local tool_scale = {0.2,0.1,0.2,0.1} - local tool_pick_scale = {1.3,2.0,2.0,2.0} - local tool_textcolor = { - function() return 0xFFC0C0C0 end, - function() - local cr,cg,cb - cr,cg,cb = this.blk_color[1],this.blk_color[2],this.blk_color[3] - return (cr*256+cg)*256+cb+0xFF000000 - end, - function() - if this.wpn.ammo_clip == 0 then - return 0xFFFF3232 - else - return 0xFFC0C0C0 - end - end, - function() return 0xFFC0C0C0 end - } - local tool_textgen = { - function() return ""..this.blocks end, - function() return ""..this.blocks end, - function() return ""..this.wpn.ammo_clip.."-"..this.wpn.ammo_reserve end, - function() return ""..this.grenades end - } - local bounce = 0. -- picked tool bounce - - local bone_intel = scene.bone{model=mdl_intel, bone=mdl_intel_bone, - x=w*0.1,y=h*0.5,scale=0.18,visible=false} - scene.root.add_child(bone_intel) - - local function bone_rotate(dT) - for k,bone in pairs(this.tools_align.children) do - bone.rot_y = bone.rot_y + dT * 120 * 0.01 - bone.y = tool_y[k] - bone.scale = tool_scale[k] - if this.tool == tool_mappings[k] then - bone.y = bone.y + math.sin(bounce * 120 * 0.01) * 0.02 - bone.scale = bone.scale * tool_pick_scale[k] - end - bone.y = bone.y * h/2 - end - for k,bone in pairs({bone_intel}) do - bone.rot_y = bone.rot_y + dT * 120 * 0.01 - end - bounce = bounce + dT * 4 - bone_intel.visible = (this.has_intel ~= nil) - if this.has_intel then - bone_intel.model = this.has_intel.mdl_intel - end - end - this.tools_align.add_listener(GE_DELTA_TIME, bone_rotate) - - bone_rotate(0) - - --TODO: use the actual yes/no key mappings - this.quit_msg = scene.textfield{wordwrap=false, color=0xFFFF3232, font=font_large, - text="Are you sure? (Y/N)", x = w/2, y = h/4, align_x = 0.5, align_y = 0.5, - visible=false} - - this.reload_msg = scene.textfield{wordwrap=false, color=0xFFFF3232, font=font_large, - text="RELOAD", x = w/2, y = h/2+15, align_x = 0.5, align_y = 0, - visible=false} - - --TODO: update bluetext/greentext with the actual keys (if changed in controls.json) - this.team_change_msg_b = scene.textfield{wordwrap=false, color=0xFF0000FF, font=font_large, - text="Press 1 to join Blue", x = w/2, y = h/4, align_x = 0.5, align_y = 0.5} - this.team_change_msg_g = scene.textfield{wordwrap=false, color=0xFF00FF00, font=font_large, - text="Press 2 to join Green", x = w/2, y = h/4 + 40, align_x = 0.5, align_y = 0.5} - this.team_change = scene.display_object{visible=false} - - -- chat and killfeed - - this.chat_text = scene.textfield{font=font_mini, ctab={}, - align_x=0, align_y=1, x = 4, y = h - 90} - this.kill_text = scene.textfield{font=font_mini, ctab={}, - align_x=1, align_y=1, x = w - 4, y = h - 90} - - -- map (large_map and minimap) - - this.mini_map = scene.display_object{width=128, height=128, align_x = 1, align_y = 0, - x=w, y=0, use_img = false} - this.large_map = scene.display_object{x=w/2, y=h/2 - 24, visible=false, use_img = false} - - function this.large_map.update_size() - local ow, oh - ow, oh = common.img_get_dims(img_overview) - this.large_map.width = ow - this.large_map.height = oh - end - this.large_map.update_size() - - function this.map_gridname(x, y) - return string.char(65+math.floor(x/64))..(1+math.floor(y/64)) - end - - function this.print_map_location(x, y) - local s = "Location: "..this.map_gridname(this.x, this.z) - font_mini.print(x - font_mini.width*#s/2, y, 0xFFFFFFFF, s) - end - - function this.update_overview_icons(dT) - for i=1,#log_mspr,2 do - local u,v - u = log_mspr[i ] - v = log_mspr[i+1] - common.img_pixel_set(img_overview_icons, u, v, 0x00000000) - end - log_mspr = {} - - for j=1,players.max do - local plr = players[j] - if plr then - local x,y - x,y = plr.x, plr.z - local c - local drawit = true - if not plr.alive then - drawit = false - elseif plr == this then - c = 0xFF00FFFF - for i=0,10-1 do - local d=i/math.sqrt(2) - local u,v - u = math.floor(x)+math.floor(d*math.sin(plr.angy)) - v = math.floor(y)+math.floor(d*math.cos(plr.angy)) - log_mspr[#log_mspr+1] = u - log_mspr[#log_mspr+1] = v - common.img_pixel_set(img_overview_icons, u, v, c) - end - elseif plr.team == this.team then - c = 0xFFFFFFFF - else - c = 0xFFFF0000 - drawit = drawit and (this.t_rcirc ~= nil and - (MODE_MINIMAP_RCIRC or large_map)) - end - - if drawit then - for i=1,#mspr_player,2 do - local u,v - u = math.floor(x)+mspr_player[i ] - v = math.floor(y)+mspr_player[i+1] - log_mspr[#log_mspr+1] = u - log_mspr[#log_mspr+1] = v - common.img_pixel_set(img_overview_icons, u, v, c) - end - end - end - end - - for j=1,#intent do - local obj = intent[j] - - if obj.visible then - local x,y - x,y = obj.x, obj.z - local l = teams[obj.team].color_chat - local c = argb_split_to_merged(l[1],l[2],l[3]) - for i=1,#(obj.mspr),2 do - local u,v - u = math.floor(x)+obj.mspr[i ] - v = math.floor(y)+obj.mspr[i+1] - log_mspr[#log_mspr+1] = u - log_mspr[#log_mspr+1] = v - common.img_pixel_set(img_overview_icons, u, v, c) - end - end - end - end - - function this.large_map.draw_update() - this.large_map.update_size() - local mx, my - mx = this.large_map.l - my = this.large_map.t - client.img_blit(img_overview, mx, my) - client.img_blit(img_overview_grid, mx, my, - this.large_map.width, this.large_map.height, - 0, 0, 0x80FFFFFF) - client.img_blit(img_overview_icons, mx, my) - - local i - - for i=1,math.floor(this.large_map.height/64+0.5) do - font_mini.print(mx - 12, my + (i-0.5)*64, - 0xFFFFFFFF, ""..i) - font_mini.print(mx + this.large_map.width + 12-6, - my + (i-0.5)*64, - 0xFFFFFFFF, ""..i) - end - - for i=1,math.floor(this.large_map.width/64+0.5) do - font_mini.print(mx + (i-0.5)*64, my - 12, - 0xFFFFFFFF, ""..string.char(64+i)) - font_mini.print(mx + (i-0.5)*64, - my + this.large_map.height + 12-6, - 0xFFFFFFFF, ""..string.char(64+i)) - end - end - - local dt_samples = {} - local dt_max = 0 - - this.net_graph = scene.waveform{ - sample_sets={}, - width=200, - height=50, - x=w/4, - y=h-30 - } - - local function net_graph_update(delta_time) - -- the incoming dT is clamped, therefore we use delta_last instead - table.insert(dt_samples, delta_last) - dt_max = math.max(delta_last, dt_max) - if #dt_samples > this.net_graph.width then - table.remove(dt_samples, 1) - end - this.net_graph.push( - {{dt_samples,0xFF00FF00,0xFF008800,-dt_max,dt_max}}) - end - - function this.mini_map.draw_update() - if MODE_ENABLE_MINIMAP then - local mw, mh - mw, mh = this.mini_map.width, this.mini_map.height - - local left, top - left = this.mini_map.l - top = this.mini_map.t - - local qx, qy - for qy=-1,1 do - for qx=-1,1 do - - local view_left, view_top - view_left = this.x-mw/2+this.large_map.width*qx - view_top = this.z-mh/2+this.large_map.height*qy - - client.img_blit(img_overview, left, top, - mw, mh, - view_left, view_top, - 0xFFFFFFFF) - client.img_blit(img_overview_grid, left, top, - mw, mh, - view_left, view_top, - 0x80FFFFFF) - client.img_blit(img_overview_icons, left, top, - mw, mh, - view_left, view_top, - 0xFFFFFFFF) - end - end - this.print_map_location(this.mini_map.cx, this.mini_map.b + 2) - end - end - - function this.menus_visible() - return this.quit_msg.visible or this.team_change.visible - end - local function is_view_released() - return gui_focus ~= nil - end - - local function quit_events(options) - if options.state then - if this.quit_msg.visible then - if options.key == BTSK_YES then - -- TODO: clean up - client.hook_tick = nil - elseif options.key == BTSK_NO then - this.quit_msg.visible = false - end - elseif options.key == BTSK_QUIT and - not this.menus_visible() and - not is_view_released() then - this.quit_msg.visible = true - end - end - end - local function teamchange_events(options) - local viz = this.team_change.visible - if options.state and not is_view_released() then - if viz then - - local team - - if options.key == BTSK_TOOL1 then viz = false; team = 0 - elseif options.key == BTSK_TOOL2 then viz = false; team = 1 - elseif (options.key == BTSK_QUIT or options.key == BTSK_TEAM) - then viz = false - end - - local plr - plr = players[players.current] - if plr ~= nil and team ~= nil and team ~= plr.team then - common.net_send(nil, common.net_pack("Bbbz", 0x11, team, WPN_RIFLE, plr.name or "")) - end - - elseif options.key == BTSK_TEAM and not this.menus_visible() then - viz = true - end - end - this.team_change.visible = viz - end - local function toggle_map_state(options) - if options.state and options.key == BTSK_MAP and not is_view_released() then - this.mini_map.visible = not this.mini_map.visible - this.large_map.visible = not this.large_map.visible - end - end - local function feed_update(options) - this.chat_text.ctab = chat_text.render() - this.kill_text.ctab = chat_killfeed.render() - end - - this.crosshair = scene.image{img=img_crosshair, x=w/2, y=h/2} - this.cpal = scene.image{img=img_cpal, x=0, y=h, align_x=0, align_y=1} - this.cpal_rect = scene.image{img=img_cpal_rect, align_x=0, align_y=0} - - local function cpal_update(options) - this.cpal_rect.x = this.blk_color_x * 8 + this.cpal.l - this.cpal_rect.y = this.blk_color_y * 8 + this.cpal.t - end - - cpal_update() - - this.health_text = scene.textfield{font=font_digits, - text="100", - color=0xFFA1FFA1, - align_x=0.5, - align_y=0, - x = w/2, - y = h-48} - - local function health_update(options) - this.health_text.text = ""..this.health - end - - this.ammo_text = scene.textfield{font=font_digits, - text="", - color=0xFFC0C0C0, - align_x = 1, - align_y = 0, - x = w - 16, - y = h - 48} - - local function ammo_update(options) - local tool = this.tool + 1 - this.ammo_text.color = tool_textcolor[tool]() - this.ammo_text.text = tool_textgen[tool]() - end - - this.typing_type = scene.textfield{ - text="", - color=0xFFFFFFFF, - align_x = 0, - align_y = 0, - x = 0, - y = 0} - this.typing_text = scene.textfield{ - text="", - color=0xFFFFFFFF, - align_x = 0, - align_y = 0, - x = 0, - y = 0, - take_input = true} - this.typing_layout = scene.hspacer{x=4, y=h - 80, spread = 0, align_x=0, align_y=0} - this.typing_layout.add_child(this.typing_type) - this.typing_layout.add_child(this.typing_text) - this.typing_layout.visible = false - - function this.typing_text.done_typing(options) - this.typing_layout.visible = false - discard_typing_state() - end - - function this.typing_text.on_return(options) - - if this.typing_text.text ~= "" then - if string.sub(this.typing_text.text,1,1) == "~" then - loadstring(string.sub(this.typing_text.text,2))() --nasty, but handy - else - if this.typing_type.text == "Chat: " then - common.net_send(nil, common.net_pack("Bz", 0x0C, this.typing_text.text)) - elseif this.typing_type.text == "Team: " then - common.net_send(nil, common.net_pack("Bz", 0x0D, this.typing_text.text)) - elseif this.typing_type.text == "Squad: " then - common.net_send(nil, common.net_pack("Bz", 0x1E, this.typing_text.text)) - end - end - end - - this.typing_text.done_typing() - end - - -- spacer test - --[[local spacer = scene.hspacer{x=w/2,y=h/2,spread=8} - scene.root.add_child(spacer) - local boxes = {} - local i - for i=1, 10 do - local box = scene.tile9{ - width=20+math.random(50), - height=20+math.random(50), - tiles=img_tiles_roundrect - } - table.insert(boxes, box) - spacer.add_child(box) - end - spacer.reflow() - boxes[1].add_listener(GE_DELTA_TIME, function(dT) - for i=1, 10 do - boxes[i].width=20+math.random(50) - boxes[i].height=20+math.random(50) - end - spacer.reflow() - end)]] - - this.quit_msg.add_listener(GE_BUTTON, quit_events) - this.team_change.add_listener(GE_BUTTON, teamchange_events) - this.large_map.add_listener(GE_DELTA_TIME, this.update_overview_icons) - this.mini_map.add_listener(GE_BUTTON, toggle_map_state) - this.cpal_rect.add_listener(GE_DELTA_TIME, cpal_update) - this.chat_text.add_listener(GE_DELTA_TIME, feed_update) - this.health_text.add_listener(GE_DELTA_TIME, health_update) - this.ammo_text.add_listener(GE_DELTA_TIME, ammo_update) - this.net_graph.add_listener(GE_DELTA_TIME, net_graph_update) - - scene.root.add_child(this.crosshair) - scene.root.add_child(this.cpal) - scene.root.add_child(this.cpal_rect) - scene.root.add_child(this.mini_map) - scene.root.add_child(this.large_map) - scene.root.add_child(this.health_text) - scene.root.add_child(this.ammo_text) - scene.root.add_child(this.chat_text) - scene.root.add_child(this.kill_text) - scene.root.add_child(this.typing_layout) - scene.root.add_child(this.net_graph) - this.team_change.add_child(this.team_change_msg_b) - this.team_change.add_child(this.team_change_msg_g) - scene.root.add_child(this.team_change) - scene.root.add_child(this.quit_msg) - scene.root.add_child(this.reload_msg); - - this.scene = scene - end - - function this.on_mouse_button(button, state) - if this.tool == TOOL_GUN and this.alive then - this.wpn.click(button, state) - end - if button == 1 then - -- LMB - if state and not this.ev_lmb and this.tool == TOOL_GUN and this.alive and this.wpn.ammo_clip == 0 then - client.wav_play_global(wav_pin, this.x, this.y, this.z) - this.reload_msg.visible = true - this.reload_msg.static_alarm{name='reloadviz', - time=0.5, on_trigger=function() this.reload_msg.visible = false end} - end - this.ev_lmb = state - if this.ev_lmb then - this.ev_rmb = false - end - elseif button == 3 then - -- RMB - this.ev_rmb = state - if this.ev_rmb then - this.ev_lmb = false - end - elseif button == 4 then - -- mousewheelup - if state then - this.tool_switch_prev() - end - elseif button == 5 then - -- mousewheeldown - if state then - this.tool_switch_next() - end - elseif button == 2 then - -- middleclick - end - end - - function this.on_mouse_motion(x, y, dx, dy) - this.dangy = this.dangy - dx*math.pi*sensitivity/this.zoom - this.dangx = this.dangx + dy*math.pi*sensitivity/this.zoom - end - - function this.focus_typing(typing_type, default_text) - this.typing_type.text = typing_type - gui_focus = this.typing_text - this.typing_text.text = default_text - enter_typing_state() - this.typing_layout.reflow() - this.typing_layout.visible = true - end - - function this.on_key(key, state, modif) - if key == BTSK_FORWARD then - this.ev_forward = state - elseif key == BTSK_BACK then - this.ev_back = state - elseif key == BTSK_LEFT then - this.ev_left = state - elseif key == BTSK_RIGHT then - this.ev_right = state - elseif key == BTSK_CROUCH then - this.ev_crouch = state - elseif key == BTSK_JUMP then - this.ev_jump = state - elseif key == BTSK_SNEAK then - this.ev_sneak = state - elseif key == BTSK_SCORES then - show_scores = state - elseif state then - if key == BTSK_DEBUG then - debug_enabled = not debug_enabled - elseif key == SDLK_F10 then - local s = "clsave/"..common.base_dir.."/vol/lastsav.icemap" - print(s) - --client.map_load(s) - client.map_save(map_loaded, s, "icemap") - chat_add(chat_text, sec_last, "Map saved to "..s, 0xFFC00000) - elseif not this.menus_visible() then - if key == BTSK_RELOAD then - if this.alive and this.wpn and this.tool == TOOL_GUN then - this.wpn.reload() - end - elseif key == BTSK_TOOL1 then - this.tool_switch(TOOL_SPADE) - elseif key == BTSK_TOOL2 then - this.tool_switch(TOOL_BLOCK) - elseif key == BTSK_TOOL3 then - this.tool_switch(TOOL_GUN) - elseif key == BTSK_TOOL4 then - this.tool_switch(TOOL_NADE) - elseif key == BTSK_TOOL5 then - -- TODO - elseif key == BTSK_CHAT then - this.focus_typing("Chat: ", "") - elseif key == BTSK_COMMAND then - this.focus_typing("Chat: ", "/") - elseif key == BTSK_TEAMCHAT then - this.focus_typing("Team: ", "") - elseif key == BTSK_SQUADCHAT then - this.focus_typing("Squad: ", "") - elseif this.alive and key == BTSK_COLORLEFT then - this.blk_color_x = this.blk_color_x - 1 - if this.blk_color_x < 0 then - this.blk_color_x = 7 - end - this.blk_color = cpalette[this.blk_color_x+this.blk_color_y*8+1] - common.net_send(nil, common.net_pack("BBBBB", - 0x18, 0x00, - this.blk_color[1],this.blk_color[2],this.blk_color[3])) - elseif this.alive and key == BTSK_COLORRIGHT then - this.blk_color_x = this.blk_color_x + 1 - if this.blk_color_x > 7 then - this.blk_color_x = 0 - end - this.blk_color = cpalette[this.blk_color_x+this.blk_color_y*8+1] - common.net_send(nil, common.net_pack("BBBBB", - 0x18, 0x00, - this.blk_color[1],this.blk_color[2],this.blk_color[3])) - elseif this.alive and key == BTSK_COLORUP then - this.blk_color_y = this.blk_color_y - 1 - if this.blk_color_y < 0 then - this.blk_color_y = 7 - end - this.blk_color = cpalette[this.blk_color_x+this.blk_color_y*8+1] - common.net_send(nil, common.net_pack("BBBBB", - 0x18, 0x00, - this.blk_color[1],this.blk_color[2],this.blk_color[3])) - elseif this.alive and key == BTSK_COLORDOWN then - this.blk_color_y = this.blk_color_y + 1 - if this.blk_color_y > 7 then - this.blk_color_y = 0 - end - this.blk_color = cpalette[this.blk_color_x+this.blk_color_y*8+1] - common.net_send(nil, common.net_pack("BBBBB", - 0x18, 0x00, - this.blk_color[1],this.blk_color[2],this.blk_color[3])) - end - end - end - end - - function this.show_hud() - local fogr,fogg,fogb,fogd = client.map_fog_get() - - local ays,ayc,axs,axc - ays = math.sin(this.angy) - ayc = math.cos(this.angy) - axs = math.sin(this.angx) - axc = math.cos(this.angx) - - --font_mini.print(64,8,0xFFFFFFFF,mouse_prettyprint()) - - local w, h - local i, j - w, h = client.screen_get_dims() - - -- TODO: palettise this more nicely - prv_recolor_block(this.blk_color[1],this.blk_color[2],this.blk_color[3]) - - -- TODO: wireframe cube - if this.tool == TOOL_BLOCK and this.blx1 and (this.alive or this.respawning) then - if map_is_buildable(this.blx1, this.bly1, this.blz1) or MODE_BLOCK_PLACE_IN_AIR then - bname, mdl_data = client.model_bone_get(mdl_cube, mdl_cube_bone) - - mdl_data_backup = mdl_data - - for i=1,#mdl_data do - if mdl_data[i].r > 4 then - mdl_data[i].r = math.max(this.blk_color[1], 5) --going all the way down to - mdl_data[i].g = math.max(this.blk_color[2], 5) --to 4 breaks it and you'd - mdl_data[i].b = math.max(this.blk_color[3], 5) --have to reload the model - end - end - - client.model_bone_set(mdl_cube, mdl_cube_bone, bname, mdl_data) - - client.model_render_bone_global(mdl_cube, mdl_cube_bone, - this.blx1+0.5, this.bly1+0.5, this.blz1+0.5, - 0.0, 0.0, 0.0, 24.0) --no rotation, 24 roughly equals the cube size - - elseif not MODE_BLOCK_NO_RED_MARKER - client.model_render_bone_global(mdl_Xcube, mdl_Xcube_bone, - this.blx1+0.5, this.bly1+0.5, this.blz1+0.5, - 0.0, 0.0, 0.0, 24.0) - print(this.blx1.." "..this.bly1.." "..this.blz1) - end - elseif this.tool == TOOL_SPADE and this.blx1 and (this.alive or this.respawning) and map_block_get(this.blx2, this.bly2, this.blz2) then - client.model_render_bone_global(mdl_test, mdl_test_bone, - this.blx1+0.5, this.bly1+0.5, this.blz1+0.5, - rotpos*0.01, rotpos*0.004, 0.0, 0.1+0.01*math.sin(rotpos*0.071)) - client.model_render_bone_global(mdl_test, mdl_test_bone, - (this.blx1*2+this.blx2)/3+0.5, - (this.bly1*2+this.bly2)/3+0.5, - (this.blz1*2+this.blz2)/3+0.5, - -rotpos*0.01, -rotpos*0.004, 0.0, 0.1+0.01*math.sin(-rotpos*0.071)) - end - --[[ - client.model_render_bone_local(mdl_test, mdl_test_bone, - 1-0.2, 600/800-0.2, 1.0, - rotpos*0.01, rotpos*0.004, 0.0, 0.1) - ]] - - if not this.scene then - this.create_hud() - end - - this.render() - - if MODE_DEBUG_SHOWBOXES then - client.model_render_bone_global(mdl_bbox, - (this.crouching and mdl_bbox_bone2) or mdl_bbox_bone1, - this.x, this.y, this.z, 0, 0, 0.0, 1) - end - - for i=1,players.max do - local plr = players[i] - if plr and plr ~= this then - plr.render() - if plr.alive and plr.team == this.team then - local px,py - local dx,dy,dzNULL - dx,dy,dz = plr.x-this.x, - plr.y+plr.jerkoffs-this.y-this.jerkoffs-0.5, - plr.z-this.z - local d = dx*dx+dy*dy+dz*dz - d = math.sqrt(d) - dx,dy,dz = dx/d,dy/d,dz/d - dx,dy,dz = - (dx*ayc-dz*ays), - dy, - (dx*ays+dz*ayc) - dx,dy,dz = - dx, - (dy*axc-dz*axs), - (dy*axs+dz*axc) - - if dz > 0.001 then - local fatt = ((fogd*fogd - -((d*d < 0.001 and 0.001) or d*d)) - /(fogd*fogd)); - if fatt > 1.0 then fatt = 1.0 end - if fatt < 0.25 then fatt = 0.25 end - px = w/2-w/2*dx*this.zoom/dz - py = h/2+w/2*dy*this.zoom/dz - local c - if plr.squad and plr.squad == this.squad then - c = {255,255,255} - else - c = teams[this.team].color_chat - end - - local s_name = plr.name - if plr.squad then - s_name = s_name.." ["..plr.squad.."]" - end - - font_mini.print(px-(6*#s_name)/2,py-7 - ,argb_split_to_merged(c[1],c[2],c[3] - ,math.floor(fatt*255)) - ,s_name) - end - end - end - end - - for i=1,#intent do - local obj = intent[i] - if obj.visible then - obj.render() - end - end - - this.scene.draw() - - if debug_enabled then - local camx,camy,camz - camx,camy,camz = client.camera_get_pos() - local cam_pos_str = string.format("x: %f y: %f z: %f j: %f c: %i" - , camx, camy, camz, this.jerkoffs, (this.crouching and 1) or 0) - - font_mini.print(4, 4, 0x80FFFFFF, cam_pos_str) - end - - if show_scores then - local bi, gi - bi = 1 - gi = 1 - for i=1,players.max do - local plr = players_sorted[i] - if plr ~= nil then - local sn = plr.name - if plr.squad then - sn = sn.." ["..plr.squad.."]" - end - local s = sn.." #"..i..": " - ..plr.score.." ("..plr.kills.."/"..plr.deaths..")" - if plr.team == 1 then - font_mini.print(w / 2 + 50, gi * 15 + 150 - , argb_split_to_merged(150, 255, 150, 255) - , s) - gi = gi + 1 - else - font_mini.print(w / 2 - 50 - (6 * #s), bi * 15 + 150 - , argb_split_to_merged(150, 150, 255, 255) - , s) - bi = bi + 1 - end - end - end - end - - end - - return this -end +--[[ + This file is part of Ice Lua Components. + + Ice Lua Components is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Ice Lua Components is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Ice Lua Components. If not, see . +]] + +function new_player(settings) + local this = {} this.this = this this.this.this = this this = this.this + + this.team = settings.team or math.floor(math.random()*2) + this.squad = settings.squad or nil + this.weapon = settings.weapon or WPN_RIFLE + this.pid = settings.pid or error("pid must be set when creating player!") + this.alive = false + this.spawned = false + this.zooming = false + + this.mdl_block = common.model_new(1) + this.mdl_block = common.model_bone_new(this.mdl_block) + + this.mdl_player = common.model_new(4) + this.mdl_player = common.model_bone_new(this.mdl_player) + this.mdl_player = common.model_bone_new(this.mdl_player) + this.mdl_player = common.model_bone_new(this.mdl_player) + this.mdl_player = common.model_bone_new(this.mdl_player) + + this.score = 0 + this.kills = 0 + this.deaths = 0 + + local function prv_recolor_team(r,g,b) + if not client then return end + local mname,mdata + mname,mdata = common.model_bone_get(mdl_player, mdl_player_head) + recolor_component(r,g,b,mdata) + common.model_bone_set(this.mdl_player, mdl_player_head, mname, mdata) + mname,mdata = common.model_bone_get(mdl_player, mdl_player_body) + recolor_component(r,g,b,mdata) + common.model_bone_set(this.mdl_player, mdl_player_body, mname, mdata) + mname,mdata = common.model_bone_get(mdl_player, mdl_player_arm) + recolor_component(r,g,b,mdata) + common.model_bone_set(this.mdl_player, mdl_player_arm, mname, mdata) + mname,mdata = common.model_bone_get(mdl_player, mdl_player_leg) + recolor_component(r,g,b,mdata) + common.model_bone_set(this.mdl_player, mdl_player_leg, mname, mdata) + end + + function this.recolor_team() + local c = teams[this.team].color_mdl + local r,g,b + r,g,b = c[1],c[2],c[3] + prv_recolor_team(r,g,b) + end + + local function prv_recolor_block(r,g,b) + if not client then return end + local mname,mdata + mname,mdata = common.model_bone_get(mdl_block, mdl_block_bone) + recolor_component(r,g,b,mdata) + common.model_bone_set(this.mdl_block, mdl_block_bone, mname, mdata) + end + + prv_recolor_block(0,0,0) + do + local c = teams[this.team].color_mdl + local r,g,b + r,g,b = c[1],c[2],c[3] + prv_recolor_team(r,g,b) + end + + function this.block_recolor() + prv_recolor_block(this.blk_color[1],this.blk_color[2],this.blk_color[3]) + end + + function this.input_reset() + this.ev_forward = false + this.ev_back = false + this.ev_left = false + this.ev_right = false + + this.ev_jump = false + this.ev_crouch = false + this.ev_sneak = false + + this.ev_lmb = false + this.ev_rmb = false + end + + this.input_reset() + + function this.free() + if this.mdl_block then common.model_free(this.mdl_block) end + if this.mdl_player then common.model_free(this.mdl_player) end + end + + this.t_rcirc = nil + + function this.prespawn() + this.alive = false + this.spawned = false + + this.grounded = false + this.crouching = false + + this.arm_rest_right = 0.0 + this.arm_rest_left = 1.0 + + this.t_respawn = nil + this.t_switch = nil + this.t_nadeboom = nil + this.t_newnade = nil + this.t_newblock = nil + this.t_newspade1 = nil + this.t_newspade2 = nil + this.t_step = nil + + this.dangx, this.dangy = 0, 0 + this.vx, this.vy, this.vz = 0, 0, 0 + + this.blx1, this.bly1, this.blz1 = nil, nil, nil + this.blx2, this.bly2, this.blz2 = nil, nil, nil + + this.sx, this.sy, this.sz = 0, -1, 0 + this.drunkx, this.drunkz = 0, 0 + this.drunkfx, this.drunkfz = 0, 0 + + this.blk_color = {0x7F,0x7F,0x7F} + this.block_recolor() + this.blk_color_x = 3 + this.blk_color_y = 0 + + this.jerkoffs = 0.0 + + this.zoom = 1.0 + this.zooming = false + + this.health = 100 + this.blocks = 25 + this.grenades = 2 + + this.wpn = weapons[this.weapon](this) + + this.tool = 2 + + this.has_intel = nil + end + + local function prv_spawn_cont1() + this.prespawn() + + this.alive = true + this.spawned = true + this.t_switch = true + end + + function this.spawn_at(x,y,z,ya,xa) + this.x = x + this.y = y + this.z = z + this.angy = ya + this.angx = xa + + return prv_spawn_cont1() + end + + function this.spawn() + local xlen,ylen,zlen + xlen,ylen,zlen = common.map_get_dims() + + while true do + this.x = math.floor(math.random()*xlen/4.0)+0.5 + this.z = math.floor(math.random()*zlen)+0.5 + if this.team == 1 then this.x = xlen - this.x end + this.y = (common.map_pillar_get(this.x, this.z))[1+1] + if this.y < ylen-1 then break end + end + this.y = this.y - 3.0 + this.angy, this.angx = math.pi/2.0, 0.0 + if this.team == 1 then this.angy = this.angy-math.pi end + + return prv_spawn_cont1() + end + + this.name = settings.name or "Noob" + if server then + this.spawn() + else + this.prespawn() + end + + function this.tool_switch(tool) + if not this.alive then return end + + if this.tool == TOOL_GUN then + if this.wpn then + this.wpn.firing = false + this.wpn.reloading = false + end + this.zooming = false + this.arm_rest_right = 0 + end + this.t_switch = true + if client and this == players[players.current] and this.tool ~= tool then + common.net_send(nil, common.net_pack("BBB" + , 0x17, 0x00, tool)) + end + this.tool = tool + this.ev_lmb = false + this.ev_rmb = false + + -- hud + if this.tools_align then + this.tools_align.visible = true + this.tools_align.static_alarm{name='viz', + time=3.0, on_trigger=function() this.tools_align.visible = false end} + end + + end + + function this.tool_switch_next() + new_tool = (this.tool + 1) % (TOOL_NADE + 1) -- Nade is last weapon + this.tool_switch(new_tool) + end + + function this.tool_switch_prev() + new_tool = (this.tool - 1) % (TOOL_NADE + 1) -- Nade is last weapon + this.tool_switch(new_tool) + end + + --[[ + keys are: + 0x01: up + 0x02: down + 0x04: left + 0x08: right + 0x10: sneak | scope + 0x20: crouch + 0x40: jump + 0x80: * RESERVED * + ]] + + function this.get_pos() + return this.x, this.y, this.z + end + + function this.set_pos_recv(x, y, z) + this.x = x + this.y = y + this.z = z + end + + function this.get_orient() + local keys = 0 + if this.ev_forward then keys = keys + 0x01 end + if this.ev_back then keys = keys + 0x02 end + if this.ev_left then keys = keys + 0x04 end + if this.ev_right then keys = keys + 0x08 end + if this.ev_sneak or this.zooming then keys = keys + 0x10 end + if this.ev_crouch then keys = keys + 0x20 end + if this.ev_jump then keys = keys + 0x40 end + --if this.ev_aimbot then keys = keys + 0x80 end + + return this.angy, this.angx, keys + end + + function this.set_orient_recv(ya, xa, keys) + this.angy = ya + this.angx = xa + + this.ev_forward = bit_and(keys,0x01) ~= 0 + this.ev_back = bit_and(keys,0x02) ~= 0 + this.ev_left = bit_and(keys,0x04) ~= 0 + this.ev_right = bit_and(keys,0x08) ~= 0 + this.ev_sneak = bit_and(keys,0x10) ~= 0 + this.ev_crouch = bit_and(keys,0x20) ~= 0 + this.ev_jump = bit_and(keys,0x40) ~= 0 + --this.ev_aimbot = bit_and(keys,0x80) ~= 0 + end + + function this.recoil(sec_current, recoil_y, recoil_x) + local xrec = recoil_x*math.cos(sec_current*math.pi*2)*math.pi*20 + local ydip = math.sin(this.angx) + local ycos = math.cos(this.angx) + local yrec = recoil_y + ydip + local ydist = math.sqrt(ycos*ycos+yrec*yrec) + this.angy = this.angy + xrec + this.angx = math.asin(yrec/ydist) + end + + function this.update_score() + net_broadcast(nil, common.net_pack("BBBBhhhzz", + 0x05, this.pid, + this.team, this.weapon, + this.score, this.kills, this.deaths, + this.name, this.squad)) + sort_players() + end + + function this.tent_restock() + this.health = 100 + this.blocks = 100 + this.grenades = 4 + if this.wpn then + this.wpn.ammo_clip = this.wpn.cfg.ammo_clip + this.wpn.ammo_reserve = this.wpn.cfg.ammo_reserve + end + if server then + net_broadcast(nil, common.net_pack("BB", 0x15, this.pid)) + end + end + + function this.set_health_damage(amt, kcol, kmsg, enemy) + this.health = amt + + if this.health <= 0 and this.alive then + if server then + this.intel_drop() + this.deaths = this.deaths + 1 + if enemy == nil then + -- do nothing -- + elseif enemy == this then + enemy.score = enemy.score + SCORE_SUICIDE + elseif enemy.team == this.team then + enemy.score = enemy.score + SCORE_TEAMKILL + else + enemy.score = enemy.score + SCORE_KILL + enemy.kills = enemy.kills + 1 + end + if enemy ~= nil and enemy ~= this then + enemy.update_score() + end + this.update_score() + net_broadcast(nil, common.net_pack("BIz", 0x0F, kcol, kmsg)) + end + --chat_add(chat_killfeed, nil, kmsg, kcol) + this.health = 0 + this.alive = false + end + + if server then + net_broadcast(nil, common.net_pack("BBB", 0x14, this.pid, this.health)) + end + end + + function this.damage(amt, kcol, kmsg, enemy) + return this.set_health_damage( + this.health - amt, kcol, kmsg, enemy) + end + + function this.fall_damage(amt) + --print("damage",this.name,part,amt) + local l = teams[this.team].color_chat + r,g,b = l[1],l[2],l[3] + + local c = argb_split_to_merged(r,g,b) + + local kmsg = this.name.." found a high place" + this.damage(amt, c, kmsg, this) + end + + function this.gun_damage(part, amt, enemy) + --print("damage",this.name,part,amt) + + if not server then + return + end + + local midmsg = " killed " + if this.team == enemy.team then + midmsg = " teamkilled " + end + + local r,g,b + r,g,b = 0,0,0 + + local l = teams[enemy.team].color_chat + r,g,b = l[1],l[2],l[3] + + local c = argb_split_to_merged(r,g,b) + + local kmsg = enemy.name..midmsg..this.name + this.damage(amt, c, kmsg, enemy) + end + + function this.spade_damage(part, amt, enemy) + --print("damage",this.name,part,amt) + + if not server then + return + end + + local midmsg = " spaded " + if this.team == enemy.team then + error("THIS SHOULD NEVER HAPPEN WORST PYSPADES BUG EVER") + end + + local r,g,b + r,g,b = 0,0,0 + + local l = teams[enemy.team].color_chat + r,g,b = l[1],l[2],l[3] + + local c = argb_split_to_merged(r,g,b) + + local kmsg = enemy.name..midmsg..this.name + this.damage(amt, c, kmsg, enemy) + end + + function this.grenade_damage(amt, enemy) + --print("damage",this.name,part,amt) + local midmsg = " grenaded " + if this.team == enemy.team and this ~= enemy then + error("THIS SHOULD NEVER HAPPEN") + end + + local r,g,b + r,g,b = 0,0,0 + + local l = teams[enemy.team].color_chat + r,g,b = l[1],l[2],l[3] + + local c = argb_split_to_merged(r,g,b) + + local kmsg = enemy.name..midmsg..this.name + if enemy == this then + kmsg = this.name.." exploded" + end + + this.damage(amt, c, kmsg, enemy) + end + + function this.intel_pickup(intel) + if this.has_intel or intel.team == this.team then + return false + end + + if server then + local x,y,z,f + x,y,z = intel.get_pos() + intel.visible = false + f = intel.get_flags() + net_broadcast(nil, common.net_pack("BHhhhB", 0x12, intel.iid, x,y,z,f)) + net_broadcast(nil, common.net_pack("BHB", 0x16, intel.iid, this.pid)) + local s = "* "..this.name.." has picked up the "..teams[intel.team].name.." intel." + net_broadcast(nil, common.net_pack("BIz", 0x0E, 0xFF800000, s)) + this.has_intel = intel + end + + return true + end + + function this.intel_drop() + if server then + local intel = this.has_intel + --print("dropped", intel) + if not intel then + return + end + + intel.intel_drop() + this.has_intel = nil + + local s = "* "..this.name.." has dropped the "..teams[intel.team].name.." intel." + net_broadcast(nil, common.net_pack("BIz", 0x0E, 0xFF800000, s)) + end + end + + function this.intel_capture(sec_current) + if server then + local intel = this.has_intel + if not intel then + return + end + + intel.intel_capture(sec_current) + this.has_intel = nil + + local s = "* "..this.name.." has captured the "..teams[intel.team].name.." intel." + net_broadcast(nil, common.net_pack("BIz", 0x0E, 0xFF800000, s)) + net_broadcast_team(this.team, common.net_pack("B", 0x1C)) + end + end + + function this.throw_nade(sec_current) + local sya = math.sin(this.angy) + local cya = math.cos(this.angy) + local sxa = math.sin(this.angx) + local cxa = math.cos(this.angx) + local fwx,fwy,fwz + fwx,fwy,fwz = sya*cxa, sxa, cya*cxa + + local n = new_nade({ + x = this.x, + y = this.y, + z = this.z, + vx = fwx*MODE_NADE_SPEED*MODE_NADE_STEP+this.vx*MODE_NADE_STEP, + vy = fwy*MODE_NADE_SPEED*MODE_NADE_STEP+this.vy*MODE_NADE_STEP, + vz = fwz*MODE_NADE_SPEED*MODE_NADE_STEP+this.vz*MODE_NADE_STEP, + fuse = math.max(0, this.t_nadeboom - sec_current) + }) + nade_add(n) + common.net_send(nil, common.net_pack("BhhhhhhH", + 0x1B, + math.floor(n.x*32+0.5), + math.floor(n.y*32+0.5), + math.floor(n.z*32+0.5), + math.floor(n.vx*256+0.5), + math.floor(n.vy*256+0.5), + math.floor(n.vz*256+0.5), + math.floor(n.fuse*100+0.5))) + end + + function this.tick_listeners(sec_current, sec_delta) + if this.scene then + this.scene.pump_listeners(sec_delta, input_events) + end + end + + function this.tick(sec_current, sec_delta) + local xlen,ylen,zlen + xlen,ylen,zlen = common.map_get_dims() + + if not this.spawned then + return + end + + if (not this.alive) and (not this.t_respawn) then + this.t_respawn = sec_current + MODE_RESPAWN_TIME + this.input_reset() + this.wpn.firing = false + this.wpn.reloading = false + this.zooming = false + end + + if this.t_respawn then + if server and this.t_respawn <= sec_current then + --print("server respawn!") + this.t_respawn = nil + this.spawn() + net_broadcast(nil, common.net_pack("BBfffBB", + 0x10, this.pid, + this.x, this.y, this.z, + this.angy*128/math.pi, this.angx*256/math.pi)) + else + -- any last requests? + end + end + + if not this.alive then + this.input_reset() + end + + local inwater = (this.y > ylen-3) + + if this.t_switch == true then + this.t_switch = sec_current + MODE_DELAY_TOOL_CHANGE + end + + if this.t_rcirc and sec_current >= this.t_rcirc then + this.t_rcirc = nil + end + + if this.alive and this.t_switch then + if sec_current > this.t_switch then + this.t_switch = nil + this.arm_rest_right = 0 + else + local delta = this.t_switch-sec_current + this.arm_rest_right = math.max(0.0,delta/0.2) + end + end + + if this.t_newblock and sec_current >= this.t_newblock then + this.t_newblock = nil + end + + if this.t_newspade1 and sec_current >= this.t_newspade1 then + this.t_newspade1 = nil + end + + if this.t_newnade and sec_current >= this.t_newnade then + this.t_newnade = nil + end + + if this.t_nadeboom then + if (not this.ev_lmb) or sec_current >= this.t_nadeboom then + this.throw_nade(sec_current) + this.t_newnade = sec_current + MODE_DELAY_NADE_THROW + this.t_nadeboom = nil + this.ev_lmb = false + end + end + + if not this.ev_rmb then + this.t_newspade2 = nil + end + + if this.t_newspade2 and sec_current >= this.t_newspade2 and this.blx2 then + if this.blx2 >= 0 and this.blx2 < xlen and this.blz2 >= 0 and this.blz2 < zlen then + if this.bly2-1 <= ylen-3 then + common.net_send(nil, common.net_pack("BHHH", + 0x0A, + this.blx2, this.bly2, this.blz2)) + end + end + + this.t_newspade2 = nil + end + + if client then + local moving = (this.ev_left or this.ev_right or this.ev_forward or this.ev_back) + local sneaking = (this.ev_crouch or this.ev_sneak or this.zooming) + + if moving and not sneaking then + if not this.t_step then + this.t_step = sec_current + 0.5 + end + if this.t_step < sec_current then + local freq_mod = (inwater and 0.25) or 1.0 + local tdiff = 0.01 + if this.grounded then + client.wav_play_global(wav_steps[ + math.floor(math.random()*#wav_steps)+1], + this.x, this.y, this.z, + 1.0, freq_mod) + tdiff = 0.5 + end + this.t_step = this.t_step + tdiff + if this.t_step < sec_current then + this.t_step = sec_current + tdiff + end + end + else + this.t_step = nil + end + + if this.wpn.reloading then + this.reload_msg.visible = false + end + end + + -- calc X delta angle + local nax = this.angx + this.dangx + if nax > math.pi*0.49 then + nax = math.pi*0.49 + elseif nax < -math.pi*0.49 then + nax = -math.pi*0.49 + end + this.dangx = (nax - this.angx) + + -- apply delta angles + if MODE_DRUNKCAM_LOCALTURN and this.dangy ~= 0 then + this.angx = this.angx + this.dangx + + local fx,fy,fz -- forward + local sx,sy,sz -- sky + local ax,ay,az -- horiz side + local bx,by,bz -- vert side + + local sya = math.sin(this.angy) + local cya = math.cos(this.angy) + local sxa = math.sin(this.angx) + local cxa = math.cos(this.angx) + + -- get vectors + fx,fy,fz = vnorm(sya*cxa, sxa, cya*cxa) + sx,sy,sz = vnorm(this.sx, this.sy, this.sz) + ax,ay,az = vnorm(vcross(fx,fy,fz,sx,sy,sz)) + bx,by,bz = vnorm(vcross(fx,fy,fz,ax,ay,az)) + + + -- rotate forward and sky + + fx,fy,fz = vrotate(this.dangy,fx,fy,fz,bx,by,bz) + sx,sy,sz = vrotate(this.dangy,sx,sy,sz,bx,by,bz) + + -- normalise F and S + fx,fy,fz = vnorm(fx,fy,fz) + sx,sy,sz = vnorm(sx,sy,sz) + + -- stash sky arrow + this.sx = sx + this.sy = sy + this.sz = sz + + -- convert forward from vector to polar + this.angx = math.asin(fy) + local langx = this.angx + + if math.cos(langx) <= 0.0 then + fx = -fx + fz = -fz + end + + this.angy = math.atan2(fx,fz) + + --print("polar",this.angx, this.angy) + + else + this.angx = this.angx + this.dangx + this.angy = this.angy + this.dangy + end + this.dangx = 0 + this.dangy = 0 + + if this.zooming then + this.zoom = 3.0 + else + this.zoom = 1.0 + end + + -- set camera direction + local sya = math.sin(this.angy) + local cya = math.cos(this.angy) + local sxa = math.sin(this.angx) + local cxa = math.cos(this.angx) + local fwx,fwy,fwz + fwx,fwy,fwz = sya*cxa, sxa, cya*cxa + + if client and this.alive and (not this.t_switch) then + if this.ev_lmb then + if this.tool == TOOL_BLOCK and this.blx1 then + if (not this.t_newblock) and this.blocks > 0 then + if this.blx1 >= 0 and this.blx1 < xlen and this.blz1 >= 0 and this.blz1 < zlen then + if this.bly1 <= ylen-3 and map_is_buildable(this.blx1, this.bly1, this.blz1) then + common.net_send(nil, common.net_pack("BHHHBBBB", + 0x08, + this.blx1, this.bly1, this.blz1, + this.blk_color[3], + this.blk_color[2], + this.blk_color[1], + 1)) + this.blocks = this.blocks - 1 + this.t_newblock = sec_current + MODE_DELAY_BLOCK_BUILD + this.t_switch = this.t_newblock + end + end + end + elseif this.tool == TOOL_SPADE then + if (not this.t_newspade1) then + + -- see if there's anyone we can kill + local d = this.bld2 or 5 -- NOTE: cannot spade through walls anymore. Sorry guys :/ + local hurt_idx = nil + local hurt_part = nil + local hurt_part_idx = 0 + local hurt_dist = d*d + local i,j + + for i=1,players.max do + local p = players[i] + if p and p ~= this and p.alive and p.team ~= this.team then + local dx = p.x-this.x + local dy = p.y-this.y+0.1 + local dz = p.z-this.z + + for j=1,3 do + local dd = dx*dx+dy*dy+dz*dz + + local dotk = dx*fwx+dy*fwy+dz*fwz + local dot = math.sqrt(dd-dotk*dotk) + if dot < 0.55 and dd < hurt_dist then + hurt_idx = i + hurt_dist = dd + hurt_part_idx = j + hurt_part = ({"head","body","legs"})[j] + + break + end + dy = dy + 1.0 + end + end + end + + if hurt_idx then + if server then + players[hurt_idx].spade_damage( + hurt_part, 1000, this) + else + common.net_send(nil, common.net_pack("BBB" + , 0x13, hurt_idx, hurt_part_idx)) + end + elseif this.blx2 then + if this.blx2 >= 0 and this.blx2 < xlen and this.blz2 >= 0 and this.blz2 < zlen then + if this.bly2 <= ylen-3 then + bhealth_damage(this.blx2, this.bly2, this.blz2, MODE_BLOCK_DAMAGE_SPADE) + this.t_newspade1 = sec_current + MODE_DELAY_SPADE_HIT + end + end + end + + end + elseif this.tool == TOOL_NADE then + if (not this.t_newnade) and this.grenades > 0 then + if (not this.t_nadeboom) then + this.grenades = this.grenades - 1 + this.t_nadeboom = sec_current + MODE_NADE_FUSE + end + end + else + + end + elseif this.ev_rmb then + if this.tool == TOOL_BLOCK and this.blx3 and this.alive then + local ct,cr,cg,cb + ct,cr,cg,cb = map_block_pick(this.blx3, this.bly3, this.blz3) + if ct ~= nil then + this.blk_color = {cr,cg,cb} + common.net_send(nil, common.net_pack("BBBBB", + 0x18, 0x00, + this.blk_color[1],this.blk_color[2],this.blk_color[3])) + end + this.ev_rmb = false + elseif this.tool == TOOL_SPADE and this.blx2 and this.alive then + if (not this.t_newspade2) then + this.t_newspade2 = sec_current + + MODE_DELAY_SPADE_DIG + end + end + end + end + + -- move along + local mvx = 0.0 + local mvy = 0.0 + local mvz = 0.0 + + if this.ev_forward then + mvz = mvz + 1.0 + end + if this.ev_back then + mvz = mvz - 1.0 + end + if this.ev_left then + mvx = mvx + 1.0 + end + if this.ev_right then + mvx = mvx - 1.0 + end + + if this.ev_crouch then + if this.grounded and not this.crouching then + if MODE_SOFTCROUCH then this.jerkoffs = this.jerkoffs - 1 end + this.y = this.y + 1 + end + this.crouching = true + end + if this.ev_jump and this.alive and (MODE_CHEAT_FLY or this.grounded) then + this.vy = -7 + this.ev_jump = false + if client then + client.wav_play_global(wav_jump_up, this.x, this.y, this.z) + end + end + + -- normalise mvx,mvz + local mvd = math.max(0.00001,math.sqrt(mvx*mvx + mvz*mvz)) + mvx = mvx / mvd + mvz = mvz / mvd + + -- apply base slowdown + local mvspd = 8.0 + local mvchange = 10.0 + mvx = mvx * mvspd + mvz = mvz * mvspd + + -- apply extra slowdowns + if not this.grounded then + mvx = mvx * 0.6 + mvz = mvz * 0.6 + mvchange = mvchange * 0.3 + end + if inwater then + mvx = mvx * 0.6 + mvz = mvz * 0.6 + end + if this.crouching then + mvx = mvx * 0.5 + mvz = mvz * 0.5 + end + if this.zooming or this.ev_sneak then + mvx = mvx * 0.5 + mvz = mvz * 0.5 + end + + -- apply rotation + mvx, mvz = mvx*cya+mvz*sya, mvz*cya-mvx*sya + + this.vx = this.vx + (mvx - this.vx)*(1.0-math.exp(-sec_delta*mvchange)) + this.vz = this.vz + (mvz - this.vz)*(1.0-math.exp(-sec_delta*mvchange)) + this.vy = this.vy + 2*9.81*sec_delta + + local ox, oy, oz + local nx, ny, nz + local tx1,ty1,tz1 + ox, oy, oz = this.x, this.y, this.z + this.x, this.y, this.z = this.x + this.vx*sec_delta, this.y + this.vy*sec_delta, this.z + this.vz*sec_delta + nx, ny, nz = this.x, this.y, this.z + this.jerkoffs = this.jerkoffs * math.exp(-sec_delta*15.0) + + local by1, by2 + by1, by2 = -0.3, 2.5 + if this.crouching then + if (not this.ev_crouch) and box_is_clear( + ox-0.39, oy-0.8, oz-0.39, + ox+0.39, oy-0.3, oz+0.39) then + this.crouching = false + oy = oy - 1 + if this.grounded then + ny = ny - 1 + if MODE_SOFTCROUCH then this.jerkoffs = this.jerkoffs + 1 end + end + end + end + if this.crouching or MODE_AUTOCLIMB then + by2 = by2 - 1 + if MODE_AUTOCLIMB then + by2 = by2 - 0.01 + end + end + + if this.alive then + tx1,ty1,tz1 = trace_map_box( + ox, oy, oz, + nx, ny, nz, + -0.4, by1, -0.4, + 0.4, by2, 0.4, + false) + else + tx1,ty1,tz1 = nx,ny,nz + end + + if this.alive and MODE_AUTOCLIMB and not this.crouching then + by2 = by2 + 1.01 + end + + if this.alive and MODE_AUTOCLIMB and not this.crouching then + local jerky = ty1 + + local h1a,h1b,h1c,h1d + local h2a,h2b,h2c,h2d + local h1,h2,_ + _,h2 = trace_gap(tx1,ty1+1.0,tz1) + h1a,h2a = trace_gap(tx1-0.39,ty1+1.0,tz1-0.39) + h1b,h2b = trace_gap(tx1+0.39,ty1+1.0,tz1-0.39) + h1c,h2c = trace_gap(tx1-0.39,ty1+1.0,tz1+0.39) + h1d,h2d = trace_gap(tx1+0.39,ty1+1.0,tz1+0.39) + + if (not h1a) or (h1b and h1a < h1b) then h1a = h1b end + if (not h1a) or (h1c and h1a < h1c) then h1a = h1c end + if (not h1a) or (h1d and h1a < h1d) then h1a = h1d end + if (not h2a) or (h2b and h2a > h2b) then h2a = h2b end + if (not h2a) or (h2c and h2a > h2c) then h2a = h2c end + if (not h2a) or (h2d and h2a > h2d) then h2a = h2d end + + h1 = h1a + h2 = h2a + + local dh1 = (h1 and -(h1 - ty1)) + local dh2 = (h2 and (h2 - ty1)) + + if dh2 and dh2 < by2 and dh2 > 0 then + --print("old", ty1, dh2, by2, h1, h2) + + if (dh1 and dh1 < -by1) then + -- crouch + this.crouching = true + ty1 = ty1 + 1 + else + -- climb + ty1 = h2 - by2 + local jdiff = jerky - ty1 + if math.abs(jdiff) > 0.1 then + this.jerkoffs = this.jerkoffs + jdiff + end + end + + --print("new", ty1, this.vy) + --if this.vy > 0 then this.vy = 0 end + end + end + + if MODE_DRUNKCAM_VELOCITY then + local xdiff = tx1-ox + local zdiff = tz1-oz + local dfac = math.sqrt(1.0-fwy*fwy) * 2.0 + xdiff = xdiff * dfac + zdiff = zdiff * dfac + this.drunkfx = this.drunkfx + (xdiff - this.drunkfx)*(1.0-math.exp(-5.0*sec_delta)) + this.drunkfz = this.drunkfz + (zdiff - this.drunkfz)*(1.0-math.exp(-5.0*sec_delta)) + xdiff = this.drunkfx + zdiff = this.drunkfz + this.sx = this.sx - (xdiff-this.drunkx)*20.0*sec_delta + this.sz = this.sz - (zdiff-this.drunkz)*20.0*sec_delta + this.drunkx = this.drunkx + (xdiff - this.drunkx)*(1.0-math.exp(-10.0*sec_delta)) + this.drunkz = this.drunkz + (zdiff - this.drunkz)*(1.0-math.exp(-10.0*sec_delta)) + end + this.x, this.y, this.z = tx1, ty1, tz1 + + local fgrounded = not box_is_clear( + tx1-0.39, ty1+by2, tz1-0.39, + tx1+0.39, ty1+by2+0.1, tz1+0.39) + + --print(fgrounded, tx1,ty1,tz1,by2) + + local wasgrounded = this.grounded + this.grounded = (MODE_AIRJUMP and this.grounded) or fgrounded + + if this.alive and this.vy > 0 and fgrounded then + this.vy = 0 + if client and not wasgrounded then + client.wav_play_global(wav_jump_down, this.x, this.y, this.z) + end + end + + -- trace for stuff + do + local td + local _ + + local camx,camy,camz + camx = this.x+0.4*math.sin(this.angy) + camy = this.y + camz = this.z+0.4*math.cos(this.angy) + + td, + this.blx1, this.bly1, this.blz1, + this.blx2, this.bly2, this.blz2 + = trace_map_ray_dist(camx,camy,camz, fwx,fwy,fwz, 5, false) + + + this.bld1 = td + this.bld2 = td + + _, + _, _, _, + this.blx3, this.bly3, this.blz3 + = trace_map_ray_dist(camx,camy,camz, fwx,fwy,fwz, 127.5) + end + + -- update gun + if this.wpn then this.wpn.tick(sec_current, sec_delta) end + end + + function this.camera_firstperson(sec_current, sec_delta) + -- set camera position + client.camera_move_to(this.x, this.y + this.jerkoffs, this.z) + + -- calc camera forward direction + local sya = math.sin(this.angy) + local cya = math.cos(this.angy) + local sxa = math.sin(this.angx) + local cxa = math.cos(this.angx) + local fwx,fwy,fwz + fwx,fwy,fwz = sya*cxa, sxa, cya*cxa + + -- drunkencam correction + this.sy = this.sy - MODE_DRUNKCAM_CORRECTSPEED*sec_delta + local ds = math.sqrt(this.sx*this.sx + this.sy*this.sy + this.sz*this.sz) + this.sx = this.sx / ds + this.sy = this.sy / ds + this.sz = this.sz / ds + + -- set camera direction + client.camera_point_sky(fwx, fwy, fwz, this.zoom, this.sx, this.sy, this.sz) + + -- offset by eye pos + -- slightly cheating here. + client.camera_move_global(sya*0.4, 0, cya*0.4) + end + + function this.render() + local ays,ayc,axs,axc + ays = math.sin(this.angy) + ayc = math.cos(this.angy) + axs = math.sin(this.angx) + axc = math.cos(this.angx) + + local mdl = nil + + local hand_x1 = -ayc*0.4 + local hand_y1 = 0.5 + local hand_z1 = ays*0.4 + + local hand_x2 = ayc*0.4 + local hand_y2 = 0.5 + local hand_z2 = -ays*0.4 + + local leg_x1 = -ayc*0.2 + local leg_y1 = 1.5 + local leg_z1 = ays*0.2 + + local leg_x2 = ayc*0.2 + local leg_y2 = 1.5 + local leg_z2 = -ays*0.2 + + if this.crouching then + -- TODO make this look less crap + leg_y1 = leg_y1 - 1 + leg_y2 = leg_y2 - 1 + end + + local swing = math.sin(rotpos/30*2) + *math.min(1.0, math.sqrt( + this.vx*this.vx + +this.vz*this.vz)/8.0) + *math.pi/4.0 + + local rax_right = (1-this.arm_rest_right)*(this.angx) + + this.arm_rest_right*(-swing+math.pi/2) + local rax_left = (1-this.arm_rest_left)*(this.angx) + + this.arm_rest_left*(swing+math.pi/2) + + local mdl_x = hand_x1+math.cos(rax_right)*ays*0.8 + local mdl_y = hand_y1+math.sin(rax_right)*0.8 + local mdl_z = hand_z1+math.cos(rax_right)*ayc*0.8 + if not this.alive then + -- do nothing -- + elseif this.tool == TOOL_SPADE then + client.model_render_bone_global(mdl_spade, mdl_spade_bone, + this.x+mdl_x, this.y+this.jerkoffs+mdl_y, this.z+mdl_z, + --0.0, -this.angx-math.pi/2*0.90, this.angy, 1) + 0.0, -this.angx, this.angy, 1) + elseif this.tool == TOOL_BLOCK then + if this.blocks > 0 then + client.model_render_bone_global(this.mdl_block, mdl_block_bone, + this.x+mdl_x, this.y+this.jerkoffs+mdl_y, this.z+mdl_z, + 0.0, -this.angx, this.angy, 1) + end + elseif this.tool == TOOL_GUN then + this.wpn.draw(this.x+mdl_x, this.y+this.jerkoffs+mdl_y, this.z+mdl_z, + math.pi/2, -this.angx, this.angy) + elseif this.tool == TOOL_NADE then + client.model_render_bone_global(mdl_nade, mdl_nade_bone, + this.x+mdl_x, this.y+this.jerkoffs+mdl_y, this.z+mdl_z, + 0.0, -this.angx, this.angy, 1.0) + end + + client.model_render_bone_global(this.mdl_player, mdl_player_arm, + this.x+hand_x1, this.y+this.jerkoffs+hand_y1, this.z+hand_z1, + 0.0, rax_right-math.pi/2, + this.angy-math.pi, 2.0) + client.model_render_bone_global(this.mdl_player, mdl_player_arm, + this.x+hand_x2, this.y+this.jerkoffs+hand_y2, this.z+hand_z2, + 0.0, rax_left-math.pi/2, + this.angy-math.pi, 2.0) + + client.model_render_bone_global(this.mdl_player, mdl_player_leg, + this.x+leg_x1, this.y+this.jerkoffs+leg_y1, this.z+leg_z1, + 0.0, swing, this.angy-math.pi, 2.2) + client.model_render_bone_global(this.mdl_player, mdl_player_leg, + this.x+leg_x2, this.y+this.jerkoffs+leg_y2, this.z+leg_z2, + 0.0, -swing, this.angy-math.pi, 2.2) + + client.model_render_bone_global(this.mdl_player, mdl_player_head, + this.x, this.y+this.jerkoffs, this.z, + 0.0, this.angx, this.angy-math.pi, 1) + + client.model_render_bone_global(this.mdl_player, mdl_player_body, + this.x, this.y+this.jerkoffs+0.8, this.z, + 0.0, 0.0, this.angy-math.pi, 1.5) + + if this.has_intel then + this.has_intel.render_backpack() + end + end + + --[[create static widgets for hud. + FIXME: share 1 instance across all players? (This makes ticking trickier) + ]] + function this.create_hud() + local scene = gui_create_scene(client.screen_get_dims()) + local root = scene.root + local w = root.width + local h = root.height + + -- tools + + this.tools_align = scene.display_object{x=root.l, y=root.t, visible=false} + local bone_wslot1 = scene.bone{model=mdl_spade, bone=mdl_spade_bone, + x=0.1*w*5/8} + local bone_wslot2 = scene.bone{model=this.mdl_block, bone=this.mdl_block_bone, + x=0.25*w*5/8} + local bone_wslot3 = scene.bone{model=this.wpn.get_model(), bone=0, + x=0.4*w*5/8} + local bone_wslot4 = scene.bone{model=mdl_nade, bone=mdl_nade_bone, + x=0.55*w*5/8} + scene.root.add_child(this.tools_align) + this.tools_align.add_child(bone_wslot1) + this.tools_align.add_child(bone_wslot2) + this.tools_align.add_child(bone_wslot3) + this.tools_align.add_child(bone_wslot4) + + local tool_mappings = {TOOL_SPADE,TOOL_BLOCK,TOOL_GUN,TOOL_NADE} + local tool_y = {0.3,0.25,0.25,0.25} + local tool_scale = {0.2,0.1,0.2,0.1} + local tool_pick_scale = {1.3,2.0,2.0,2.0} + local tool_textcolor = { + function() return 0xFFC0C0C0 end, + function() + local cr,cg,cb + cr,cg,cb = this.blk_color[1],this.blk_color[2],this.blk_color[3] + return (cr*256+cg)*256+cb+0xFF000000 + end, + function() + if this.wpn.ammo_clip == 0 then + return 0xFFFF3232 + else + return 0xFFC0C0C0 + end + end, + function() return 0xFFC0C0C0 end + } + local tool_textgen = { + function() return ""..this.blocks end, + function() return ""..this.blocks end, + function() return ""..this.wpn.ammo_clip.."-"..this.wpn.ammo_reserve end, + function() return ""..this.grenades end + } + local bounce = 0. -- picked tool bounce + + local bone_intel = scene.bone{model=mdl_intel, bone=mdl_intel_bone, + x=w*0.1,y=h*0.5,scale=0.18,visible=false} + scene.root.add_child(bone_intel) + + local function bone_rotate(dT) + for k,bone in pairs(this.tools_align.children) do + bone.rot_y = bone.rot_y + dT * 120 * 0.01 + bone.y = tool_y[k] + bone.scale = tool_scale[k] + if this.tool == tool_mappings[k] then + bone.y = bone.y + math.sin(bounce * 120 * 0.01) * 0.02 + bone.scale = bone.scale * tool_pick_scale[k] + end + bone.y = bone.y * h/2 + end + for k,bone in pairs({bone_intel}) do + bone.rot_y = bone.rot_y + dT * 120 * 0.01 + end + bounce = bounce + dT * 4 + bone_intel.visible = (this.has_intel ~= nil) + if this.has_intel then + bone_intel.model = this.has_intel.mdl_intel + end + end + this.tools_align.add_listener(GE_DELTA_TIME, bone_rotate) + + bone_rotate(0) + + --TODO: use the actual yes/no key mappings + this.quit_msg = scene.textfield{wordwrap=false, color=0xFFFF3232, font=font_large, + text="Are you sure? (Y/N)", x = w/2, y = h/4, align_x = 0.5, align_y = 0.5, + visible=false} + + this.reload_msg = scene.textfield{wordwrap=false, color=0xFFFF3232, font=font_large, + text="RELOAD", x = w/2, y = h/2+15, align_x = 0.5, align_y = 0, + visible=false} + + --TODO: update bluetext/greentext with the actual keys (if changed in controls.json) + this.team_change_msg_b = scene.textfield{wordwrap=false, color=0xFF0000FF, font=font_large, + text="Press 1 to join Blue", x = w/2, y = h/4, align_x = 0.5, align_y = 0.5} + this.team_change_msg_g = scene.textfield{wordwrap=false, color=0xFF00FF00, font=font_large, + text="Press 2 to join Green", x = w/2, y = h/4 + 40, align_x = 0.5, align_y = 0.5} + this.team_change = scene.display_object{visible=false} + + -- chat and killfeed + + this.chat_text = scene.textfield{font=font_mini, ctab={}, + align_x=0, align_y=1, x = 4, y = h - 90} + this.kill_text = scene.textfield{font=font_mini, ctab={}, + align_x=1, align_y=1, x = w - 4, y = h - 90} + + -- map (large_map and minimap) + + this.mini_map = scene.display_object{width=128, height=128, align_x = 1, align_y = 0, + x=w, y=0, use_img = false} + this.large_map = scene.display_object{x=w/2, y=h/2 - 24, visible=false, use_img = false} + + function this.large_map.update_size() + local ow, oh + ow, oh = common.img_get_dims(img_overview) + this.large_map.width = ow + this.large_map.height = oh + end + this.large_map.update_size() + + function this.map_gridname(x, y) + return string.char(65+math.floor(x/64))..(1+math.floor(y/64)) + end + + function this.print_map_location(x, y) + local s = "Location: "..this.map_gridname(this.x, this.z) + font_mini.print(x - font_mini.width*#s/2, y, 0xFFFFFFFF, s) + end + + function this.update_overview_icons(dT) + for i=1,#log_mspr,2 do + local u,v + u = log_mspr[i ] + v = log_mspr[i+1] + common.img_pixel_set(img_overview_icons, u, v, 0x00000000) + end + log_mspr = {} + + for j=1,players.max do + local plr = players[j] + if plr then + local x,y + x,y = plr.x, plr.z + local c + local drawit = true + if not plr.alive then + drawit = false + elseif plr == this then + c = 0xFF00FFFF + for i=0,10-1 do + local d=i/math.sqrt(2) + local u,v + u = math.floor(x)+math.floor(d*math.sin(plr.angy)) + v = math.floor(y)+math.floor(d*math.cos(plr.angy)) + log_mspr[#log_mspr+1] = u + log_mspr[#log_mspr+1] = v + common.img_pixel_set(img_overview_icons, u, v, c) + end + elseif plr.team == this.team then + c = 0xFFFFFFFF + else + c = 0xFFFF0000 + drawit = drawit and (this.t_rcirc ~= nil and + (MODE_MINIMAP_RCIRC or large_map)) + end + + if drawit then + for i=1,#mspr_player,2 do + local u,v + u = math.floor(x)+mspr_player[i ] + v = math.floor(y)+mspr_player[i+1] + log_mspr[#log_mspr+1] = u + log_mspr[#log_mspr+1] = v + common.img_pixel_set(img_overview_icons, u, v, c) + end + end + end + end + + for j=1,#intent do + local obj = intent[j] + + if obj.visible then + local x,y + x,y = obj.x, obj.z + local l = teams[obj.team].color_chat + local c = argb_split_to_merged(l[1],l[2],l[3]) + for i=1,#(obj.mspr),2 do + local u,v + u = math.floor(x)+obj.mspr[i ] + v = math.floor(y)+obj.mspr[i+1] + log_mspr[#log_mspr+1] = u + log_mspr[#log_mspr+1] = v + common.img_pixel_set(img_overview_icons, u, v, c) + end + end + end + end + + function this.large_map.draw_update() + this.large_map.update_size() + local mx, my + mx = this.large_map.l + my = this.large_map.t + client.img_blit(img_overview, mx, my) + client.img_blit(img_overview_grid, mx, my, + this.large_map.width, this.large_map.height, + 0, 0, 0x80FFFFFF) + client.img_blit(img_overview_icons, mx, my) + + local i + + for i=1,math.floor(this.large_map.height/64+0.5) do + font_mini.print(mx - 12, my + (i-0.5)*64, + 0xFFFFFFFF, ""..i) + font_mini.print(mx + this.large_map.width + 12-6, + my + (i-0.5)*64, + 0xFFFFFFFF, ""..i) + end + + for i=1,math.floor(this.large_map.width/64+0.5) do + font_mini.print(mx + (i-0.5)*64, my - 12, + 0xFFFFFFFF, ""..string.char(64+i)) + font_mini.print(mx + (i-0.5)*64, + my + this.large_map.height + 12-6, + 0xFFFFFFFF, ""..string.char(64+i)) + end + end + + local dt_samples = {} + local dt_max = 0 + + this.net_graph = scene.waveform{ + sample_sets={}, + width=200, + height=50, + x=w/4, + y=h-30 + } + + local function net_graph_update(delta_time) + -- the incoming dT is clamped, therefore we use delta_last instead + table.insert(dt_samples, delta_last) + dt_max = math.max(delta_last, dt_max) + if #dt_samples > this.net_graph.width then + table.remove(dt_samples, 1) + end + this.net_graph.push( + {{dt_samples,0xFF00FF00,0xFF008800,-dt_max,dt_max}}) + end + + function this.mini_map.draw_update() + if MODE_ENABLE_MINIMAP then + local mw, mh + mw, mh = this.mini_map.width, this.mini_map.height + + local left, top + left = this.mini_map.l + top = this.mini_map.t + + local qx, qy + for qy=-1,1 do + for qx=-1,1 do + + local view_left, view_top + view_left = this.x-mw/2+this.large_map.width*qx + view_top = this.z-mh/2+this.large_map.height*qy + + client.img_blit(img_overview, left, top, + mw, mh, + view_left, view_top, + 0xFFFFFFFF) + client.img_blit(img_overview_grid, left, top, + mw, mh, + view_left, view_top, + 0x80FFFFFF) + client.img_blit(img_overview_icons, left, top, + mw, mh, + view_left, view_top, + 0xFFFFFFFF) + end + end + this.print_map_location(this.mini_map.cx, this.mini_map.b + 2) + end + end + + function this.menus_visible() + return this.quit_msg.visible or this.team_change.visible + end + local function is_view_released() + return gui_focus ~= nil + end + + local function quit_events(options) + if options.state then + if this.quit_msg.visible then + if options.key == BTSK_YES then + -- TODO: clean up + client.hook_tick = nil + elseif options.key == BTSK_NO then + this.quit_msg.visible = false + end + elseif options.key == BTSK_QUIT and + not this.menus_visible() and + not is_view_released() then + this.quit_msg.visible = true + end + end + end + local function teamchange_events(options) + local viz = this.team_change.visible + if options.state and not is_view_released() then + if viz then + + local team + + if options.key == BTSK_TOOL1 then viz = false; team = 0 + elseif options.key == BTSK_TOOL2 then viz = false; team = 1 + elseif (options.key == BTSK_QUIT or options.key == BTSK_TEAM) + then viz = false + end + + local plr + plr = players[players.current] + if plr ~= nil and team ~= nil and team ~= plr.team then + common.net_send(nil, common.net_pack("Bbbz", 0x11, team, WPN_RIFLE, plr.name or "")) + end + + elseif options.key == BTSK_TEAM and not this.menus_visible() then + viz = true + end + end + this.team_change.visible = viz + end + local function toggle_map_state(options) + if options.state and options.key == BTSK_MAP and not is_view_released() then + this.mini_map.visible = not this.mini_map.visible + this.large_map.visible = not this.large_map.visible + end + end + local function feed_update(options) + this.chat_text.ctab = chat_text.render() + this.kill_text.ctab = chat_killfeed.render() + end + + this.crosshair = scene.image{img=img_crosshair, x=w/2, y=h/2} + this.cpal = scene.image{img=img_cpal, x=0, y=h, align_x=0, align_y=1} + this.cpal_rect = scene.image{img=img_cpal_rect, align_x=0, align_y=0} + + local function cpal_update(options) + this.cpal_rect.x = this.blk_color_x * 8 + this.cpal.l + this.cpal_rect.y = this.blk_color_y * 8 + this.cpal.t + end + + cpal_update() + + this.health_text = scene.textfield{font=font_digits, + text="100", + color=0xFFA1FFA1, + align_x=0.5, + align_y=0, + x = w/2, + y = h-48} + + local function health_update(options) + this.health_text.text = ""..this.health + end + + this.ammo_text = scene.textfield{font=font_digits, + text="", + color=0xFFC0C0C0, + align_x = 1, + align_y = 0, + x = w - 16, + y = h - 48} + + local function ammo_update(options) + local tool = this.tool + 1 + this.ammo_text.color = tool_textcolor[tool]() + this.ammo_text.text = tool_textgen[tool]() + end + + this.typing_type = scene.textfield{ + text="", + color=0xFFFFFFFF, + align_x = 0, + align_y = 0, + x = 0, + y = 0} + this.typing_text = scene.textfield{ + text="", + color=0xFFFFFFFF, + align_x = 0, + align_y = 0, + x = 0, + y = 0, + take_input = true} + this.typing_layout = scene.hspacer{x=4, y=h - 80, spread = 0, align_x=0, align_y=0} + this.typing_layout.add_child(this.typing_type) + this.typing_layout.add_child(this.typing_text) + this.typing_layout.visible = false + + function this.typing_text.done_typing(options) + this.typing_layout.visible = false + discard_typing_state() + end + + function this.typing_text.on_return(options) + + if this.typing_text.text ~= "" then + if string.sub(this.typing_text.text,1,1) == "~" then + loadstring(string.sub(this.typing_text.text,2))() --nasty, but handy + else + if this.typing_type.text == "Chat: " then + common.net_send(nil, common.net_pack("Bz", 0x0C, this.typing_text.text)) + elseif this.typing_type.text == "Team: " then + common.net_send(nil, common.net_pack("Bz", 0x0D, this.typing_text.text)) + elseif this.typing_type.text == "Squad: " then + common.net_send(nil, common.net_pack("Bz", 0x1E, this.typing_text.text)) + end + end + end + + this.typing_text.done_typing() + end + + -- spacer test + --[[local spacer = scene.hspacer{x=w/2,y=h/2,spread=8} + scene.root.add_child(spacer) + local boxes = {} + local i + for i=1, 10 do + local box = scene.tile9{ + width=20+math.random(50), + height=20+math.random(50), + tiles=img_tiles_roundrect + } + table.insert(boxes, box) + spacer.add_child(box) + end + spacer.reflow() + boxes[1].add_listener(GE_DELTA_TIME, function(dT) + for i=1, 10 do + boxes[i].width=20+math.random(50) + boxes[i].height=20+math.random(50) + end + spacer.reflow() + end)]] + + this.quit_msg.add_listener(GE_BUTTON, quit_events) + this.team_change.add_listener(GE_BUTTON, teamchange_events) + this.large_map.add_listener(GE_DELTA_TIME, this.update_overview_icons) + this.mini_map.add_listener(GE_BUTTON, toggle_map_state) + this.cpal_rect.add_listener(GE_DELTA_TIME, cpal_update) + this.chat_text.add_listener(GE_DELTA_TIME, feed_update) + this.health_text.add_listener(GE_DELTA_TIME, health_update) + this.ammo_text.add_listener(GE_DELTA_TIME, ammo_update) + this.net_graph.add_listener(GE_DELTA_TIME, net_graph_update) + + scene.root.add_child(this.crosshair) + scene.root.add_child(this.cpal) + scene.root.add_child(this.cpal_rect) + scene.root.add_child(this.mini_map) + scene.root.add_child(this.large_map) + scene.root.add_child(this.health_text) + scene.root.add_child(this.ammo_text) + scene.root.add_child(this.chat_text) + scene.root.add_child(this.kill_text) + scene.root.add_child(this.typing_layout) + scene.root.add_child(this.net_graph) + this.team_change.add_child(this.team_change_msg_b) + this.team_change.add_child(this.team_change_msg_g) + scene.root.add_child(this.team_change) + scene.root.add_child(this.quit_msg) + scene.root.add_child(this.reload_msg); + + this.scene = scene + end + + function this.on_mouse_button(button, state) + if this.tool == TOOL_GUN and this.alive then + this.wpn.click(button, state) + end + if button == 1 then + -- LMB + if state and not this.ev_lmb and this.tool == TOOL_GUN and this.alive and this.wpn.ammo_clip == 0 then + client.wav_play_global(wav_pin, this.x, this.y, this.z) + this.reload_msg.visible = true + this.reload_msg.static_alarm{name='reloadviz', + time=0.5, on_trigger=function() this.reload_msg.visible = false end} + end + this.ev_lmb = state + if this.ev_lmb then + this.ev_rmb = false + end + elseif button == 3 then + -- RMB + this.ev_rmb = state + if this.ev_rmb then + this.ev_lmb = false + end + elseif button == 4 then + -- mousewheelup + if state then + this.tool_switch_prev() + end + elseif button == 5 then + -- mousewheeldown + if state then + this.tool_switch_next() + end + elseif button == 2 then + -- middleclick + end + end + + function this.on_mouse_motion(x, y, dx, dy) + this.dangy = this.dangy - dx*math.pi*sensitivity/this.zoom + this.dangx = this.dangx + dy*math.pi*sensitivity/this.zoom + end + + function this.focus_typing(typing_type, default_text) + this.typing_type.text = typing_type + gui_focus = this.typing_text + this.typing_text.text = default_text + enter_typing_state() + this.typing_layout.reflow() + this.typing_layout.visible = true + end + + function this.on_key(key, state, modif) + if key == BTSK_FORWARD then + this.ev_forward = state + elseif key == BTSK_BACK then + this.ev_back = state + elseif key == BTSK_LEFT then + this.ev_left = state + elseif key == BTSK_RIGHT then + this.ev_right = state + elseif key == BTSK_CROUCH then + this.ev_crouch = state + elseif key == BTSK_JUMP then + this.ev_jump = state + elseif key == BTSK_SNEAK then + this.ev_sneak = state + elseif key == BTSK_SCORES then + show_scores = state + elseif state then + if key == BTSK_DEBUG then + debug_enabled = not debug_enabled + elseif key == SDLK_F10 then + local s = "clsave/"..common.base_dir.."/vol/lastsav.icemap" + print(s) + --client.map_load(s) + client.map_save(map_loaded, s, "icemap") + chat_add(chat_text, sec_last, "Map saved to "..s, 0xFFC00000) + elseif not this.menus_visible() then + if key == BTSK_RELOAD then + if this.alive and this.wpn and this.tool == TOOL_GUN then + this.wpn.reload() + end + elseif key == BTSK_TOOL1 then + this.tool_switch(TOOL_SPADE) + elseif key == BTSK_TOOL2 then + this.tool_switch(TOOL_BLOCK) + elseif key == BTSK_TOOL3 then + this.tool_switch(TOOL_GUN) + elseif key == BTSK_TOOL4 then + this.tool_switch(TOOL_NADE) + elseif key == BTSK_TOOL5 then + -- TODO + elseif key == BTSK_CHAT then + this.focus_typing("Chat: ", "") + elseif key == BTSK_COMMAND then + this.focus_typing("Chat: ", "/") + elseif key == BTSK_TEAMCHAT then + this.focus_typing("Team: ", "") + elseif key == BTSK_SQUADCHAT then + this.focus_typing("Squad: ", "") + elseif this.alive and key == BTSK_COLORLEFT then + this.blk_color_x = this.blk_color_x - 1 + if this.blk_color_x < 0 then + this.blk_color_x = 7 + end + this.blk_color = cpalette[this.blk_color_x+this.blk_color_y*8+1] + common.net_send(nil, common.net_pack("BBBBB", + 0x18, 0x00, + this.blk_color[1],this.blk_color[2],this.blk_color[3])) + elseif this.alive and key == BTSK_COLORRIGHT then + this.blk_color_x = this.blk_color_x + 1 + if this.blk_color_x > 7 then + this.blk_color_x = 0 + end + this.blk_color = cpalette[this.blk_color_x+this.blk_color_y*8+1] + common.net_send(nil, common.net_pack("BBBBB", + 0x18, 0x00, + this.blk_color[1],this.blk_color[2],this.blk_color[3])) + elseif this.alive and key == BTSK_COLORUP then + this.blk_color_y = this.blk_color_y - 1 + if this.blk_color_y < 0 then + this.blk_color_y = 7 + end + this.blk_color = cpalette[this.blk_color_x+this.blk_color_y*8+1] + common.net_send(nil, common.net_pack("BBBBB", + 0x18, 0x00, + this.blk_color[1],this.blk_color[2],this.blk_color[3])) + elseif this.alive and key == BTSK_COLORDOWN then + this.blk_color_y = this.blk_color_y + 1 + if this.blk_color_y > 7 then + this.blk_color_y = 0 + end + this.blk_color = cpalette[this.blk_color_x+this.blk_color_y*8+1] + common.net_send(nil, common.net_pack("BBBBB", + 0x18, 0x00, + this.blk_color[1],this.blk_color[2],this.blk_color[3])) + end + end + end + end + + function this.show_hud() + local fogr,fogg,fogb,fogd = client.map_fog_get() + + local ays,ayc,axs,axc + ays = math.sin(this.angy) + ayc = math.cos(this.angy) + axs = math.sin(this.angx) + axc = math.cos(this.angx) + + --font_mini.print(64,8,0xFFFFFFFF,mouse_prettyprint()) + + local w, h + local i, j + w, h = client.screen_get_dims() + + -- TODO: palettise this more nicely + prv_recolor_block(this.blk_color[1],this.blk_color[2],this.blk_color[3]) + + -- TODO: wireframe cube + if this.tool == TOOL_BLOCK and this.blx1 and (this.alive or this.respawning) then + if map_is_buildable(this.blx1, this.bly1, this.blz1) or MODE_BLOCK_PLACE_IN_AIR then + bname, mdl_data = client.model_bone_get(mdl_cube, mdl_cube_bone) + + mdl_data_backup = mdl_data + + for i=1,#mdl_data do + if mdl_data[i].r > 4 then + mdl_data[i].r = math.max(this.blk_color[1], 5) --going all the way down to + mdl_data[i].g = math.max(this.blk_color[2], 5) --to 4 breaks it and you'd + mdl_data[i].b = math.max(this.blk_color[3], 5) --have to reload the model + end + end + + client.model_bone_set(mdl_cube, mdl_cube_bone, bname, mdl_data) + + client.model_render_bone_global(mdl_cube, mdl_cube_bone, + this.blx1+0.5, this.bly1+0.5, this.blz1+0.5, + 0.0, 0.0, 0.0, 24.0) --no rotation, 24 roughly equals the cube size + + elseif not MODE_BLOCK_NO_RED_MARKER then + client.model_render_bone_global(mdl_Xcube, mdl_Xcube_bone, + this.blx1+0.5, this.bly1+0.5, this.blz1+0.5, + 0.0, 0.0, 0.0, 24.0) + print(this.blx1.." "..this.bly1.." "..this.blz1) + end + elseif this.tool == TOOL_SPADE and this.blx1 and (this.alive or this.respawning) and map_block_get(this.blx2, this.bly2, this.blz2) then + client.model_render_bone_global(mdl_test, mdl_test_bone, + this.blx1+0.5, this.bly1+0.5, this.blz1+0.5, + rotpos*0.01, rotpos*0.004, 0.0, 0.1+0.01*math.sin(rotpos*0.071)) + client.model_render_bone_global(mdl_test, mdl_test_bone, + (this.blx1*2+this.blx2)/3+0.5, + (this.bly1*2+this.bly2)/3+0.5, + (this.blz1*2+this.blz2)/3+0.5, + -rotpos*0.01, -rotpos*0.004, 0.0, 0.1+0.01*math.sin(-rotpos*0.071)) + end + --[[ + client.model_render_bone_local(mdl_test, mdl_test_bone, + 1-0.2, 600/800-0.2, 1.0, + rotpos*0.01, rotpos*0.004, 0.0, 0.1) + ]] + + if not this.scene then + this.create_hud() + end + + this.render() + + if MODE_DEBUG_SHOWBOXES then + client.model_render_bone_global(mdl_bbox, + (this.crouching and mdl_bbox_bone2) or mdl_bbox_bone1, + this.x, this.y, this.z, 0, 0, 0.0, 1) + end + + for i=1,players.max do + local plr = players[i] + if plr and plr ~= this then + plr.render() + if plr.alive and plr.team == this.team then + local px,py + local dx,dy,dzNULL + dx,dy,dz = plr.x-this.x, + plr.y+plr.jerkoffs-this.y-this.jerkoffs-0.5, + plr.z-this.z + local d = dx*dx+dy*dy+dz*dz + d = math.sqrt(d) + dx,dy,dz = dx/d,dy/d,dz/d + dx,dy,dz = + (dx*ayc-dz*ays), + dy, + (dx*ays+dz*ayc) + dx,dy,dz = + dx, + (dy*axc-dz*axs), + (dy*axs+dz*axc) + + if dz > 0.001 then + local fatt = ((fogd*fogd + -((d*d < 0.001 and 0.001) or d*d)) + /(fogd*fogd)); + if fatt > 1.0 then fatt = 1.0 end + if fatt < 0.25 then fatt = 0.25 end + px = w/2-w/2*dx*this.zoom/dz + py = h/2+w/2*dy*this.zoom/dz + local c + if plr.squad and plr.squad == this.squad then + c = {255,255,255} + else + c = teams[this.team].color_chat + end + + local s_name = plr.name + if plr.squad then + s_name = s_name.." ["..plr.squad.."]" + end + + font_mini.print(px-(6*#s_name)/2,py-7 + ,argb_split_to_merged(c[1],c[2],c[3] + ,math.floor(fatt*255)) + ,s_name) + end + end + end + end + + for i=1,#intent do + local obj = intent[i] + if obj.visible then + obj.render() + end + end + + this.scene.draw() + + if debug_enabled then + local camx,camy,camz + camx,camy,camz = client.camera_get_pos() + local cam_pos_str = string.format("x: %f y: %f z: %f j: %f c: %i" + , camx, camy, camz, this.jerkoffs, (this.crouching and 1) or 0) + + font_mini.print(4, 4, 0x80FFFFFF, cam_pos_str) + end + + if show_scores then + local bi, gi + bi = 1 + gi = 1 + for i=1,players.max do + local plr = players_sorted[i] + if plr ~= nil then + local sn = plr.name + if plr.squad then + sn = sn.." ["..plr.squad.."]" + end + local s = sn.." #"..i..": " + ..plr.score.." ("..plr.kills.."/"..plr.deaths..")" + if plr.team == 1 then + font_mini.print(w / 2 + 50, gi * 15 + 150 + , argb_split_to_merged(150, 255, 150, 255) + , s) + gi = gi + 1 + else + font_mini.print(w / 2 - 50 - (6 * #s), bi * 15 + 150 + , argb_split_to_merged(150, 150, 255, 255) + , s) + bi = bi + 1 + end + end + end + end + + end + + return this +end diff --git a/pkg/base/pmf/Xcube.pmf b/pkg/base/pmf/Xcube.pmf index 07a82e0..b00d327 100644 Binary files a/pkg/base/pmf/Xcube.pmf and b/pkg/base/pmf/Xcube.pmf differ