im doing the CS50 game development course and after creating the key and locked block required when i get close to the locked block i get an error saying GameObject.lua 28: attempted to perform arithmetic on field 'y' (nil value)
here is my code for level maker
LevelMaker = Class{}
keyCollected = false
function LevelMaker.generate(width, height)
local tiles = {}
local entities = {}
local objects = {}
local tileID = TILE_ID_GROUND
-- whether we should draw our tiles with toppers
local topper = true
local tileset = math.random(20)
local topperset = math.random(20)
-- insert blank tables into tiles for later access
for x = 1, height do
table.insert(tiles, {})
end
--generate one locked box and one locked key in a random position position
local lockBoxPosition = math.random(1, width)
local keyPosition = math.random(1, width)
local keyColor = math.random(1, 4) --for reference for the color skin used
-- column by column generation instead of row; sometimes better for platformers
for x = 1, width do
local tileID = TILE_ID_EMPTY
-- lay out the empty space
for y = 1, 6 do
table.insert(tiles[y],
Tile(x, y, tileID, nil, tileset, topperset))
end
-- chance to just be emptiness (to be sure to not generate a key and locked box above a chasm)
if math.random(7) == 1 and x ~= 1 and lockBoxPosition ~= x and keyPosition ~= x then
for y = 7, height do
table.insert(tiles[y],
Tile(x, y, tileID, nil, tileset, topperset))
end
else
tileID = TILE_ID_GROUND
-- height at which we would spawn a potential jump block
local blockHeight = 4
for y = 7, height do
table.insert(tiles[y],
Tile(x, y, tileID, y == 7 and topper or nil, tileset, topperset))
end
-- chance to generate a pillar
if math.random(8) == 1 then
blockHeight = 2
-- chance to generate bush on pillar
if math.random(8) == 1 then
table.insert(objects,
GameObject {
texture = 'bushes',
x = (x - 1) * TILE_SIZE,
y = (4 - 1) * TILE_SIZE,
width = 16,
height = 16,
-- select random frame from bush_ids whitelist, then random row for variance
frame = BUSH_IDS[math.random(#BUSH_IDS)] + (math.random(4) - 1) * 7,
collidable = false
}
)
end
-- pillar tiles
tiles[5][x] = Tile(x, 5, tileID, topper, tileset, topperset)
tiles[6][x] = Tile(x, 6, tileID, nil, tileset, topperset)
tiles[7][x].topper = nil
-- chance to generate bushes
elseif math.random(8) == 1 and keyPosition ~= x then
table.insert(objects,
GameObject {
texture = 'bushes',
x = (x - 1) * TILE_SIZE,
y = (6 - 1) * TILE_SIZE,
width = 16,
height = 16,
frame = BUSH_IDS[math.random(#BUSH_IDS)] + (math.random(4) - 1) * 7,
collidable = false
}
)
end
--spawn the key
if x == keyPosition then
table.insert(objects,
GameObject {
texture = 'key-lock',
x = (x - 1) * TILE_SIZE,
y = (blockHeight+1) * TILE_SIZE,
width = 16,
height = 16,
frame = keyColor,
collidable = true,
consumable = true,
solid = false,
--key has its own function to add to the player score
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 500
keyCollected = true
end
})
end
--spawn the locked box
if x == lockBoxPosition then
table.insert(objects,
--locked box
GameObject{
texture = 'key-lock',
x = (x-1) * TILE_SIZE,
Y = (blockHeight - 1) * TILE_SIZE,
width = 16,
height = 16,
--make it a random variant
frame = keyColor + 4,
collidable = true,
hit = false,
solid = true,
lockedBox = false,
--collision function
onCollide = function(obj)
if not obj.hit then
if keyCollected then
gSounds['pickup']:play()
obj.hit = true
--spawn flag post
local flagpost = GameObject {
texture = 'flagposts',
x = obj.x + 4,
y = (blockHeight - 4) * TILE_SIZE,
width = 8,
height = 48,
frame = 1,
collidable = true,
consumable = true,
solid = false,
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 1000
--end of the level
gStateMachine:change('play', {score = player.score, lastLevelWidth = width})
end
}
--spawn flag
local flag = GameObject {
texture = 'flags',
x = obj.x + 6,
y = (blockHeight - 2) * TILE_SIZE,
width = 16,
height = 10,
frame = 1,
collidable = true,
consumable = true,
solid = false,
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 1000
--end of the level
gStateMachine:change('play', {score = player.score, lastLevelWidth = width})
end
}
--raise the flag
Timer.tween(2.0, {
[flag] = {y = ((blockHeight - 4) * TILE_SIZE) + 4}
})
gSounds['powerup-reveal']:play()
table.insert(objects, flagpost)
table.insert(objects, flag)
end
end
gSounds['empty-block']:play()
end
}
)
end
-- chance to spawn a block
if math.random(10) == 1 then
table.insert(objects,
-- jump block
GameObject {
texture = 'jump-blocks',
x = (x - 1) * TILE_SIZE,
y = (blockHeight - 1) * TILE_SIZE,
width = 16,
height = 16,
-- make it a random variant
frame = math.random(#JUMP_BLOCKS),
collidable = true,
hit = false,
solid = true,
-- collision function takes itself
onCollide = function(obj)
-- spawn a gem if we haven't already hit the block
if not obj.hit then
-- chance to spawn gem, not guaranteed
if math.random(5) == 1 then
-- maintain reference so we can set it to nil
local gem = GameObject {
texture = 'gems',
x = (x - 1) * TILE_SIZE,
y = (blockHeight - 1) * TILE_SIZE - 4,
width = 16,
height = 16,
frame = math.random(#GEMS),
collidable = true,
consumable = true,
solid = false,
-- gem has its own function to add to the player's score
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 100
end
}
-- make the gem move up from the block and play a sound
Timer.tween(0.1, {
[gem] = {y = (blockHeight - 2) * TILE_SIZE}
})
gSounds['powerup-reveal']:play()
table.insert(objects, gem)
end
obj.hit = true
end
gSounds['empty-block']:play()
end
}
)
end
end
end
local map = TileMap(width, height)
map.tiles = tiles
return GameLevel(entities, objects, map)
end
and here is GameObject
GameObject = Class{}
function GameObject:init(def)
self.x = def.x
self.y = def.y
self.texture = def.texture
self.width = def.width
self.height = def.height
self.frame = def.frame
self.solid = def.solid
self.collidable = def.collidable
self.consumable = def.consumable
self.onCollide = def.onCollide
self.onConsume = def.onConsume
self.hit = def.hit
end
function GameObject:collides(target)
return not (target.x > self.x + self.width or self.x > target.x + target.width or
target.y > self.y + self.height or self.y > target.y + target.height)
end
function GameObject:update(dt)
end
function GameObject:render()
love.graphics.draw(gTextures[self.texture], gFrames[self.texture][self.frame], self.x, self.y)
end
the issue is supossedly in line 28 of GameObject
Related
I am creating a simple platformer with love2d in lua.
I want the player to stand on the ground so that it won't fall.
However I got some proablem that I cannot change the string of a table return from player.lua
(It is a table because the player.lua return m and the last sentance, and m is a table)
I tested that the isOnFloor function is work, but I just can't change the boolean in the player table.
Main.lua
local love = require("love")
local tileMapper = require("tile/tileMapper")
local player = require "player"
local isOnFloor = require("isOnFloor")
function love.load()
love.window.setMode(1080, 640)
love.window.setTitle("Simple Platformer")
tileMapper:spawn()
tiles = tileMapper.tiles
player:setValue()
love.keyboard.keyPressed = {}
end
function love.update(dt)
for i = 1, #tiles do
if isOnFloor(player.x, player.y, tiles[i].x, tiles[i].y, 32, 32 * 3, 72, 72) then
player.currentState = "ground" -- I wanna change it here but when I go back to
--player.lua and try to let it out put the self.currentState, nothing changed --
else
player.currentState = "air"
end
end
player:update(dt)
love.keyboard.keyPressed = {}
end
I try to print out the player.currentState the the line after I set the player.currentState = "ground". It printed out "ground"
But if I print it in player.lua, it is always "air"
player.lua(I didn't paste some of the code here)
m = {}
function m:setValue()
self.x = 400
self.y = 50
self.vtx = 0
self.y_input = 0
self.speed = 300
self.jumpForce = -800
self.gravity = 100
self.anim = {idle = {img = love.graphics.newImage("assets/Idle.png"), maxFrame = 10},
run = {img = love.graphics.newImage("assets/Run.png"), maxFrame = 11},
jump = {img = love.graphics.newImage("assets/Jump.png"), maxFrame = 0},
fall = {img = love.graphics.newImage("assets/Fall.png"), maxFrame = 0}}
self.currentAnim = nil
for i = 1, #self.anim do
self.anim[i]:setFilter("nearest", "nearest")
end
self.frame = 0
self.timer = 0
self.currentState = nil
end
function m:update(dt)
print(self.currentState)
---------------------- Player States Update ------------------------
self:getXInput()
if self.currentState == "ground" then
if self.vtx == 0 then
self.currentAnim = self.anim.idle
elseif not(self.vtx == 0) then
self.currentAnim = self.anim.run
end
elseif self.currentState == "air" then
self.y_input = self.y_input + self.gravity
if self.y_input > 0 then
self.currentAnim = self.anim.fall
elseif self.y_input < 0 then
self.currentAnim = self.anim.jump
end
end
------------------------ Anim --------------------
self:animUpdate(dt)
if love.keyboard.keyPressed["space"] == true then
self.y_input = self.jumpForce
end
self.x = self.x + self.speed * dt * self.vtx
self.y = self.y + self.y_input * dt
end
return m
If you have an idea of this porblem, please tell, thank you.
I have created a basic chunk generator, a chunk is area filled with squares, when the player moves a few blocks away from 0,0 it works correctly but after moving 4 chunks away it renders more then one chunk instead of one, I am not sure what I am doing wrong, I have given it a go changing some values, but I am left head scratching.
here is the full code, you can copy and paste into VSCODE with love2D to see what happens.
I think the main issue is somewhere around check_boarders function since that is what checks if the player is inside a chunk.
function Key_input(key)
if love.keyboard.isDown(key) then
return 1
else
return 0
end
end
function love.load()
Camera = require "camera"
Cam = Camera()
Basic_Player = {}
Basic_Player.X = 100
Basic_Player.Y = 100
Basic_Player.Speed = 15
Movement = {}
end
function love.update(dt)
Movement.X = Key_input("d") - Key_input("a")
Movement.Y = Key_input("s") - Key_input("w")
Basic_Player.X = Basic_Player.X + Movement.X * Basic_Player.Speed
Basic_Player.Y = Basic_Player.Y + Movement.Y * Basic_Player.Speed
Cam:lookAt(Basic_Player.X,Basic_Player.Y)
X, Y = Cam:position() -- Set cam position to global values
end
function love.draw()
love.graphics.setBackgroundColor(0.5,0.5,0.9)
Cam:attach() -- Renders the player and world inside its own scene
generate_world(10,0)
love.graphics.setColor( 0,0,1, 1 )
love.graphics.rectangle("fill",Basic_Player.X,Basic_Player.Y,30,30)
love.graphics.setColor( 1,1,1, 1 )
Cam:detach()
love.graphics.setColor( 1,0,0, 1 ) --Stays on the screen
love.graphics.print(X .. " / " .. Y ,300,400)
love.graphics.print(love.timer.getFPS( ) ,300,450)
love.graphics.setColor( 1,1,1, 1 )
end
function old_generate_world(_world_size, _seed) -- Before optimization
local _chunk_size = 30
local _block_size = 30
for i = 0, _world_size - 1 do
for f = 0, _world_size - 1 do
local x_val = (_chunk_size * _block_size) * i -- Position value for actually building the chunks
local y_val = (_chunk_size * _block_size) * f
gen_chunk(_chunk_size,_block_size,_seed,{X = x_val ,Y = y_val })
end
end
end
function generate_world(_world_size, _seed)
local _chunk_size = 10 -- Chunk size width and height
local _block_size = 30 -- block size inside the chunk
for i = 0, _world_size - 1 do -- loop through world size
for f = 0, _world_size - 1 do
local x_val = (_chunk_size * _block_size) * i -- Position value for actually building the chunks
local y_val = (_chunk_size * _block_size) * f
local chunk_x_local_size = 0 -- To make sure we get a length for when i and f = 0
local chunk_y_local_size = 0
if i == 0 then -- To make sure the size of the chunk isnt zero
chunk_x_local_size = _chunk_size * _block_size -- Get length of chunk when i = 0
else
chunk_x_local_size = x_val
end
if f == 0 then -- ditto
chunk_y_local_size = _chunk_size * _block_size
else
chunk_y_local_size = y_val
end
-- Checks if the player is inside a chunk if true draw it.
if Check_boarders({X = X,Y = Y},{X = x_val,Y = y_val}, {X = chunk_x_local_size, Y = chunk_y_local_size}) then
gen_chunk(_chunk_size,_block_size,_seed,{X = x_val ,Y = y_val }) -- Actually generate the chunk
end
love.graphics.setColor( 0,1,0, 1 )
love.graphics.rectangle("fill",x_val,y_val,15,15)
love.graphics.setColor( 1,1,1, 1 )
end
end
end
function Check_boarders(player_pos, boarder_pos, chunk_length) -- Checks player position is inside the boarder of the currently generated chunk
if player_pos.X > boarder_pos.X and player_pos.X < boarder_pos.X + chunk_length.X then -- Check if the player is greater then top left and less then top right
if player_pos.Y > boarder_pos.Y and player_pos.Y < boarder_pos.Y + chunk_length.Y then -- check if player is greater then top and less then bottom left
return true
end
end
return false
end
function gen_chunk(chunk_size,block_size,seed,position) -- chunk size is how many blocks inside the chunk, block size is self explain, seed n/a, pos starting chunk position
for i = 0, chunk_size - 1 do
for e = 0, chunk_size - 1 do -- loop until chunk size is met this is the amount of blocks being created
love.graphics.rectangle("fill",position.X + i * block_size,position.Y + e * block_size,block_size - 1,block_size - 1)
end
end
love.graphics.setColor( 1,0,0, 1 )
love.graphics.rectangle("fill",position.X ,position.Y,6,6)
love.graphics.setColor( 1,1,1, 1 )
end
You will need this camera.lua script just create it and paste this into it:
local _PATH = (...):match('^(.*[%./])[^%.%/]+$') or ''
local cos, sin = math.cos, math.sin
local camera = {}
camera.__index = camera
-- Movement interpolators (for camera locking/windowing)
camera.smooth = {}
function camera.smooth.none()
return function(dx,dy) return dx,dy end
end
function camera.smooth.linear(speed)
assert(type(speed) == "number", "Invalid parameter: speed = "..tostring(speed))
return function(dx,dy, s)
-- normalize direction
local d = math.sqrt(dx*dx+dy*dy)
local dts = math.min((s or speed) * love.timer.getDelta(), d) -- prevent overshooting the goal
if d > 0 then
dx,dy = dx/d, dy/d
end
return dx*dts, dy*dts
end
end
function camera.smooth.damped(stiffness)
assert(type(stiffness) == "number", "Invalid parameter: stiffness = "..tostring(stiffness))
return function(dx,dy, s)
local dts = love.timer.getDelta() * (s or stiffness)
return dx*dts, dy*dts
end
end
local function new(x,y, zoom, rot, smoother)
x,y = x or love.graphics.getWidth()/2, y or love.graphics.getHeight()/2
zoom = zoom or 1
rot = rot or 0
smoother = smoother or camera.smooth.none() -- for locking, see below
return setmetatable({x = x, y = y, scale = zoom, rot = rot, smoother = smoother}, camera)
end
function camera:lookAt(x,y)
self.x, self.y = x, y
return self
end
function camera:move(dx,dy)
self.x, self.y = self.x + dx, self.y + dy
return self
end
function camera:position()
return self.x, self.y
end
function camera:rotate(phi)
self.rot = self.rot + phi
return self
end
function camera:rotateTo(phi)
self.rot = phi
return self
end
function camera:zoom(mul)
self.scale = self.scale * mul
return self
end
function camera:zoomTo(zoom)
self.scale = zoom
return self
end
function camera:attach(x,y,w,h, noclip)
x,y = x or 0, y or 0
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
self._sx,self._sy,self._sw,self._sh = love.graphics.getScissor()
if not noclip then
love.graphics.setScissor(x,y,w,h)
end
local cx,cy = x+w/2, y+h/2
love.graphics.push()
love.graphics.translate(cx, cy)
love.graphics.scale(self.scale)
love.graphics.rotate(self.rot)
love.graphics.translate(-self.x, -self.y)
end
function camera:detach()
love.graphics.pop()
love.graphics.setScissor(self._sx,self._sy,self._sw,self._sh)
end
function camera:draw(...)
local x,y,w,h,noclip,func
local nargs = select("#", ...)
if nargs == 1 then
func = ...
elseif nargs == 5 then
x,y,w,h,func = ...
elseif nargs == 6 then
x,y,w,h,noclip,func = ...
else
error("Invalid arguments to camera:draw()")
end
self:attach(x,y,w,h,noclip)
func()
self:detach()
end
-- world coordinates to camera coordinates
function camera:cameraCoords(x,y, ox,oy,w,h)
ox, oy = ox or 0, oy or 0
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
-- x,y = ((x,y) - (self.x, self.y)):rotated(self.rot) * self.scale + center
local c,s = cos(self.rot), sin(self.rot)
x,y = x - self.x, y - self.y
x,y = c*x - s*y, s*x + c*y
return x*self.scale + w/2 + ox, y*self.scale + h/2 + oy
end
-- camera coordinates to world coordinates
function camera:worldCoords(x,y, ox,oy,w,h)
ox, oy = ox or 0, oy or 0
w,h = w or love.graphics.getWidth(), h or love.graphics.getHeight()
-- x,y = (((x,y) - center) / self.scale):rotated(-self.rot) + (self.x,self.y)
local c,s = cos(-self.rot), sin(-self.rot)
x,y = (x - w/2 - ox) / self.scale, (y - h/2 - oy) / self.scale
x,y = c*x - s*y, s*x + c*y
return x+self.x, y+self.y
end
function camera:mousePosition(ox,oy,w,h)
local mx,my = love.mouse.getPosition()
return self:worldCoords(mx,my, ox,oy,w,h)
end
-- camera scrolling utilities
function camera:lockX(x, smoother, ...)
local dx, dy = (smoother or self.smoother)(x - self.x, self.y, ...)
self.x = self.x + dx
return self
end
function camera:lockY(y, smoother, ...)
local dx, dy = (smoother or self.smoother)(self.x, y - self.y, ...)
self.y = self.y + dy
return self
end
function camera:lockPosition(x,y, smoother, ...)
return self:move((smoother or self.smoother)(x - self.x, y - self.y, ...))
end
function camera:lockWindow(x, y, x_min, x_max, y_min, y_max, smoother, ...)
-- figure out displacement in camera coordinates
x,y = self:cameraCoords(x,y)
local dx, dy = 0,0
if x < x_min then
dx = x - x_min
elseif x > x_max then
dx = x - x_max
end
if y < y_min then
dy = y - y_min
elseif y > y_max then
dy = y - y_max
end
-- transform displacement to movement in world coordinates
local c,s = cos(-self.rot), sin(-self.rot)
dx,dy = (c*dx - s*dy) / self.scale, (s*dx + c*dy) / self.scale
-- move
self:move((smoother or self.smoother)(dx,dy,...))
end
-- the module
return setmetatable({new = new, smooth = camera.smooth},
{__call = function(_, ...) return new(...) end})
i really hope im right here, as on a discord server no one wants to help me for some reason.
We run a Project for a game called Garry's Mod and currently try to get a "Toxic Gas" Script Working.
But we face the following issue
[ERROR] gamemodes/zombierp/plugins/toxicgas/sh_plugin.lua:44: attempt to index local 'item' (a boolean value)
1. IsEquippingGasmask - gamemodes/zombierp/plugins/toxicgas/sh_plugin.lua:44
2. v - gamemodes/zombierp/plugins/toxicgas/sh_plugin.lua:121
3. unknown - gamemodes/helix/gamemode/core/libs/sh_plugin.lua:477
And me being fairly new to Lua, im just completly confused and dont know how to fix it.
Here is the Full Script
local config = {
smokeColor = Color(63, 127, 0),
smokeAlpha = 110,
maskItem = "gasmask",
smokeSpawnerDistance = 100 -- distance between the smoke emitters on the box line
}
local PLUGIN = PLUGIN
PLUGIN.name = "Toxic Gas"
PLUGIN.author = ""
PLUGIN.description = ""
PLUGIN.positions = PLUGIN.positions or {}
PLUGIN.smokeStacks = PLUGIN.smokeStacks or {}
function PLUGIN:LoadData()
PLUGIN.positions = self:GetData()
self:UpdateWorldData()
end
function PLUGIN:SaveData()
self:SetData(PLUGIN.positions)
self:UpdateWorldData()
end
function PLUGIN:UpdateWorldData()
SetNetVar("toxicGasPositions", PLUGIN.positions)
-- global netvar doesn't seem to sync without this
for _, ply in pairs(player.GetAll()) do
ply:SyncVars()
end
end
function PLUGIN:IsEquippingGasmask(ply)
local character = ply:GetCharacter()
if not character then return false end
local inventoryID = character:GetInventory():GetID()
local inventory = ix.item.inventories[inventoryID]
for x, items in pairs(inventory.slots) do
for y, item in pairs(items) do
if item.uniqueID == config.maskItem
and item:GetData("equip") == true then
return true
end
end
end
return false
end
local function GetBoxLine(min, max)
local deltaX = math.abs(min.x - max.x)
local deltaY = math.abs(min.y - max.y)
local lineStart, lineEnd
if deltaX < deltaY then
lineStart = Vector(min.x + (max.x - min.x) / 2, min.y, min.z)
lineEnd = Vector(min.x + (max.x - min.x) / 2, min.y + (max.y - min.y), min.z)
else
lineStart = Vector(min.x, min.y + (max.y - min.y) / 2, min.z)
lineEnd = Vector(min.x + (max.x - min.x), min.y + (max.y - min.y) / 2, min.z)
end
return lineStart, lineEnd
end
if SERVER then
function PLUGIN:Think()
for idx, gasBox in pairs(PLUGIN.positions) do
if PLUGIN.smokeStacks[idx] == nil then
local min, max = gasBox.min, gasBox.max
local startSmoke, endSmoke = GetBoxLine(min, max)
PLUGIN.smokeStacks[idx] = {
count = math.floor(startSmoke:Distance(endSmoke) / config.smokeSpawnerDistance),
stacks = {}
}
for i = 1, PLUGIN.smokeStacks[idx].count do
local smoke = ents.Create("env_smokestack")
smoke:SetPos(startSmoke + (endSmoke - startSmoke):GetNormalized() * (i) * config.smokeSpawnerDistance)
smoke:SetKeyValue("InitialState", "1")
smoke:SetKeyValue("WindAngle", "0 0 0")
smoke:SetKeyValue("WindSpeed", "0")
smoke:SetKeyValue("rendercolor", tostring(config.smokeColor))
smoke:SetKeyValue("renderamt", tostring(config.smokeAlpha))
smoke:SetKeyValue("SmokeMaterial", "particle/particle_smokegrenade.vmt")
smoke:SetKeyValue("BaseSpread", tostring(config.smokeSpawnerDistance))
smoke:SetKeyValue("SpreadSpeed", "10")
smoke:SetKeyValue("Speed", "32")
smoke:SetKeyValue("StartSize", "32")
smoke:SetKeyValue("EndSize", "32")
smoke:SetKeyValue("roll", "8")
smoke:SetKeyValue("Rate", "64")
smoke:SetKeyValue("JetLength", tostring(max.z - min.z))
smoke:SetKeyValue("twist", "6")
smoke:Spawn()
smoke:Activate()
smoke.Think = function()
if PLUGIN.positions[idx] == nil then
smoke:Remove()
end
end
PLUGIN.smokeStacks[idx].stacks[i] = smoke
end
end
end
for _, ply in pairs(player.GetAll()) do
local pos = ply:EyePos()
if not ply:Alive() then continue end
local canBreathe = false
if not canBreathe then
canBreathe = self:IsEquippingGasmask(ply)
end
if not canBreathe then
for _, gasBox in pairs(PLUGIN.positions) do
if pos:WithinAABox(gasBox.min, gasBox.max) then
ply.nextGasDamage = ply.nextGasDamage or CurTime()
if CurTime() >= ply.nextGasDamage then
ply.nextGasDamage = CurTime() + .75
ply:TakeDamage(6)
ix.util.Notify("You are choking. You need a gas mask.", ply)
end
break
end
end
end
end
end
end
if CLIENT then
-- toggles showing toxic gas boxes when in noclip/observer
CreateConVar("ix_toxicgas_observer", "0", FCVAR_ARCHIVE)
local function IsInRange(min, max, scale)
local localPos = LocalPlayer():GetPos()
local distance = min:Distance(max)
if localPos:Distance(min + ((max - min) / 2)) <= distance * scale then
return true
end
return false
end
function PLUGIN:PostDrawTranslucentRenderables()
local toxicGasPositions = GetNetVar("toxicGasPositions")
if toxicGasPositions == nil then return end
for _, gasBox in pairs(toxicGasPositions) do
local min, max = gasBox.min, gasBox.max
if not IsInRange(min, max, 3) then continue end
local observerCvar = GetConVar("ix_toxicgas_observer")
if LocalPlayer():IsAdmin()
and LocalPlayer():GetMoveType() == MOVETYPE_NOCLIP
and observerCvar and observerCvar:GetBool() then
render.DrawWireframeBox(min, Angle(), Vector(0, 0, 0), max - min, Color(142, 222, 131, 255), false)
local startSmoke, endSmoke = GetBoxLine(min, max)
render.DrawLine(startSmoke, endSmoke, Color(0, 255, 0), false)
end
end
end
function PLUGIN:HUDPaint()
-- this is an FPS killer tbh
--[[
local toxicGasPositions = game.GetWorld():GetNetVar("toxicGasPositions")
if toxicGasPositions == nil then return end
local inToxicGas = false
local center
local cornerDist
for _, gasBox in pairs(toxicGasPositions) do
local min, max = gasBox.min, gasBox.max
center = min + ((max - min) / 2)
cornerDist = min:Distance(max)
if LocalPlayer():EyePos():WithinAABox(min, max) then
inToxicGas = true
continue
end
end
if inToxicGas then
local isEquippingGasmask = self:IsEquippingGasmask(LocalPlayer())
local distance = LocalPlayer():EyePos():Distance(center)
ix.util.DrawBlurAt(0, 0, ScrW(), ScrH(), 1, 0.2, isEquippingGasmask and 50 or 255)
end
]]
end
end
ix.command.Add("AddToxicGas", {
description = "Adds a toxic gas box from where you're standing and where you're looking at.",
adminOnly = true,
OnRun = function(self, client)
local pos = client:GetPos()
local tr = client:GetEyeTrace()
if not tr then return end
local hitPos = tr.HitPos
table.insert(PLUGIN.positions, {
min = pos, max = hitPos
})
PLUGIN:SaveData()
return "Added toxic gas."
end
})
ix.command.Add("RemoveToxicGas", {
description = "Removes the closest toxic gas point relative to you.",
adminOnly = true,
OnRun = function(self, client)
local closestDistance = -1
local closestIndex = -1
for idx, gasBox in pairs(PLUGIN.positions) do
local min, max = gasBox.min, gasBox.max
local center = min + ((max - min) / 2)
local distance = client:GetPos():Distance(center)
if closestDistance == -1 or distance < closestDistance then
closestDistance = distance
closestIndex = idx
end
end
if closestIndex ~= -1 then
table.remove(PLUGIN.positions, closestIndex)
if PLUGIN.smokeStacks[closestIndex] then
for k, v in pairs(PLUGIN.smokeStacks[closestIndex].stacks) do
v:Remove()
end
table.remove(PLUGIN.smokeStacks, closestIndex)
end
PLUGIN:SaveData()
return "Removed 1 toxic gas box."
else
return "Could not find any toxic gas to remove!"
end
end
})
I Really hope someone can help me with that as im trying since 2 days now
Try replacing for y, item in pairs(items) do with for item, _ in pairs(items) do.
Reason: there is a chance that items is a set, i.e., a Lua table, in which keys are set members and values are true.
change line 44
if item.uniqueID == config.maskItem
either by refusing boolean values
if type(item) ~= 'boolean' and item.uniqueID == config.maskItem
or only allowing tables, because they could possibly contain .uniqueID
if type(item) == 'table' and item.uniqueID == config.maskItem
So, I'm currently in a problem with updating my Super Mario Bros. Yeah, sure, it works fine, but the problem is this:
Whenever I touch the flagpole at the end, it literally resets my score.
It makes no sense with this program since I made a params for PlayState:enter, and I don't exactly know why my score goes back to 0.
This shows up in PlayState.lua:
function PlayState:enter(params)
self.score = params.score
self.lastLevelWidth = params.lastLevelWidth
if self.lastLevelWidth == 0 then
self.lastLevelWidth = 100
else
self.lastLevelWidth = self.lastLevelWidth + 50
end
self.camX = 0
self.camY = 0
self.level = LevelMaker.generate(100, 10)
self.tileMap = self.level.tileMap
self.background = math.random(3)
self.backgroundX = 0
self.gravityOn = true
self.gravityAmount = 6
self.player = Player({
x = 0, y = 0,
width = 16, height = 20,
texture = 'green-alien',
stateMachine = StateMachine {
['idle'] = function() return PlayerIdleState(self.player) end,
['walking'] = function() return PlayerWalkingState(self.player) end,
['jump'] = function() return PlayerJumpState(self.player, self.gravityAmount) end,
['falling'] = function() return PlayerFallingState(self.player, self.gravityAmount) end
},
map = self.tileMap,
level = self.level,
})
self:spawnEnemies()
self.player:changeState('falling')
end
I used the params to get to a new score, 0, but I don't want it to let it stay like that. This is what I have done in LevelMaker.lua:
keyCollected = false
function LevelMaker.generate(width, height)
local tiles = {}
local entities = {}
local objects = {}
local tileID = TILE_ID_GROUND
-- whether we should draw our tiles with toppers
local topper = true
local tileset = math.random(20)
local topperset = math.random(20)
-- insert blank tables into tiles for later access
for x = 1, height do
table.insert(tiles, {})
end
-- make positions for the lock box and key in the level
local lockBoxPosition = math.random(1, width)
local keyPosition = math.random(1, width)
local keySkin = math.random(1, 4)
-- column by column generation instead of row; sometimes better for platformers
for x = 1, width do
local tileID = TILE_ID_EMPTY
-- lay out the empty space
for y = 1, 6 do
table.insert(tiles[y],
Tile(x, y, tileID, nil, tileset, topperset))
end
-- chance to just be emptiness
if math.random(7) == 1 and x ~= 1 and lockBoxPosition ~= x and keyPosition ~= x then
for y = 7, height do
table.insert(tiles[y],
Tile(x, y, tileID, nil, tileset, topperset))
end
else
tileID = TILE_ID_GROUND
local blockHeight = 4
for y = 7, height do
table.insert(tiles[y],
Tile(x, y, tileID, y == 7 and topper or nil, tileset, topperset))
end
-- chance to generate a pillar
if math.random(8) == 1 then
blockHeight = 2
-- chance to generate bush on pillar
if math.random(8) == 1 then
table.insert(objects,
GameObject {
texture = 'bushes',
x = (x - 1) * TILE_SIZE,
y = (4 - 1) * TILE_SIZE,
width = 16,
height = 16,
-- select random frame from bush_ids whitelist, then random row for variance
frame = BUSH_IDS[math.random(#BUSH_IDS)] + (math.random(4) - 1) * 7
}
)
end
-- pillar tiles
tiles[5][x] = Tile(x, 5, tileID, topper, tileset, topperset)
tiles[6][x] = Tile(x, 6, tileID, nil, tileset, topperset)
tiles[7][x].topper = nil
-- chance to generate bushes
elseif math.random(8) == 1 and keyPosition ~= 1 then
table.insert(objects,
GameObject {
texture = 'bushes',
x = (x - 1) * TILE_SIZE,
y = (6 - 1) * TILE_SIZE,
width = 16,
height = 16,
frame = BUSH_IDS[math.random(#BUSH_IDS)] + (math.random(4) - 1) * 7,
collidable = false
}
)
end
if x == keyPosition then
table.insert(objects,
GameObject {
texture = 'keys-and-locks',
x = (x - 1) * TILE_SIZE,
y = (blockHeight + 1) * TILE_SIZE,
width = 16,
height = 16,
frame = keySkin,
collidable = true,
consumable = true,
solid = false,
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 500
keyCollected = true
end
}
)
end
if x == lockBoxPosition then
table.insert(objects,
GameObject {
texture = 'keys-and-locks',
x = (x - 1) * TILE_SIZE,
y = (blockHeight - 1) * TILE_SIZE,
width = 16,
height = 16,
frame = keySkin + 4,
collidable = true,
consumable = true,
hit = false,
solid = true,
lockedBox = false,
objectRemove = false,
onCollide = function(obj)
if not obj.hit then
if keyCollected then
gSounds['pickup']:play()
obj.hit = true
obj.objectRemove = true
obj.consumable = true
local pole = GameObject {
texture = 'poles',
x = (width * TILE_SIZE) - 32,
y = (blockHeight - 1) * TILE_SIZE,
width = 16,
height = 48,
frame = math.random(#POLES),
collidable = true,
consumable = true,
solid = false,
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 1000
gStateMachine:change('play', {
score = player.score,
lastLevelWidth = width
})
end
}
local flag = GameObject {
texture = 'flags',
x = (width * TILE_SIZE) - 32 + 6,
y = blockHeight * TILE_SIZE,
width = 16,
height = 10,
frame = 1,
collidable = true,
consumable = true,
solid = false,
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 1000
gStateMachine:change('play', {
score = player.score,
lastLevelWidth = width
})
end
}
Timer.tween(2.0 , {
[flag] = {y = ((blockHeight - 1) * TILE_SIZE) + 4}
})
gSounds['powerup-reveal']:play()
table.insert(objects, pole)
table.insert(objects, flag)
end
keyCollected = false
end
gSounds['empty-block']:play()
end
}
)
-- chance to spawn a block
elseif math.random(10) == 1 then
table.insert(objects,
-- jump block
GameObject {
texture = 'jump-blocks',
x = (x - 1) * TILE_SIZE,
y = (blockHeight - 1) * TILE_SIZE,
width = 16,
height = 16,
-- make it a random variant
frame = math.random(#JUMP_BLOCKS),
collidable = true,
hit = false,
solid = true,
-- collision function takes itself
onCollide = function(obj)
-- spawn a gem if we haven't already hit the block
if not obj.hit then
-- chance to spawn gem, not guaranteed
if math.random(5) == 1 then
-- maintain reference so we can set it to nil
local gem = GameObject {
texture = 'gems',
x = (x - 1) * TILE_SIZE,
y = (blockHeight - 1) * TILE_SIZE - 4,
width = 16,
height = 16,
frame = math.random(#GEMS),
collidable = true,
consumable = true,
solid = false,
-- gem has its own function to add to the player's score
onConsume = function(player, object)
gSounds['pickup']:play()
player.score = player.score + 100
end
}
-- make the gem move up from the block and play a sound
Timer.tween(0.1, {
[gem] = {y = (blockHeight - 2) * TILE_SIZE}
})
gSounds['powerup-reveal']:play()
table.insert(objects, gem)
end
obj.hit = true
end
gSounds['empty-block']:play()
end
}
)
end
end
end
local map = TileMap(width, height)
map.tiles = tiles
return GameLevel(entities, objects, map)
end
What's supposed to happen is that when I collected the key and unlocked the lock block, I get a flag, and when I collide with the flag at the end of the map, I get to a new level, with the same score, but not 0. Sadly, everytime I get to a new level, it turns into 0.
I don't know if the problem is in StartState.lua:
function StartState:update(dt)
if love.keyboard.wasPressed('enter') or love.keyboard.wasPressed('return') then
gStateMachine:change('play', {
score = 0,
lastLevelWidth = 0
})
end
end
Any ideas why it's like this?
If you collide with the pole and then change to StartState, then that is the problem. I suggest feeding the score through start with: function StartState:enter(params) self.score = params.score end
Then just pass self.score through to the playstate
This gear was inserted from the Catalog. It relocates the player to the center of the map when it's unequipped by clicking on the thumbnail. At 1st, I tested in a game I made. Everytime I unequipped it, the player kept falling through the baseplate and dying. I noticed it is the same position over and over. I moved the baseplate's position lower and the player falls down onto the baseplate instead of dying. Then I tested the gear in a new empty baseplate, unequipping it, the player moves to the center, too. I check the position of the both the Handle and the player's Torso, but that axis does not match any position in the script. Can someone point this out for me so that I can change it to the last position that the player stops?
Tool = script.Parent
Handle = Tool:WaitForChild("Handle")
Players = game:GetService("Players")
Debris = game:GetService("Debris")
Assets = require(Tool:WaitForChild("Assets"))
Data = Assets.Data
BaseUrl = Assets.BaseUrl
BasePart = Instance.new("Part")
BasePart.Material = Enum.Material.Plastic
BasePart.Shape = Enum.PartType.Block
BasePart.TopSurface = Enum.SurfaceType.Smooth
BasePart.BottomSurface = Enum.SurfaceType.Smooth
BasePart.FormFactor = Enum.FormFactor.Custom
BasePart.Size = Vector3.new(0.2, 0.2, 0.2)
BasePart.Anchored = false
BasePart.CanCollide = true
BasePart.Locked = true
Animations = {
Hold = {Animation = Tool:WaitForChild("Hold"), FadeTime = nil, Weight = nil, Speed = nil}
}
Sounds = {
Honk = Handle:WaitForChild("Honk"),
Engine = Handle:WaitForChild("Running")
}
Controls = {
Forward = {Key = "w", ByteKey = 17, Mode = false},
Backward = {Key = "s", ByteKey = 18, Mode = false},
Left = {Key = "a", ByteKey = 20, Mode = false},
Right = {Key = "d", ByteKey = 19, Mode = false}
}
Rate = (1 / 60)
Gravity = 196.20
PoseOffset = CFrame.new(0, -1.5125, -0.3) * CFrame.Angles(0, 0, 0) --The offset your character is from the center of the vehicle.
SpeedBoost = {
Allowed = false,
Active = false,
Enabled = true,
Duration = 10,
ReloadTime = 30
}
Special = {
Allowed = false,
Enabled = true,
Active = false,
Duration = 0,
ReloadTime = 60
}
Speed = {
Acceleration = {
Normal = 30,
Boost = 30
},
Deceleration = {
Normal = 30,
Boost = 30
},
MovementSpeed = {
Normal = {Min = 20, Max = 70},
Boost = {Min = 20, Max = 70}
},
TurnSpeed = {
Speed = {Min = 5, Max = 5},
TurnAlpha = 0.30,
AlphaDampening = 0.2
},
}
MaxSpeed = { --Maximum speed which the vehicle can move and turn at.
Movement = Speed.MovementSpeed.Normal,
Turn = Speed.TurnSpeed.Speed,
Acceleration = Speed.Acceleration.Normal,
Deceleration = Speed.Deceleration.Normal
}
CurrentSpeed = { --The speed which the vehicle is moving and turning at.
Movement = 0,
Turn = 0
}
Honk = {
Honking = false,
LastHonk = 0,
ReloadTime = 1
}
Jump = {
Jumping = false,
LastJump = 0,
ReloadTime = 1.25,
JumpForce = 30
}
ToolEquipped = false
ServerControl = (Tool:FindFirstChild("ServerControl") or Instance.new("RemoteFunction"))
ServerControl.Name = "ServerControl"
ServerControl.Parent = Tool
ClientControl = (Tool:FindFirstChild("ClientControl") or Instance.new("RemoteFunction"))
ClientControl.Name = "ClientControl"
ClientControl.Parent = Tool
Tool.Enabled = true
function RayCast(Position, Direction, MaxDistance, IgnoreList)
local IgnoreList = ((type(IgnoreList) == "table" and IgnoreList) or {IgnoreList})
return game:GetService("Workspace"):FindPartOnRayWithIgnoreList(Ray.new(Position, Direction.unit * (MaxDistance or 999.999)), IgnoreList)
end
function GetAllConnectedParts(Object)
local Parts = {}
local function GetConnectedParts(Object)
for i, v in pairs(Object:GetConnectedParts()) do
local Ignore = false
for ii, vv in pairs(Parts) do
if v == vv then
Ignore = true
end
end
if not Ignore then
table.insert(Parts, v)
GetConnectedParts(v)
end
end
end
GetConnectedParts(Object)
return Parts
end
function EnableFirstPersonView()
if not CheckIfAlive() or not ToolEquipped then
return
end
local Limbs = {"LeftHand", "RightHand"}
for i, v in pairs(Limbs) do
local Limb = Character:FindFirstChild(v)
if Limb:IsA("BasePart") then
Spawn(function()
InvokeClient("SetLocalTransparencyModifier", {Object = Limb, Transparency = 0, AutoUpdate = false})
end)
end
end
end
function ThrustUpdater()
for i, v in pairs(CurrentSpeed) do
CurrentSpeed[i] = 0
end
for i, v in pairs(Controls) do
Controls[i].Mode = false
end
while ToolEquipped and Body and Body.Parent and CheckIfAlive() and RotationForce and RotationForce.Parent and ThrustForce and ThrustForce.Parent and TurnGyro and TurnGyro.Parent do
RotationForce.angularvelocity = Vector3.new(0, CurrentSpeed.Turn, 0)
if math.abs(CurrentSpeed.Turn) > Speed.TurnSpeed.AlphaDampening then
CurrentSpeed.Turn = (CurrentSpeed.Turn - (Speed.TurnSpeed.AlphaDampening * (math.abs(CurrentSpeed.Turn) / CurrentSpeed.Turn)))
else
CurrentSpeed.Turn = 0
end
if not Controls.Forward.Mode or Controls.Backward.Mode then --Slow down if not controlling.
CurrentSpeed.Movement = (CurrentSpeed.Movement * 0.99)
end
local MySpeed = Vector3.new(Body.Velocity.X, 0, Body.Velocity.Z).magnitude
local VelocityDifference = math.abs((MySpeed - (ThrustForce.velocity.magnitude)))
if MySpeed > 3 and ThrustForce.velocity.magnitude > 3 and VelocityDifference > (0.7 * ThrustForce.velocity.magnitude) then
CurrentSpeed.Movement = (CurrentSpeed.Movement * 0.9)
end
if Controls.Forward.Mode then --Handle acceleration
CurrentSpeed.Movement = math.min(MaxSpeed.Movement.Max, (CurrentSpeed.Movement + (MaxSpeed.Acceleration * Rate)))
end
if Controls.Backward.Mode then --Handle deceleration, if speed is more than 0, decrease quicker.
CurrentSpeed.Movement = math.max(-MaxSpeed.Movement.Min, (CurrentSpeed.Movement - (MaxSpeed.Deceleration * ((CurrentSpeed.Movement > 0 and 2.8) or 1) * Rate)))
end
if Controls.Left.Mode then --Handle left turn speed
CurrentSpeed.Turn = math.min(Speed.TurnSpeed.Speed.Max, (CurrentSpeed.Turn + (Speed.TurnSpeed.TurnAlpha)))
end
if Controls.Right.Mode then --Handle right turn speed
CurrentSpeed.Turn = math.max(-Speed.TurnSpeed.Speed.Min, (CurrentSpeed.Turn - (Speed.TurnSpeed.TurnAlpha)))
end
local Direction = UpperTorso.CFrame.lookVector
Direction = Vector3.new(Direction.x, 0, Direction.z).unit
local Velocity = (Direction * CurrentSpeed.Movement) --The thrust force which you move.
ThrustForce.velocity = Vector3.new(Velocity.X, ThrustForce.velocity.Y, Velocity.Z)
local LeanAmount = (-CurrentSpeed.Turn * (math.pi / 6) / 4) --Amount your character leans over.
local XZAngle = math.atan2(UpperTorso.CFrame.lookVector.z, 0, UpperTorso.CFrame.lookVector.x) --Handle rotation
TurnGyro.cframe = CFrame.Angles((LeanAmount * Direction.x), 0, (LeanAmount * Direction.z))
--Wheel animation
local DesiredAngle = (999999999 * (-CurrentSpeed.Movement / math.abs(CurrentSpeed.Movement)))
local MaxVelocity = (CurrentSpeed.Movement / 250)
for i, v in pairs({FrontMotor, BackMotor}) do
if v and v.Parent then
v.DesiredAngle = DesiredAngle
v.MaxVelocity = MaxVelocity
end
end
--Smoke exhaust from vehicle running.
for i, v in pairs(ExhaustSmoke) do
if v and v.Parent then
v.Opacity = ((math.min(math.abs(CurrentSpeed.Movement), 10) / 10) * 0.5)
end
end
--Engine running sound which pitch changes while in motion.
Sounds.Engine.Pitch = (1 + (math.abs(CurrentSpeed.Movement / MaxSpeed.Movement.Max) * 1))
wait(Rate)
end
end
function SpawnVehicle()
Handle.Transparency = 1
Spawn(function()
InvokeClient("PlaySound", Sounds.Engine)
InvokeClient("PlayAnimation", Animations.Hold)
end)
Humanoid.PlatformStand = true
local VehicleData = Assets.CreateVehicle()
Body = VehicleData.Vehicle
local ParticleTable = VehicleData.Tables
--FrontMotor = Body.FrontMotor
--BackMotor = Body.BackMotor
ExhaustSmoke = ParticleTable.ExhaustSmoke
Lights = ParticleTable.Lights
Sparkles = ParticleTable.Sparkles
if SpeedBoost.Active then
for i, v in pairs(Sparkles) do
if v and v.Parent then
v.Enabled = true
end
end
end
local UpperTorsoWeld = Instance.new("Weld")
UpperTorsoWeld.C0 = PoseOffset
UpperTorsoWeld.Part0 = UpperTorso
UpperTorsoWeld.Part1 = Body
UpperTorsoWeld.Parent = Body
Body.CanCollide = true
RotationForce = Instance.new("BodyAngularVelocity")
RotationForce.maxTorque = Vector3.new(0, math.huge, 0)
RotationForce.angularvelocity = Vector3.new(0, 0, 0)
RotationForce.Parent = UpperTorso
ThrustForce = Instance.new("BodyVelocity")
ThrustForce.maxForce = Vector3.new(math.huge, 0, math.huge)
ThrustForce.velocity = Vector3.new(0, 0, 0)
ThrustForce.P = 100
ThrustForce.Parent = UpperTorso
TurnGyro = Instance.new("BodyGyro")
TurnGyro.maxTorque = Vector3.new(5000, 0, 5000)
TurnGyro.P = 300
TurnGyro.D = 100
TurnGyro.Parent = UpperTorso
Body.Parent = Tool
local RayHit, RayPos, RayNormal = RayCast(UpperTorso.Position, Vector3.new(0, -1, 0), (UpperTorso.Size.Y * 2), {Character})
if RayHit then
UpperTorso.CFrame = UpperTorso.CFrame + Vector3.new(0, ((Character:GetModelSize().Y / 2) + 1.5), 0)
end
Spawn(ThrustUpdater)
end
function FreezePlayer()
if CheckIfAlive() then
local FreezePart = BasePart:Clone()
FreezePart.Name = "FreezePart"
FreezePart.Transparency = 1
FreezePart.Anchored = true
FreezePart.CanCollide = false
local FreezeWeld = Instance.new("Weld")
FreezeWeld.Part0 = UpperTorso
FreezeWeld.Part1 = FreezePart
FreezeWeld.Parent = FreezePart
Debris:AddItem(FreezePart, 0.125)
FreezePart.Parent = Character
UpperTorso.Velocity = Vector3.new(0, -25, 0)
UpperTorso.RotVelocity = Vector3.new(0, 0, 0)
end
end
function CleanUp()
Handle.Velocity = Vector3.new(0, 0, 0)
Handle.RotVelocity = Vector3.new(0, 0, 0)
for i, v in pairs({}) do
if v then
v:disconnect()
end
end
for i, v in pairs({Body, RotationForce, ThrustForce, TurnGyro}) do
if v and v.Parent then
v:Destroy()
end
end
for i, v in pairs(Tool:GetChildren()) do
if v:IsA("BasePart") and v ~= Handle then
v:Destroy()
end
end
end
function CheckIfAlive()
return (((Character and Character.Parent and Humanoid and Humanoid.Parent and Humanoid.Health > 0 and UpperTorso and UpperTorso.Parent and Player and Player.Parent) and true) or false)
end
function Equipped(Mouse)
Character = Tool.Parent
Player = Players:GetPlayerFromCharacter(Character)
Humanoid = Character:FindFirstChild("Humanoid")
UpperTorso = Character:FindFirstChild("UpperTorso")
if not CheckIfAlive() then
return
end
Spawn(CleanUp)
Spawn(EnableFirstPersonView)
Spawn(SpawnVehicle)
ToolEquipped = true
end
function Unequipped()
Spawn(CleanUp)
Spawn(FreezePlayer)
for i, v in pairs(Sounds) do
v:Stop()
Spawn(function()
InvokeClient("StopSound", v)
end)
end
if CheckIfAlive() then
Humanoid.PlatformStand = false
end
Handle.Transparency = 0
ToolEquipped = false
end
function OnServerInvoke(player, mode, value)
if player == Player and ToolEquipped and value and CheckIfAlive() then
if mode == "KeyPress" then
local Down = value.Down
local Key = value.Key
local ByteKey = string.byte(Key)
for i, v in pairs(Controls) do
if Key == v.Key or ByteKey == v.ByteKey then
Controls[i].Mode = Down
end
end
if Key == " " and Down then --Jump controller
if math.abs(tick() - Jump.LastJump) > Jump.ReloadTime and not Jump.Jumping and ThrustForce and ThrustForce.Parent then
Jump.Jumping = true
local Parts = GetAllConnectedParts(Body)
local Mass = 0
for i, v in pairs(Parts) do
Mass = (Mass + v:GetMass())
end
ThrustForce.maxForce = Vector3.new(ThrustForce.maxForce.X, ((Mass * Gravity) * 100), ThrustForce.maxForce.Z)
ThrustForce.velocity = (Vector3.new(0, 1, 0) * Jump.JumpForce) + Vector3.new(ThrustForce.velocity.X, 0, ThrustForce.velocity.Z)
wait(0.1)
ThrustForce.maxForce = Vector3.new(ThrustForce.maxForce.X, 0, ThrustForce.maxForce.Z)
ThrustForce.velocity = Vector3.new(ThrustForce.velocity.X, 0, ThrustForce.velocity.Z)
Jump.LastJump = tick()
Jump.Jumping = false
end
elseif Key == "x" and Down then --Toggle light(s) on/off.
for i, v in pairs(Lights) do
if v and v.Parent then
v.Enabled = not v.Enabled
end
end
elseif Key == "h" and Down then --Play honk sound.
local Sound = Sounds.Honk
if (tick() - Honk.LastHonk) >= (Sound.TimeLength + Honk.ReloadTime) and not Honk.Honking then
Honk.Honking = true
local TempSound = Sound:Clone()
Debris:AddItem(TempSound, Sound.TimeLength)
TempSound.Parent = Body
TempSound:Play()
Honk.LastHonk = tick()
Honk.Honking = false
end
elseif Key == "q" and Down then --Activate special.
if not Special.Allowed or not Special.Enabled or Special.Active then
return
end
Special.Enabled = false
Special.Active = true
wait(Special.Duration)
Special.Active = false
wait(Special.ReloadTime)
Special.Enabled = true
elseif ByteKey == 48 and Down then --Activate speed boost.
if not SpeedBoost.Allowed or not SpeedBoost.Enabled or SpeedBoost.Active then
return
end
SpeedBoost.Enabled = false
SpeedBoost.Active = true
for i, v in pairs(Sparkles) do
if v and v.Parent then
v.Enabled = true
end
end
MaxSpeed.Acceleration = Speed.Acceleration.Boost
MaxSpeed.Deceleration = Speed.Deceleration.Boost
MaxSpeed.Movement = Speed.MovementSpeed.Boost
wait(SpeedBoost.Duration)
MaxSpeed.Acceleration = Speed.Acceleration.Normal
MaxSpeed.Deceleration = Speed.Deceleration.Normal
MaxSpeed.Movement = Speed.MovementSpeed.Normal
for i, v in pairs(Sparkles) do
if v and v.Parent then
v.Enabled = false
end
end
SpeedBoost.Active = false
wait(SpeedBoost.ReloadTime)
SpeedBoost.Enabled = true
end
end
end
end
function InvokeClient(Mode, Value)
local ClientReturn = nil
pcall(function()
ClientReturn = ClientControl:InvokeClient(Player, Mode, Value)
end)
return ClientReturn
end
Spawn(CleanUp)
ServerControl.OnServerInvoke = OnServerInvoke
Tool.Equipped:connect(Equipped)
Tool.Unequipped:connect(Unequipped)
I found it. Freezingplayer is the culprit. I took out "Spawn(FreezePlayer)" when it is unequipped and it works.
function Unequipped()
Spawn(CleanUp)
---Spawn(FreezePlayer)