Problem with adding multiple npc's

Yesterday I had the amazing idea to create a game where you play as a ghost and you have to take over people’s bodies to solve puzzles. One of my problems is that I have no programming experience exept from what I learned from the forums and from little projects I made myself. I know it’s hard to make good games and that it takes a lot of time but I still want to give it a shot. Another problem is that for some reason when I put down multiple npc’s I can’t take over bodies or leave bodies. If you’re willing to share a solution, please don’t make it too complicated as I don’t want any code in my project that I don’t understand!

Here’s the code:



--# controls
controls = class()

function controls:init(x,y,subject,speed)
    -- you can accept and set parameters here
    self.x = x
    self.y = y
    self.moveleft=false
    self.moveright=false
    self.movedown=false
    self.moveup=false
    self.subject = subject
    self.speed = speed
    self.leftsize=50
    self.rightsize=50
    self.downsize=50
    self.upsize=-50
end

function controls:draw()
    -- Codea does not automatically call this method
    sprite("Cargo Bot:Command Left", self.x-50, self.y,self.leftsize)
    sprite("Cargo Bot:Command Right", self.x+50, self.y,self.rightsize)
    sprite("Cargo Bot:Command Grab",self.x, self.y-50, self.downsize)
    sprite("Cargo Bot:Command Grab", self.x, self.y+50, self.upsize)
    if self.moveleft==true then
        self.subject.x=self.subject.x-self.speed
        self.leftsize=60
    end
    if self.moveright==true then
        self.subject.x=self.subject.x+self.speed
        self.rightsize=60
    end
    if self.movedown==true then
        self.subject.y=self.subject.y-self.speed
        self.downsize=60
    end
    if self.moveup==true then
        self.subject.y=self.subject.y+self.speed
        self.upsize=-60
    end
    
    if self.subject.x<=0 then
        self.subject.x=0
    end
    if self.subject.x>=WIDTH then
        self.subject.x=WIDTH
    end
    if self.subject.y<=0 then
        self.subject.y=0
    end
    if self.subject.y>=HEIGHT then
        self.subject.y=HEIGHT
    end
end

function controls:touched(touch)
    -- Codea does not automatically call this method
        --dpad
    if touch.x<=self.x-25 and touch.x>=self.x-75 and touch.y<=self.y+25 and touch.y>=self.y-25 then
        self.moveleft=true
    end
    if touch.x<=self.x+75 and touch.x>=self.x+25 and touch.y<=self.y+25 and touch.y>=self.y-25 then
        self.moveright=true
    end
    if touch.x<=self.x+25 and touch.x>=self.x-25 and touch.y<=self.y-25 and touch.y>=self.y-75 then
        self.movedown=true
    end
    if touch.x<=self.x+25 and touch.x>=self.x-25 and touch.y<=self.y+75 and touch.y>=self.y+25 then
        self.moveup=true
    end
    if touch.state==ENDED then
        self.moveleft=false
        self.moveright=false
        self.movedown=false
        self.moveup=false
        self.leftsize=50
        self.rightsize=50
        self.downsize=50
        self.upsize=-50
    end
end

--# Ghost
Ghost = class()

function Ghost:init(x,y)
    -- you can accept and set parameters here
    self.x = x
    self.y = y
    self.tint = color(255, 255, 255, 152)
end

function Ghost:draw()
    -- Codea does not automatically call this method
    tint(self.tint)
    sprite("Planet Cute:Character Boy",self.x,self.y)
    noTint()
end

function Ghost:touched(touch)
    -- Codea does not automatically call this method
end


--# Main
--Made by G_nex

displayMode(FULLSCREEN)

    
function setup()
    ghost=Ghost(WIDTH/2, HEIGHT/2)
    npc={}
    npc[1]=NPC(600, 600,"Planet Cute:Character Pink Girl",ghost)
    npc[2]=NPC(300, HEIGHT-300,"Planet Cute:Character Cat Girl",ghost)
    npc[3]=NPC(800, 200, "Planet Cute:Character Horn Girl", ghost)
    dpad=controls(150,150,ghost,5)
    music("Game Music One:Nothingness",true)
end

function draw()
    background(13, 210, 20, 255)
    
    for i=1,#npc do
    npc[i]:draw()
    end
    
    ghost:draw()
    
    --dpad
    dpad:draw()
end

function touched(touch)
    
    
    --classes
    dpad:touched(touch)
    for i=1,#npc do
    npc[i]:touched(touch)
    end
end

--# NPC
NPC = class()

function NPC:init(x,y,img,aff)
    -- you can accept and set parameters here
    self.x = x
    self.y = y
    self.img = img
    self.aff = aff
    self.takeover=false
    self.takenover=false
end

function NPC:draw()
    -- Codea does not automatically call this method
    sprite(self.img,self.x,self.y)
    if self.aff.x<self.x+50 and self.aff.x>self.x-50 and self.aff.y<self.y+50 and self.aff.y>self.y-50 then
        self.takeover=ask
    else
        self.takeover=false
    end
    if self.takeover==ask and dpad.subject==self.aff then
        fill(255, 255, 255, 255)
        rect(self.x+50, self.y, 150, 75)
        fill(0, 0, 0, 255)
        fontSize(20)
        textWrapWidth(500)
        font("Copperplate")
        text("Take body?", self.x+125, self.y+37)
    end
    if self.takeover==ask and dpad.subject==self then
        fill(255, 255, 255, 255)
        rect(self.x-200, self.y, 150, 75)
        fill(0, 0, 0, 255)
        text("Leave body?", self.x-125, self.y+37)
    end
    if self.takenover==1 then
        dpad.subject=self
    end
    if self.takenover==2 then
        dpad.subject=self.aff
    end
