cyclic declaration of Ship error

Dubbsta

New member
Joined
Jul 13, 2017
Messages
186
have a generic interface of shootable<t> class enemyship and class ship both implement it. only the ship class gives me the error and not
the enemyship class. enemyship imports shootable<ship> and ship imports shootable<enemyship>. cant figure this error out.
 
Last edited:

dawlane

Well-known member
CX Code Contributor
Joined
Jun 21, 2017
Messages
803
You are going to need to post some code to see exactly what you are trying to accomplish. Else all everyone is going to do is just guess.
 

Dubbsta

New member
Joined
Jul 13, 2017
Messages
186
sure thing.
so the error only happens when they both implement shootable, or else either works alone
error is at line implements shootable

[CODE lang="cerberus" title="shootable"]Interface Shootable<t>
Method gotshot:Void(ship:t)
End[/CODE]
[CODE lang="cerberus" title="enemyship" highlight="10"]Strict
Import files.shipbase
Import files.floatinggun
Import files.ships
Import files.shootable
Import mojo



Class EnemyShip Implements Shootable<PlayerShip>

Field x:Float
Field y:Float
Field shipbase:ShipBase
Field guns:FloatingGun[]


Method New(x:Float,y:Float,planeimage:Image,name:String,speed:Int)
Self.x = x
Self.y = y
shipbase = New ShipBase(x,y,planeimage,name,speed)
End



Method update:Void()
shipbase.hover()
End


Method gotshot:Void(ship:playerShip)


End


Method createguns:Void(numguns:Int)
For Local i:Int = 0 Until numguns
guns = guns.Resize(guns.Length + 1)
End
End




Method draw:Void()
shipbase.draw()
For Local i:= Eachin guns
i.draw()
End
End
End




Class Zor Extends EnemyShip

Method New()
Super.New(1500,100,
LoadImage("Enemy\enemy1.png",1,Image.MidHandle),
"Zor",
8)

createguns(1)
For Local i:Int = 0 Until guns.Length
guns = New FloatingGun(shipbase.x,shipbase.y,LoadImage("Enemy\gun1.png",1,Image.MidHandle))
End
shipbase.health = 5
End



Method update:Void()
Super.update()

guns[0].update(shipbase.x + 10 ,shipbase.y + 20,guns[0].x + 45)
End
End



Class Zorg Extends EnemyShip

Method New()
Super.New(1300,500,
LoadImage("Enemy\enemy2.png",1,Image.MidHandle),
"Zor",
8)

createguns(3)
For Local i:Int = 0 Until guns.Length
guns = New FloatingGun(shipbase.x,shipbase.y,LoadImage("Enemy\gun2.png",1,Image.MidHandle))
End
shipbase.health = 10
End



Method update:Void()
Super.update()

guns[0].update(shipbase.x,shipbase.y + -70,guns[0].x + 45)
guns[1].update(shipbase.x + 70,shipbase.y ,guns[1].x + 45)
guns[2].update(shipbase.x,shipbase.y + 70,guns[2].x + 45)

End
End [/CODE]


[CODE lang="cerberus" title="playership"]Strict
Import files.guns
Import files.shipbase
Import files.floatinggun
Import files.enemyship
Import files.shootable
Import mojo


Class PlayerShip Implements Shootable<EnemyShip>
Field x:Float
Field y:Float

Field shipbase:ShipBase

Field maingun:Gun
Field heavygun:FloatingGun
Field rLauncher:Gun


Method New(x:Float,y:Float,planeimage:Image,name:String,speed:Int)
Self.x = x
Self.y = y

shipbase = New ShipBase(Self.x,Self.y,planeimage,name,speed)
End

Method gotshot:Void(ship:EnemyShip)


End


Method update:Void()
shipbase.hover()
End


Method setmaingun:Void(newgun:Gun)
maingun = newgun
End

Method setheavygun:Void(newgun:Gun)
heavygun.gun = newgun
End

Method setlauncher:Void(newgun:Gun)
rLauncher = newgun
End


Method draw:Void()
shipbase.draw()
heavygun.draw()
rLauncher.draw()
maingun.draw()
End

End




Class Dozer Extends PlayerShip

Method New()
Super.New(100,100,
LoadImage("Main Planes\Plane01\jack.png",1,Image.MidHandle),
"Dozer",
8)

maingun = New StrongGun()
heavygun = New FloatingGun(shipbase.x,shipbase.y,LoadImage("Main Planes\Plane01\gun.png",1,Image.MidHandle))
rLauncher = New StrongGun()
End

Method update:Void()
Super.update()
maingun.shoot(shipbase.x + 90,shipbase.y + 30,KEY_SPACE)
heavygun.update(shipbase.x + -50,shipbase.y + -40,heavygun.x + 45)
rLauncher.shoot(shipbase.x + 70,shipbase.y + 28 ,KEY_S)
End

End




Class Zip Extends PlayerShip

Method New()
Super.New(100,100,
LoadImage("Main Planes\Plane07\zip.png",1,Image.MidHandle),
"Zip",
5)

maingun = New StandardGun()
heavygun = New FloatingGun(shipbase.x,shipbase.y,LoadImage("Main Planes\Plane07\gun.png",1,Image.MidHandle))
rLauncher = New Chaser()

End



Method update:Void()
Super.update()
maingun.shoot(shipbase.x + 90,shipbase.y + 8,KEY_SPACE)
heavygun.update(shipbase.x + -20,shipbase.y + -40,heavygun.x + 55)
rLauncher.shoot(shipbase.x + 70,shipbase.y + 48 ,KEY_S)
End

End [/CODE]
 

dawlane

Well-known member
CX Code Contributor
Joined
Jun 21, 2017
Messages
803
In my opinion, Interfaces are the most useless thought up additions to any language. They are nothing more than a outline for a data structure and used as a reminder for a programmer to implement methods for every interface used. In other words Interfaces are almost totally abstract base classes. If you want to know more about Interfaces, then read about Java interfaces, as this is what Cerberus/Monkey implements.
 
Last edited:

Dubbsta

New member
Joined
Jul 13, 2017
Messages
186
my idea was to tie them up to a common method call since they dont extend the same ship class, but im not sure if i even need it right now. no biggie. ill look for an alternate route. still good info none the less thanks

ps. weird how it does seem to let one slide through though
 

Arantor

New member
Joined
May 27, 2020
Messages
19
In other languages that do type-checking at runtime rather than at compile time where you can also do multiple implemented interfaces (e.g. PHP), you can define a method to accept only things that implement that interface, regardless of what concrete type the object might otherwise be.

This is why, for example, guiBasic implements a monster base class Gui which everything has to extend with every possible property and you can't cleanly subdivide responsibilities down because you can't cleanly separate some of the things except at run-time, and that's not doable in Cerberus for obvious reasons. (For Cerberus this does make sense.)
 

dawlane

Well-known member
CX Code Contributor
Joined
Jun 21, 2017
Messages
803
my idea was to tie them up to a common method call since they dont extend the same ship class, but im not sure if i even need it right now. no biggie. ill look for an alternate route. still good info none the less thanks
In that case you don't extend the ship class, but create a base class that both the player and the enemy can inherit from that contains common variables and methods.

Here's a bit of a complex example, where classes use chain inheritance.
[CODE lang="cerberus" title="example.cxs"]Strict

Import mojo

Const ALIEN_TYPE:= 1

' CPoint is the base class for any 2D pixel coordinate
' Access will be via property setters and getters.
Class CPoint
Field x:Float
Field y:Float

' Assign a 2D coordinate on construction
Method New( x:Float, y:Float )
Self.x = x
Self.y = y
End Method

' The setters and getters for the X coordinate
Method X:Void( x:Float ) Property
Self.x = x
End Method

Method X:Float() Property
Return Self.x
End Method

' The setters and getters for the Y coordinate
Method Y:Void( y:Float ) Property
Self.y = y
End Method

Method Y:Float() Property
Return Self.y
End Method
End Class

' CRect is the base class for a 2D rectangle. This will also be the bounding collision box.
Class CRect Extends CPoint
' The width and height will also be stored as a point object, though it would be better here
' if the CPoint values were of an integer value and not floats as defined.
Field dimensions:CPoint

' Two ways to define the rectangle. The first passing all the dimentions. The second just with the Width and the Height.
Method New( x:Int, y:Int, w:Int, h:Int )
Super.New( x, y )
Self.dimensions = New CPoint( w, h )
End Method

Method New( w:Int, h:Int )
Self.dimensions = New CPoint( w, h )
End Method

' The width and the height need to be easily accessible to make adjustments, so these will be done via setter and getter properties
Method Width:Void( w:Int ) Property
Self.dimensions.X = w
End Method

Method Height:Void( h:Int ) Property
Self.dimensions.Y = h
End Method

Method Width:Int() Property
Return dimensions.X
End Method

Method Height:Int() Property
Return dimensions.Y
End Method
End Class