end

function NPC:touched(touch)
    -- Codea does not automatically call this method
if self.takeover==ask and touch.x<=self.x+200 and touch.x>=self.x+50 and touch.y<=self.y+75 and touch.y>=self.y then
        self.takenover=1
    end
if self.takeover==ask and touch.x<=self.x-50 and touch.x>=self.x-200 and touch.y<=self.y+75 and touch.y>=self.y then
        self.takenover=2
    end
end

I know it’s kind of messy but I just found out how classes work so don’t expect me to write everything super short but if it would help someone, you can use it for your own projects.

By the way, the game works properly when there’s only one npc

It looks like you need to change takenover to self.takenover in th npc class init function. Right now their global variables so the program can’t tell which npc is taken over.

Thanks, I don’t know how I missed that though :open_mouth: but now I’m having another problem ~X(
Well, I’ll try to fix it myself before I ask for help again.

I don’t like to ask it but could anyone help me with my other problem? Somehow the buttons act weird after 1 npc and I can’t figure out why it does that… I edited the code above with the updated version.

Don’t be afraid to help :stuck_out_tongue:
(I really want to continue the game as soon as I can.)

@G_nex I know you’re in a hurry, so instead of trying to figure out why your code isn’t working, it was faster to write another version of what I think you’re doing to give you some other ideas. I didn’t go into the detail you did. When the ghost and other sprites overlap, I put some text (switch) at the top of the screen. Tap it once to switch which sprite will then move. Only the ghost can switch with another sprite.


displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)

function setup()
    speed=2 -- speed to move
    dirX=0  -- x direction
    dirY=0  -- y direction
    str=""
    switch=false
    spriteMode(CENTER)
    tab={}  -- table of sprites
    -- x position, y position, sprite, ghost
    tab[1]=xx(300,100,"Planet Cute:Star",true)
    tab[2]=xx(500,200,"Planet Cute:Character Cat Girl",false)
    tab[3]=xx(400,300,"Planet Cute:Character Horn Girl",false)
    tab[4]=xx(500,400,"Planet Cute:Character Pink Girl",false)
    tab[5]=xx(600,500,"Planet Cute:Character Princess Girl",false)
end

function draw()
    background(40, 40, 50)
    fill(255)
    for a,b in pairs(tab) do    -- draw sprites in table
        b:draw()
    end
    -- movement arrows
    sprite("Cargo Bot:Command Left",100,150)
    sprite("Cargo Bot:Command Right",200,150)
    sprite("Cargo Bot:Command Grab",150,200,-50)
    sprite("Cargo Bot:Command Grab",150,100)
    switch=false
    text(str,WIDTH/2,HEIGHT-50)
    str=""
end

function touched(t)
    if t.state==BEGAN then
        if t.x>75 and t.x<125 and t.y>125 and t.y<175 then
            dirX=-speed -- move left
        end  
        if t.x>175 and t.x<225 and t.y>125 and t.y<175 then
            dirX=speed  -- move right
        end       
        if t.x>125 and t.x<175 and t.y>175 and t.y<225 then
            dirY=speed  -- move up
        end       
        if t.x>125 and t.x<175 and t.y>75 and t.y<125 then
            dirY=-speed -- move down
        end  
        if t.y>HEIGHT-150 then  -- switch bodies
            switch=true
        end
    end
    if t.state==ENDED then  -- set x,y directions to 0
        dirX=0
        dirY=0
    end
end

xx=class()  -- no name class

function xx:init(x,y,sp,sel)
    self.x=x    -- current x position
    self.y=y    -- current y position
    self.sp=sp  -- sprite to draw
    self.sel=sel    -- which sprite is selected and moves
    if sel then
        self.ghost=true     -- ghost sprite
    else
        self.ghost=false    -- not ghost sprite
    end
end

function xx:draw()
    sprite(self.sp,self.x,self.y)
    if self.sel then    -- move selected sprite
        self.x=self.x+dirX  -- move in x direction
        self.y=self.y+dirY  -- move in y direction
    end
    if not self.ghost then  -- check for overlap with ghost
        if math.abs(tab[1].x-self.x)<25 and math.abs(tab[1].y-self.y)<25 then
            str="switch bodies"
            if switch then
                if tab[1].sel then  -- switch from ghost to sprite
                    tab[1].sel=false
                    self.sel=true
                else
                    tab[1].sel=true -- switch from sprite to ghost
                    self.sel=false
                end  
            end
        end
    end
end

@G_nex Make the change I show below in the function NPC:draw(). Also, you’re mixing values. Some places you set an initial value to false, then you set it later on to some number. Also, you use the variable “ask”, but it’s never set to anything, so it’s nil. You need to go thru your code and find those kind of things, or you’re going to have more trouble finding errors as the code gets bigger.

function NPC:draw()   
     ...
     ...

     if self.takenover==2 then
        dpad.subject=self.aff
        self.takenover=false        -- add this line
    end

@G_nex - what @dave1707 points out suggests that you may be rushing your code and writing too much at a time. This makes it very difficult to find errors.

Try making very small changes and testing them properly before carrying on. This is actually a lot quicker overall.

@Ignatz Making small changes also helps for debugging, sometimes I’ve written a lot of code and pressed the play button to find there’s an error in the ton of code I just wrote, and it tends to not tell me what line, or the line number can be incorrect.

@TheSkyCoder - yes, that’s exactly the point I was trying to make.

@dave1707 Thanks for helping’ I said from the beginning my code was messy, but for now I’ll take a break in writing new code and try to fix the current one.

@Ignatz @SkyTheCoder Thanks for the tip. These kind of small tips really make a big difference at the end.