' The sprite class extends CRect and CSprite will be the base class for all other movable object blocks.
' All sprite related things can be implemented here.
' For this example eleven core member fields are implemented.
Class CSprite Extends CRect

Field id:Int ' An integer to identify the sprite
Field type:Int ' An integer to identify the type of sprite. You would use this to check before casting to the appropriate object type.
Field img:Image ' A variable to hold the current image set to use.
Field img1:Image ' Reference to one image set. This is the main sprite image
Field img2:Image ' Reference to another image set. This one will be the explosion.
Field frame:Int ' Keep track of the current animation frame.
Field totalframes:Int ' Keep track of the total number of frames for the full animation sequence.
Field flags:Int ' Binary data flags to keep track of the sprites state.
Field delay:Float ' Simple delay count between each animated frame

Field vx:Float ' Variable to store the vector for the X axis movement
Field vy:Float ' Variable to store the vector for the Y axis movement

Const DRAW_IMAGE2:= $01 ' Constant binary data flag for easy reading.

' To keep it simple, just use the two image strips for this job.
' A more complex version should be used for full blown sprite sheets.
Method New( id:Int, x:Float, y:Float, img1:Image, img2:Image = Null )
Super.New( x, y, 1, 1 ) ' Initialise the base Rectangle. The width and the height will be set when the images are assigned.
Self.id = id ' Set the id for the sprite

' Assign the image sets. NOTE: The first image will set the bounding rectangle.
' And the second image will only be applied if there is one. It will not adjust the bounding rectangle.
' For two versions of a bounding rectangle; a different approach is needed.
Self.AssignImage1( img1 )
If img2<>Null Self.AssignImage2( img2 )
End Method

' A secondary constructor that doesn't assign any images
Method New( id:Int, x:Float, y:Float )
Super.New( x, y, 1, 1 )
Self.id = id
End Method

' Set the direction vectors to move the sprite.
Method SetVel:Void( vx:Float, vy:Float )
Self.vx = vx
Self.vy = vy
End Method

Method AssignImage1:Void( img:Image )
Self.img1 = img
Self.Width = img.Width()
Self.Height = img.Height()
End Method

Method AssignImage2:Void( img:Image )
Self.img2 = img
End Method

Method Id:Int()
Return id
End Method

' Methods to flip the direction vectors
Method FlipVX:Void()
Self.vx *= -1
End Method

Method FlipVY:Void()
Self.vy *= -1
End Method

' Returns the actual sprite base data.
' To make any use of it, you would have to cast it up to the derived child object.
' This is where the type field comes into play.
' NOTE: Always check for a Null or Non Null object before doing any actions.
' And only cast when absolutely necessary to avoid performance penalties.
Method IsHit:CSprite()
If Self.flags & DRAW_IMAGE2 Return Self
Return Null
End Method

' This method is used to change the image set depending on the flag value.
Method SwitchImageSet:Void()
If img1 = Null Return ' Safety check to avoid memory exceptions.
Self.img = img1
If (Self.flags & DRAW_IMAGE2) And img2<>Null Self.img = img2 ' Switch to the second images set if the flag is set.
Self.totalframes = img.Frames() - 1 ' IMPORTANT: The second set could have a different number of animation frames. So make adjustments.
End Method

' The method to render the sprite image to screen
Method Draw:Void()
If Self.img = Null Return ' Safety check to avoid memory exceptions
DrawImage( img, Self.X, Self.Y, Self.frame ) ' Display the actual sprite image frame.
DrawText( Self.Id(), Self.x, Self.Y ) ' Display the id number given to a sprite.
End

End Class

' All the aliens are going to be stored onto a stack container. The stack it's self will be static to allow access through calling static functions.
' Doing it this ways makes it much easier to search and manipulate objects items without.
' There is nothing to from creating a common CSprite list, but you would have to identify the required type and cast the object accordingly.
Class CAlien Extends CSprite

' As there will be a number of aliens, then make a static stack of the alien type, so it can be accessed form anywhere.
Global aliens:Stack<CAlien> = New Stack<CAlien>

' The basic constructor just passing the id and the position
Method New( id:Int, x:Float, y:Float )
Super.New( id, x, y )
Self.type = ALIEN_TYPE
End Method

' A method to set the current object instance directional speeds.
' If the speeds need changing after; then a function (not a method) will be needed to find the sprite and update.
Method SetSpeeds:Void( vx:Float, vy:Float )
Self.vx = vx
Self.vy = vy
End Method

' These functions can be accessed anywhere without the need to keep track of each alien object instance.
' Add an alien to the stack container, but return the alien object created so addition operations can be carried out if needed.
Function Add:CAlien( id:Int, x:Float=0, y:Float=0)
Local alien:= New CAlien( id, x, y )
aliens.Push( alien )
Return alien
End Function

Function AssignImages:Void( alien:CAlien, img1:Image, img2:Image = Null )
alien.AssignImage1( img1 )
If img2<>Null alien.AssignImage2( img2 )
End Function

' Routine to update a number of things related to the aliens on screen.
Function UpdateAliens:Void()
For Local i:=Eachin aliens

' Flip the direction the alien sprite is moving if the edge of the screen is reached.
If i.X < 0 Or i.X > (DeviceWidth()-32) i.FlipVX()
If i.Y < 0 Or i.Y > (DeviceHeight()-32) i.FlipVY()
i.X += i.vx
i.Y += i.vy

' Very simple frame delay
If i.delay > 20
i.delay = 0
i.frame +=1
If i.frame > i.totalframes i.frame = 0
Endif
i.delay += 1

' Internal routine to set the collision state for each of the aliens in the list
i.flags &= ~CSprite.DRAW_IMAGE2 ' Always clear the current collision before proceeding.

' Loop though the aliens
For Local j:=Eachin aliens
If i.Id() = j.Id() Continue ' Skip the one current testing against.

' NOTE: Though the objects are of type CAlien, they are children of CSprite and grand children of CRect.
' So the compiler can do automatic casting to a CRect object, which is what the Rect2Rect function excepts.
If Rect2Rect( j, i )
i.flags |= CSprite.DRAW_IMAGE2
j.flags |= CSprite.DRAW_IMAGE2
Endif
Next

' This needs to be called after to switch the image set based on the collision information set internally
i.SwitchImageSet()
Next
End Function

End Class

Class CGame Extends App

' NOTE: Images themselves can be stored in a Stack of Images. You just have to keep track of them i.e. via an asset manager.
Field alienImage:Image
Field bangImage:Image

Method OnCreate:Int()
' Load the images to use
alienImage = LoadImage( "alien.png", 32, 32, 4 )
bangImage = LoadImage( "bang.png", 32, 32, 4 )

' Create 10 aliens
For Local i:Int = 1 To 10
Local alien:CAlien = CAlien.Add( i, Rnd(DeviceWidth()-32), Rnd(DeviceHeight()-32) )
CAlien.AssignImages( alien, alienImage, bangImage )
alien.SetVel( Rnd(-2, 2), Rnd(-2, 2) )
Next

Return 0
End Method

Method OnUpdate:Int()
CAlien.UpdateAliens()
Return 0
End Method

Method OnRender:Int()
Cls
For Local i:=Eachin CAlien.aliens
i.Draw()

' If an object is returned, then draw the message.
If i.IsHit() DrawText( "OUCH", i.X, i.Y+21 )
Next

Return 0
End Method

End Class

Function Rect2Rect:Bool( r1:CRect, r2:CRect )
Return (r1.X + r1.Width) >= r2.X And r1.X <= (r2.X + r2.Width) And (r1.Y + r1.Height) >= r2.Y And r1.Y <= (r2.Y + r2.Height)
End Function

Function Main:Int()
New CGame()
Return 0
End Function
[/CODE]

Play around with this code and images.
As an exercise, break it up into separate files, make certain parts private, add a player, bullets, scorings and a common list to render all sprite objects.
 

Attachments

  • alien.png
    alien.png
    874 bytes · Views: 16
  • bang.png
    bang.png
    8 KB · Views: 16
  • player.png
    player.png
    849 bytes · Views: 17

Dubbsta

New member
Joined
Jul 13, 2017
Messages
186
thanks will play with this. not sure why i chose to composition over inheritance for my shipbase class, sounded right at first might have to take another look at it. thx

think i didnt what to have to many methods or methods that didnt make sense for both classes. but will take a closer look
 

Jimmy

Active member
3rd Party Module Dev
Tutorial Author
Joined
Jan 2, 2020
Messages
578
I wish that Cerberus was made more towards composition than inheritance. It's still a rare paradigm for some reason but new lanaguges will certainly catch up. The paradigm has a lot of good design ideas when it comes to programming and with inheritance can easily paint you into a corner when you scale things up. That's what you don't want.
 

dawlane

Well-known member
CX Code Contributor
Joined
Jun 21, 2017
Messages
803
i chose to composition over inheritance
If you want to implement composition, i.e. objects that have a relationship. Then you create a base class that has a number of fields to accommodate the related objects.

Example:
A car is a type of vehicle, so the base classes would be vehicle.
Vehicles can be broken into sub objects, road wheels, controls, engine, storage area, etc. You would then add fields to vehicle for those objects types.
Each vehicle type could have a different way to handle a flat tyre, meaning for a car you could change the wheel yourself, but an eighteen wheeler would need specialist roadside assistance, so that method can be made abstract and implemented in the objects that derived from type vehicle.

Chained, nested inherited subtypes or multiple inheritance can eventually be come a hinderance. And as you have discovered, interfaces have limits and can themselves become unwieldly with them being abstract. The only real advantage with the use of interfaces, is automatic casting of a parameter being passed to a method or function where the class implements that interface.

Sometime later I will have to show an example of the use of interfaces.
 

dawlane

Well-known member
CX Code Contributor
Joined
Jun 21, 2017
Messages
803
OK. I think this should be a reasonable example for the use of Interfaces.
code_language.cerberus:
Strict

' Let's define some interfaces
Interface IMoveable
    Method Move:Void()
End

Interface ITalking
    Method Talk:Void()
End

Interface IType
    Method Type:Object()
End

' This is the base class to define an Actor.
' Our actor can only do a number of actions
Class CActor Implements IMoveable, ITalking, IType
    Field name:String
 
    Method Move:Void()
        Print "Moving...."
    End
 
    Method Talk:Void()
        Print "Talking...."
    End
 
    Method Type:Object()
        Return Self
    End
   
    Method Name:Void( name:String ) Property
        Self.name = name
    End
   
    Method Name:String() Property
        Return Self.name
    End
   
    ' Define an abstract method that needs to be implemented in each of the derived child classes.
    Method WhoIsA:String() Abstract
 
End

' Create the template for the Hero
Class CHero Extends CActor
    Method WhoIsA:String()
        Return String( "Hero" )
    End Method
End Class

' Create the template for the Villan
Class CVillan Extends CActor
    Method WhoIsA:String()
        Return String( "Villan" )
    End Method
End

' Actors need props!
Class CProp Implements IMoveable
    Method Move:Void()
        Print "~nThe stage hand is moving the stage prop"
    End
End

' The object can be any class that implements IType.
' NOTES: As there is only one method defined in IType, that is the only method that can be used.
' Automatic type conversion will not work with objects where the interface returns an object as the value.
' The base class (CActor) implementation and the Interface will return a generic type of Object. As the object will not be known,
' so the object passed needs to be cast to the required base object; in this case CActor.
' It is not recommened accessing an object in this fashion. You should just create a field in the base class as a object identifier
' and use that to avoid unecessary casting.
Function Type:CActor( type:IType )
    ' DebugStop()    ' If you uncomment this and build and run in debug mode, then examining the debug view you will see that two objects can be cast CActor (the base class) and the calling object.
    Local obj:=CActor(type.Type())
     
    Select obj
        Case CHero(obj)
            obj = CHero(obj)
        Case CVillan(obj)
            obj = CVillan(obj)
    End
 
    Print "Calling Functions: "+obj.WhoIsA()
 
    ' Once the type has been identified and cast to the correct type, call the internal method for the character
    ' before calling the external functions to preform operations.
    Move( obj )
    Talk( obj )
 
    ' NOTE: You could save the call hit and just calls the actors action methods directly
    Print "~nDirect method acting: "+obj.WhoIsA()
    obj.Move()
    obj.Talk()
 
    Return obj    '
End

' The object can be any class that implements IMoveable
' NOTE: As there is only one method defined in IMoveable, that is the only method that can be used.
Function Move:Void( moveable:IMoveable )
    moveable.Move
End

' The object can be any class that implements IDrawable
' NOTE: As there is only one method defined in IDrawable, that is the only method that can be used.
Function Talk:Void( talking:ITalking )
    talking.Talk
End

Function Main:Int()
    Local prop:=New CProp
    Local hero:=New CHero
    Local villan:=New CVillan
    Local actor:CActor                ' The functions Type will return the actor portraying either the hero or villan
 
    ' Let's set the actors names
    hero.Name = "JD"
    villan.Name = "AH"

    '
    Print "~nOur actor is doing...."
    actor = Type( hero )        ' Returned the object as CActor, which has the Name property
    Print "ACTOR: "+actor.Name
 
    Print "~nOur actor is doing...."
    actor = Type( villan )        ' Returned the object as CActor, which has the Name property
    Print "ACTOR: "+actor.Name
 
    Move( prop )
    ' Talk( prop )    ' Uncommenting this will throw a compiler error as CProp cannot talk.
 
    Return 0
End
 
Last edited:
Top Bottom